using C4IT.F4SD.DisplayFormatting;
using C4IT.F4SD.SupportCaseProtocoll.Models;
using C4IT.FASD.Base;
using C4IT.FASD.Cockpit.Communication;
using C4IT.Logging;
using C4IT.MultiLanguage;
using FasdCockpitBase.Models;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Services.ProtocollService;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static C4IT.Logging.cLogManager;

using static FasdDesktopUi.Basics.Models.cQuickActionStatusMonitorModel.cQuickActionStep;
using static FasdDesktopUi.Basics.UserControls.QuickActionStatusMonitor;

namespace FasdDesktopUi.Basics.UiActions
{
    public class cUiRemoteQuickAction : cUiQuickAction
    {
        public cF4sdQuickActionRemote RemoteQuickAction { get; set; }

        public cUiRemoteQuickAction(cF4sdQuickActionRemote quickAction) : base(quickAction)
        {
            RemoteQuickAction = quickAction;
        }

        public async override Task<cQuickActionStatusMonitorModel> GetQuickActionStatusMonitorDataAsync()
        {
            var scriptInfos = await cFasdCockpitCommunicationBase.Instance.GetQuickActionOfAgent(RemoteQuickAction.AgentScriptId);

            var actionSteps = new List<cQuickActionStatusMonitorModel.cQuickActionStep>() { new cQuickActionStatusMonitorModel.cQuickActionStep(cMultiLanguageSupport.GetItem("QuickAction.Remote.Connect"), RemoteQuickAction.Name, enumActionStepType.connectingToClient) };
            if (scriptInfos.UserPermissionRequired)
                actionSteps.Add(new cQuickActionStatusMonitorModel.cQuickActionStep(cMultiLanguageSupport.GetItem("QuickAction.Remote.UserAcceptance"), RemoteQuickAction.Name, enumActionStepType.waitingForUserAcceptance));

            actionSteps.Add(new cQuickActionStatusMonitorModel.cQuickActionStep(cMultiLanguageSupport.GetItem("QuickAction.Remote.Running"), RemoteQuickAction.Name, enumActionStepType.running));

            return new cQuickActionStatusMonitorModel()
            {
                ActionName = RemoteQuickAction.Names.GetValue(),
                QuickActionDefinition = RemoteQuickAction,
                RequiresUserPermission = scriptInfos != null && scriptInfos.UserPermissionRequired,
                ActionSteps = actionSteps,
                QuickActionParameters = RemoteQuickAction.AdjustableParameters,
                RunQuickAction = ProcessActionAsync
            };
        }

        public override async Task<cDataCanvasDataModel> GetQuickActionDataAsync(cSupportCaseDataProvider dataProvider, bool isDetailedLayout)
        {
            DataProvider = dataProvider;

            var detailedData = new cDetailedDataModel() { Heading = cMultiLanguageSupport.GetItem("DetailsPage.History") };

            //Get QuickAciton Data
            if (isDetailedLayout)
            {
                detailedData.FullDetailedData = new List<object>() { new List<object>() { "Status", cMultiLanguageSupport.GetItem("QuickAction.Revision.ExecutionTime") } };
                var quickActionHistory = await dataProvider.QuickActionProtocollHelper.GetQuickActionHistoryAsync(RemoteQuickAction.Name);
                foreach (var quickActionLine in quickActionHistory)
                {
                    quickActionLine[1] = cUtility.RawValueFormatter.GetDisplayValue(quickActionLine[1], RawValueType.DATETIME);
                    detailedData.FullDetailedData.Add(quickActionLine);
                }
            }
            else
                detailedData.FullDetailedData = new List<object>();

            //Get Process Steps of Action
            var quickActionData = await GetQuickActionStatusMonitorDataAsync();

            //Get Recommendation
            cRecommendationDataModel recommendationData = QuickActionRecommendation?.Recommendation != null ? QuickActionRecommendation : null;

            return new cDataCanvasDataModel() { DetailedData = detailedData, QuickActionStatusMonitorData = quickActionData, RecommendationData = recommendationData };
        }

        public override async Task<List<object>> ProcessActionAsync(CancellationToken token, Dictionary<cAdjustableParameter, object> ParameterDictionary = null)
        {
            try
            {
                int agentDeviceId = int.MinValue;
                int? agentUserIdNullable = null;
                var startTime = DateTime.UtcNow;

                if (DataProvider.NamedParameterEntries.TryGetValue("AgentDeviceId", out var agentDeviceIdParameter))
                    _ = int.TryParse(agentDeviceIdParameter.GetValue(), out agentDeviceId);

                if (RemoteQuickAction.InformationClasses.Contains(enumFasdInformationClass.User))
                    if (DataProvider.NamedParameterEntries.TryGetValue("AgentUserId", out var agentUserIdParameter))
                        if (int.TryParse(agentUserIdParameter.GetValue(), out int agentUserId))
                            agentUserIdNullable = agentUserId;

                //todo: task of DataProviderBase?
                var QuickActionResult = new cF4sdQuickActionRevision();
                if (cCockpitConfiguration.Instance.agentApiConfiguration.OrganizationCode != null)
                {
                    var parameters = cExternalToolExecutorEnh.ReplaceParameters(RemoteQuickAction.Parameters, DataProvider.NamedParameterEntries.ToInterfaceDictionary(), false, ParameterDictionary);

                    var scriptInformation = await cFasdCockpitCommunicationBase.Instance.RunAgentScript(RemoteQuickAction, cCockpitConfiguration.Instance.agentApiConfiguration.OrganizationCode.Value, agentDeviceId, agentUserIdNullable, parameters);
                    await UpdateQuickActionStatusAsync(scriptInformation.TaskId, token);
                    QuickActionResult = await GetRevisionForStatusMonitorAsync(scriptInformation.RevisionId, scriptInformation.TaskId, token);

                    if (QuickActionResult != null && QuickActionResult.Status is enumQuickActionRevisionStatus.finishedWithError)
                    {
                        if (QuickActionResult.Output == null)
                            QuickActionResult.Output = new cF4sdQuickActionRevision.cOutput() { ErrorCode = 1, ErrorDescription = QuickActionResult.OutputText };
                        if (QuickActionResult.Output.ErrorCode == null && QuickActionResult.Output.ResultCode == 0 && QuickActionResult.Output.ErrorDescription == null && !string.IsNullOrEmpty(QuickActionResult.OutputText))
                            QuickActionResult.Output.ErrorDescription = QuickActionResult.OutputText;
                    }
                }

                if (QuickActionResult == null)
                    QuickActionResult = new cF4sdQuickActionRevision() { Status = enumQuickActionRevisionStatus.finishedWithError, OutputText = cMultiLanguageSupport.GetItem("QuickAction.Revision.Status.Canceled") };

                if (QuickActionResult.Measures?.Count > 0)
                {
                    var StatusMonitorMeasureValues = GetStatusMonitorMeasureValues(QuickActionResult.Measures, RemoteQuickAction.QuickActionMeasures);

                    if (StatusMonitorMeasureValues?.Count > 0)
                        StatusMonitor.MeasureValues = StatusMonitorMeasureValues;
                }

                var quickActionOutput = cQuickActionOutput.GetQuickActionOutput(QuickActionResult.Output, DataProvider);
                var protocollOutput = cQuickActionOutput.GetQuickActionOutput(QuickActionResult.Output, DataProvider);

                if (!token.IsCancellationRequested)
                {
                    StatusMonitor.QuickActionOutputs.Add(QuickActionResult.Output);
                    var status = quickActionOutput?.IsError == true ? enumQuickActionRevisionStatus.finishedWithError : QuickActionResult.Status;

                    cQuickActionStatusMonitorModel.cQuickActionStep.SetQuickActionStepStatuses(StatusMonitor.QuickActionData.ActionSteps, RemoteQuickAction.Name, enumActionStepType.running, status);
                    cQuickActionStatusMonitorModel.cQuickActionStep.SetQuickActionStepStatuses(StatusMonitor.QuickActionData.ActionSteps, RemoteQuickAction.Name, enumActionStepType.main, status);

                    cQuickActionCopyData copyData = QuickActionProtocollEntryOutput.GetCopyData(RemoteQuickAction, DataProvider, true, protocollOutput, StatusMonitor.MeasureValues);
                    QuickActionProtocollEntry protocollEntry = QuickActionProtocollEntryOutput.GetQuickActionProtocollEntry(RemoteQuickAction, copyData);

                    F4SDProtocoll.Instance.Add(protocollEntry);
                }
                else
                {
                    cQuickActionStatusMonitorModel.cQuickActionStep.CancelRemainingQuickActionSteps(StatusMonitor.QuickActionData.ActionSteps);

                    quickActionOutput = new cQuickActionOutputSingle(new cF4sdQuickActionRevision.cOutput() { ResultCode = enumQuickActionSuccess.error, ErrorDescription = cMultiLanguageSupport.GetItem("QuickAction.Copy.Output.Cancel") });

                    if (cF4SDCockpitXmlConfig.Instance.HealthCardConfig.ProtocollLanguage != null)
                    {
                        string tempLang = cMultiLanguageSupport.CurrentLanguage;
                        cMultiLanguageSupport.CurrentLanguage = cF4SDCockpitXmlConfig.Instance.HealthCardConfig.ProtocollLanguage;
                        protocollOutput = new cQuickActionOutputSingle(new cF4sdQuickActionRevision.cOutput() { ResultCode = enumQuickActionSuccess.error, ErrorDescription = cMultiLanguageSupport.GetItem("QuickAction.Copy.Output.Cancel") });
                        cMultiLanguageSupport.CurrentLanguage = tempLang;
                    }

                    cQuickActionCopyData copyData = QuickActionProtocollEntryOutput.GetCopyData(RemoteQuickAction, DataProvider, true, protocollOutput, StatusMonitor.MeasureValues);
                    QuickActionProtocollEntry protocollEntry = QuickActionProtocollEntryOutput.GetQuickActionProtocollEntry(RemoteQuickAction, copyData);


                    F4SDProtocoll.Instance.Add(protocollEntry);
                    return new List<object>() { cMultiLanguageSupport.GetItem("QuickAction.Revision.Status.Canceled"), cUtility.RawValueFormatter.GetDisplayValue(DateTime.UtcNow, RawValueType.DATETIME) };
                }

                string quickActionStatus;
                switch (QuickActionResult.Status)
                {
                    case enumQuickActionRevisionStatus.inProgress:
                        quickActionStatus = cMultiLanguageSupport.GetItem("QuickAction.Revision.Status.InProgress");
                        break;
                    case enumQuickActionRevisionStatus.finishedSuccessfull:
                        quickActionStatus = cMultiLanguageSupport.GetItem("QuickAction.Revision.Status.FinishedSuccessfull");
                        break;
                    case enumQuickActionRevisionStatus.finishedWithError:
                        quickActionStatus = cMultiLanguageSupport.GetItem("QuickAction.Revision.Status.FinishedWithError");
                        break;
                    case enumQuickActionRevisionStatus.unknown:
                    default:
                        quickActionStatus = cMultiLanguageSupport.GetItem("QuickAction.Revision.Status.Unknown");
                        break;
                }

                _ = Task.Run(async () =>
                {
                    try
                    {
                        Guid? userId = DataProvider?.Identities?.FirstOrDefault(identity => identity.Class == enumFasdInformationClass.User)?.Id;
                        Guid? computerId = DataProvider?.Identities?.FirstOrDefault(identity => identity.Class == enumFasdInformationClass.Computer)?.Id;

                        var reportQuickActionParameter = new cF4SDQuickActionParameters()
                        {
                            CaseId = DataProvider.CaseId,
                            QuickActionId = RemoteQuickAction.Id,
                            QuickActionName = RemoteQuickAction.Name,
                            Paramaters = ParameterDictionary is null ? null : JsonConvert.SerializeObject(ParameterDictionary, Formatting.None),
                            Result = QuickActionResult.Output.Values is null ? null : JsonConvert.SerializeObject(QuickActionResult.Output.Values, Formatting.None),
                            ErrorCode = QuickActionResult.Output.ErrorCode,
                            ErrorDescription = QuickActionResult.Output.ErrorDescription,
                            Start = startTime,
                            Finish = DateTime.UtcNow,
                            UserId = userId,
                            DeviceId = computerId,
                            ExecutionType = RemoteQuickAction.ExecutionType,
                            ResultType = RemoteQuickAction.ResultType
                        };

                        var reportedQuickActionSuccessfully = await cFasdCockpitCommunicationBase.Instance.ReportQuickAction(reportQuickActionParameter);

                        if (reportedQuickActionSuccessfully is false)
                            LogEntry($"Failed to report QuickAction '{RemoteQuickAction.Name}' for case '{reportQuickActionParameter.CaseId}' to Analytics.", LogLevels.Warning);
                    }
                    catch (Exception E)
                    {
                        LogException(E);
                    }
                });

                return new List<object>() { quickActionStatus, cUtility.RawValueFormatter.GetDisplayValue(DateTime.UtcNow, RawValueType.DATETIME) };
            }
            catch (Exception E)
            {
                LogException(E);
                return new List<object>() { cMultiLanguageSupport.GetItem("QuickAction.Revision.Status.FinishedWithError"), cUtility.RawValueFormatter.GetDisplayValue(DateTime.UtcNow, RawValueType.DATETIME) };
            }

        }

        public static List<cQuickActionMeasureValue> GetStatusMonitorMeasureValues(List<cF4sdQuickActionRevision.cMeasure> measures, Dictionary<int, cF4sdQuickActionMeasure> QuickActionMeasures)
        {
            try
            {
                var output = new List<cQuickActionMeasureValue>();

                if (QuickActionMeasures == null || measures == null)
                    return null;

                foreach (var measure in measures)
                {
                    if (!QuickActionMeasures.TryGetValue(measure.Id, out var measureDefinition))
                        continue;

                    output.Add(new cQuickActionMeasureValue()
                    {
                        Id = measure.Id,
                        Names = measureDefinition.Names,
                        Display = measureDefinition.Display,
                        Value = measure.Value,
                        PostValue = measure.PostValue
                    });
                }

                return output;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
            }

            return null;
        }

        private async Task UpdateQuickActionStatusAsync(int taskId, CancellationToken token)
        {
            try
            {
                const int pollDelayInMilliseconds = 250;

                while (!token.IsCancellationRequested)
                {
                    var quickActionStatus = await cFasdCockpitCommunicationBase.Instance.GetAgentQuickActionStatus(taskId);

                    if (quickActionStatus == enumQuickActionStatus.Unknown)
                        break;

                    List<enumActionStepType> actionStepsToUpdate = new List<enumActionStepType>();

                    switch (quickActionStatus)
                    {
                        case enumQuickActionStatus.ConnectingToClient:
                            break;
                        case enumQuickActionStatus.WaitingForUserAcceptance:
                            actionStepsToUpdate = new List<enumActionStepType>() { enumActionStepType.connectingToClient };
                            break;
                        case enumQuickActionStatus.Running:
                        case enumQuickActionStatus.Finished:
                            actionStepsToUpdate = new List<enumActionStepType>() { enumActionStepType.connectingToClient, enumActionStepType.waitingForUserAcceptance };
                            break;
                        case enumQuickActionStatus.Cancelled:
                            var remainingSteps = StatusMonitor.QuickActionData.ActionSteps.Where(step => step.Status != enumQuickActionRevisionStatus.finishedSuccessfull);

                            foreach (var remainingStep in remainingSteps)
                            {
                                remainingStep.Status = enumQuickActionRevisionStatus.canceled;
                            }
                            break;
                    }

                    foreach (var actionStepToUpdate in actionStepsToUpdate)
                    {
                        cQuickActionStatusMonitorModel.cQuickActionStep.SetQuickActionStepStatuses(StatusMonitor.QuickActionData.ActionSteps, RemoteQuickAction.Name, actionStepToUpdate, enumQuickActionRevisionStatus.finishedSuccessfull);
                    }

                    if (quickActionStatus == enumQuickActionStatus.Finished || quickActionStatus == enumQuickActionStatus.Cancelled)
                        break;

                    await Task.Delay(pollDelayInMilliseconds);
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private async Task<cF4sdQuickActionRevision> GetRevisionForStatusMonitorAsync(int revisionId, int taskId, CancellationToken token)
        {
            try
            {
                if (!token.IsCancellationRequested)
                {
                    var revision = await cFasdCockpitCommunicationBase.Instance.GetAgentRevision(revisionId, cCockpitConfiguration.Instance.agentApiConfiguration.OrganizationCode.Value);
                    return revision;
                }
                else
                    await cFasdCockpitCommunicationBase.Instance.CancelAgentTask(taskId);
            }
            catch (Exception E)
            {
                LogException(E);
            }

            return null;
        }
    }
}
