using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents;
using System.Linq;
using System.Windows.Input;
using System.Diagnostics;

using C4IT.FASD.Base;
using C4IT.Logging;
using C4IT.FASD.Cockpit.Communication;
using C4IT.MultiLanguage;
using static C4IT.Logging.cLogManager;

using FasdDesktopUi.Basics.Helper;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.UserControls;
using FasdDesktopUi.Pages;
using FasdDesktopUi.Pages.CustomMessageBox;
using FasdDesktopUi.Config;
using FasdDesktopUi.Pages.SearchPage;
using FasdDesktopUi.Pages.TicketCompletion;
using Newtonsoft.Json;
using FasdDesktopUi.Basics.Services.ProtocollService;
using FasdDesktopUi.Basics.Services.SupportCase;
using FasdDesktopUi.Basics.Services.RelationService;
using FasdDesktopUi.Basics.Services.SupportCase.Controllers;
using C4IT.F4SD.SupportCaseProtocoll.Models;


namespace FasdDesktopUi.Basics
{
    public sealed class cSupportCaseDataProvider
    {
        public class cSearchResultRelationsByClass : Dictionary<enumFasdInformationClass, List<cF4sdApiSearchResultRelation>>
        {
        }

        public FlowDocument CaseNotes { get; private set; } = new FlowDocument();

        private static cSupportCaseDataProvider CurrentProvider;

        public static Pages.SlimPage.SlimPageView slimPage { get; set; }
        public static Pages.DetailsPage.DetailsPageView detailsPage { get; set; }


        private static readonly Dictionary<Guid, cSupportCaseDataProvider> DataProviders = new Dictionary<Guid, cSupportCaseDataProvider>();

        public EventHandler<bool> NotepadDocumentUpdated { get; set; }

        #region Properties

        private readonly object caseIdLockObject = new object();
        public Guid CaseId { get; private set; }

        public bool IsActive
        {
            get
            {
                return CaseId != Guid.Empty || slimPage?.IsActive is true || detailsPage?.IsActive is true;
            }
        }

        public SupportCasePageBase ActiveSupportCaseWindow
        {
            get
            {
                if (detailsPage?.IsVisible is true)
                    return detailsPage;
                if (slimPage?.IsVisible is true)
                    return slimPage;
                return null;
            }
        }

        System.Timers.Timer caseAliveTimer;

        public cHealthCardDataHelper HealthCardDataHelper { get; private set; }
        public cDirectConnectionHelper DirectConnectionHelper { get; private set; }
        public cQuickActionProtocollHelper QuickActionProtocollHelper { get; private set; }

        public cSearchResultRelationsByClass CaseRelations { get; } = new cSearchResultRelationsByClass();

        public cNamedParameterList NamedParameterEntries { get; private set; } = new cNamedParameterList();

        public cF4sdIdentityList Identities { get; set; }

#if DEBUG
        public string lastCaseRelationsJson = null;
#endif

        #endregion

        public static event EventHandler CaseChanged;

        private cSupportCaseDataProvider()
        {
            HealthCardDataHelper = new cHealthCardDataHelper(this);
            DirectConnectionHelper = new cDirectConnectionHelper(this);
            QuickActionProtocollHelper = new cQuickActionProtocollHelper(this);
        }

        public static async Task<cSupportCaseDataProvider> GetDataProviderForAsync(cF4sdApiSearchResultRelation selectedRelation, IRelationService relationService)
        {
            try
            {
                if (selectedRelation == null)
                {
                    Debug.Assert(true, "The selected relation must not be null here!");
                    LogEntry("The selected relation must not be null here!", LogLevels.Error);
                    return null;
                }

                // get the identities of the selected relation
                var Identities = selectedRelation.Identities.Clone();

                // get the main indentities from all identites (normally this is the user, in a special case, where a computer was searched and found without any user activities, it could be the computer)
                var MainIdentity = Identities.FirstOrDefault(identity => identity.Class == enumFasdInformationClass.User);
                if (MainIdentity == null)
                    MainIdentity = Identities.FirstOrDefault(identity => identity.Class == enumFasdInformationClass.Computer);
                if (MainIdentity == null)
                    MainIdentity = Identities.FirstOrDefault(identity => identity.Class == enumFasdInformationClass.VirtualSession);
                if (MainIdentity == null)
                    MainIdentity = Identities.FirstOrDefault(identity => identity.Class == enumFasdInformationClass.MobileDevice);
                if (MainIdentity == null)
                {
                    LogEntry("Couldn't find neither a user identity nor a computer identity for the selected realtion.", LogLevels.Error);
                    return null;
                }

                var firstHealthCardInformationClass = cF4sdIdentityEntry.GetFromSearchResult(selectedRelation.Type);
                // if we have the special relation computer -> user, the initial information class is changed to computer ( user -> computer )
                if (firstHealthCardInformationClass == enumFasdInformationClass.User)
                    if (Identities.Any(v => (v.Class == enumFasdInformationClass.Computer)))
                        firstHealthCardInformationClass = enumFasdInformationClass.Computer;
                if (Identities.Any(v => (v.Class == enumFasdInformationClass.VirtualSession)))
                    firstHealthCardInformationClass = enumFasdInformationClass.VirtualSession;
                if (Identities.Any(v => (v.Class == enumFasdInformationClass.MobileDevice)))
                    firstHealthCardInformationClass = enumFasdInformationClass.MobileDevice;

                var requiredInformationClasses = new List<enumFasdInformationClass>() { firstHealthCardInformationClass };

                // if we have an opened support case, close it immediately
                if (cSupportCaseDataProvider.CurrentProvider != null)
                {
                    await cSupportCaseDataProvider.CurrentProvider.CloseCaseAsync();
                    cSupportCaseDataProvider.CurrentProvider = null;
                }

                if (!DataProviders.TryGetValue(MainIdentity.Id, out var _result))
                {
                    _result = new cSupportCaseDataProvider();
                    DataProviders.Add(MainIdentity.Id, _result);
                }

                _result.NamedParameterEntries = new cNamedParameterList(_result);

                _result.StartCase(MainIdentity.Id);

                var supportCase = SupportCaseFactory.Get(MainIdentity, relationService, _result);
                var supportCaseProcessor = SupportCaseProcessorFactory.Get(MainIdentity.Id);
                var supportCaseController = new SupportCaseController();

                supportCaseProcessor.SetSupportCase(supportCase);
                supportCaseController.SetSupportCaseProcessor(supportCaseProcessor, Identities);

                cSupportCaseDataProvider.detailsPage.SetSupportCaseController(supportCaseController);
                cSupportCaseDataProvider.slimPage.SetSupportCaseController(supportCaseController);

                var Status = await _result.SetViewDataAsync(requiredInformationClasses, Identities, true);

                cF4sdApiSearchResultRelation relationToFocus = GetRelationToFocus(selectedRelation, relationService.GetLoadedRelations());
                // relation to focus may not have all identities
                supportCaseController.UpdateFocusedCaseRelation(relationToFocus);
                if (!Status)
                    return null;

                HashSet<string> requiredTables = cHealthCard.GetRequiredTables(_result.HealthCardDataHelper.SelectedHealthCard); // todo the healthcard is not selected at this point
                detailsPage.WidgetCollection.WidgetDataList = supportCaseController.GetWidgetData();
                detailsPage.DataHistoryCollectionUserControl.HistoryDataList = supportCaseController.GetHistoryData();
                detailsPage.CustomizableSectionUc.ContainerCollections = supportCaseController.GetContainerData();

                CurrentProvider = _result;

                // start the slim or detaild page
                bool shouldSkipSlimView = Identities.Any(identity => identity.Class is enumFasdInformationClass.Ticket) || cFasdCockpitConfig.Instance.Global.ShouldSkipSlimView;
                if (shouldSkipSlimView)
                {
                    cSupportCaseDataProvider.detailsPage?.Show();
                    cSupportCaseDataProvider.slimPage.Hide();
                }
                else
                {
                    cSupportCaseDataProvider.slimPage.Show();
                    cSupportCaseDataProvider.detailsPage?.Hide();
                }

                return _result;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            return null;
        }

        private static cF4sdApiSearchResultRelation GetRelationToFocus(cF4sdApiSearchResultRelation selectedRelation, IEnumerable<cF4sdApiSearchResultRelation> loadedRelations)
        {
            try
            {
                if (selectedRelation.Type != enumF4sdSearchResultClass.User)
                    return selectedRelation;

                cF4sdIdentityEntry alternativeIdentity = selectedRelation.Identities.FirstOrDefault(i => i.Class != enumFasdInformationClass.User);
                cF4sdApiSearchResultRelation relationToFocus = loadedRelations.FirstOrDefault(r => r.id == alternativeIdentity?.Id && cF4sdIdentityEntry.GetFromSearchResult(r.Type) == alternativeIdentity.Class);

                if (relationToFocus is null)
                    return selectedRelation;

                relationToFocus.Identities = selectedRelation.Identities.Clone();
                return relationToFocus;
            }
            catch (Exception ex)
            {
                LogException(ex);
            }

            return selectedRelation;
        }

        public void StartCase(Guid userId)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                lock (caseIdLockObject)
                {
                    CaseId = Guid.NewGuid();
                }

                if (cFasdCockpitConfig.IsAnalyticsActive)
                {
                    _ = Task.Run(async () =>
                    {
                        try
                        {
                            var caseParameter = new cF4SDCaseParameters() { CaseId = CaseId, SessionId = cFasdCockpitConfig.SessionId, UserId = userId };
                            var successfullyStartedCase = await cFasdCockpitCommunicationBase.Instance.StartCase(caseParameter);

                            if (successfullyStartedCase is false)
                            {
                                LogEntry($"Could not start case '{caseParameter.CaseId}' for user '{caseParameter.UserId}' in session '{caseParameter.SessionId}'.", LogLevels.Warning);
                                return;
                            }

                            caseAliveTimer = new System.Timers.Timer(1000 * 60 * 1);
                            caseAliveTimer.Elapsed += (async (sender, e) => { await cFasdCockpitCommunicationBase.Instance.KeepAliveCase(CaseId); });
                            caseAliveTimer.Start();
                        }
                        catch (Exception E)
                        {
                            LogException(E);
                        }
                    });
                }

                TimerView.ResetTimer();
                CaseChanged?.Invoke(this, new EventArgs());
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public async Task CloseCaseAsync()
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                Guid tempCaseId;
                lock (caseIdLockObject)
                {
                    tempCaseId = CaseId;
                    if (CaseId.Equals(Guid.Empty))
                        return;

                    CaseId = Guid.Empty;
                }


                detailsPage?.EndPause();

                List<Task> tasks = new List<Task>();
                tasks.Add(DirectConnectionHelper.DirectConnectionStopAsync());

                if (cFasdCockpitConfig.IsAnalyticsActive)
                {
                    var workTimes = TimerView.GetWorkTimes();
                    var nettoTime = workTimes["NettoWorkingTime"];

                    var caseParameter = new cF4SDCaseStatusParameters() { CaseId = tempCaseId, StatusId = CaseStatus.Finished, ActiveTime = (double)nettoTime };
                    tasks.Add(cFasdCockpitCommunicationBase.Instance.UpdateCase(caseParameter, TimerView.caseTimes));
                    caseAliveTimer?.Stop();
                    caseAliveTimer?.Dispose();
                }

                await Task.WhenAll(tasks);
                ResetCachedData();
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        private void ResetCachedData()
        {
            try
            {
                CaseNotes = new FlowDocument();
                NotepadDocumentUpdated?.Invoke(this, true);
                DirectConnectionHelper.Reset();
                HealthCardDataHelper.Reset();
                F4SDProtocoll.Instance.Clear();
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }
        static bool IsFlowDocumentNotEmpty(FlowDocument doc)
        {
            if (doc == null)
                return false;

            // Erstellt einen TextRange, der den gesamten Inhalt des Dokuments abdeckt
            TextRange textRange = new TextRange(doc.ContentStart, doc.ContentEnd);
            return !string.IsNullOrWhiteSpace(textRange.Text);
        }
        public static async Task<bool> SupportTicketActiveNoticeAsync()
        {
            // returns true, if we can continue with the search process
            // returns false, if we have to stop the search process, because the current case remains opened

            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
            try
            {
                if (cSupportCaseDataProvider.CurrentProvider?.IsActive != true)
                    return true;

                Mouse.OverrideCursor = null;
                var ownerWindow = cSupportCaseDataProvider.CurrentProvider.ActiveSupportCaseWindow;
                var ButtonText = new List<string>()
                {
                    cMultiLanguageSupport.GetItem("Searchbar.NewSearch.ActiveSupportCase.CloseCase"),
                    cMultiLanguageSupport.GetItem("Searchbar.NewSearch.ActiveSupportCase.CancelCase"),
                    cMultiLanguageSupport.GetItem("Searchbar.NewSearch.ActiveSupportCase.ContinueCase")
                };
                int ResultIndex = 1;
                var completitionPolicy = cFasdCockpitConfig.Instance.Global.TicketConfiguration.CompletitionPolicy;
                if (completitionPolicy == enumShowDocumentCaseDialog.always ||
                   (completitionPolicy == enumShowDocumentCaseDialog.ifRequired &&
                    (F4SDProtocoll.Instance.GetOfType<QuickActionProtocollEntry>().Count() > 0 ||
                     IsFlowDocumentNotEmpty(CurrentProvider.CaseNotes))))
                {
                    ResultIndex = CustomMessageBox.Show(cMultiLanguageSupport.GetItem("Searchbar.NewSearch.ActiveSupportCase"), ButtonText, "First Aid Service Desk", enumHealthCardStateLevel.Warning, Owner: ownerWindow, TopMost: true, CenterScreen: ownerWindow is Pages.SlimPage.SlimPageView, MaxWidth: 700);
                }
                switch (ResultIndex)
                {
                    case 0: // close case with ticket completion
                        var _res = CloseCaseWithTicket(ownerWindow);
                        if (_res)
                        {
                            ownerWindow?.Hide();
                            return true;
                        }
                        break;
                    case 1: // close case immediately
                        await cSupportCaseDataProvider.CurrentProvider?.CloseCaseAsync();
                        ownerWindow?.Hide();
                        return true;
                    default: // return to current support case
                        break;
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }

            return false;
        }

        public static async Task<bool> CloseSupportCaseAsync()
        {
            MethodBase CM = null; if (DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }

            try
            {
                var doClose = await SupportTicketActiveNoticeAsync();

                if (doClose)
                {
                    if (cSearchManager.Instance?.HistoryList?.LastOrDefault()?.isSeen != true)
                    {
                        SearchPageView.Instance.ActivateSearchView();
                    }
                }
                return doClose;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }

            return false;
        }

        public static bool CloseCaseWithTicket(Window ownerWindow)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var closeCaseDialogResult = TicketCompletion.Show(cSupportCaseDataProvider.CurrentProvider, ownerWindow);

                if (closeCaseDialogResult != null)
                {
                    var _h = cSupportCaseDataProvider.CurrentProvider?.CloseCaseAsync();
                }

                return closeCaseDialogResult != null;
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

            return false;
        }

        public async Task<bool> SetViewDataAsync(List<enumFasdInformationClass> requiredInformationClasses, cF4sdIdentityList Identities, bool isNewCase)
        {
            try
            {
                var couldGetHealthCard = HealthCardDataHelper.TrySetSelectedHealthcard(requiredInformationClasses);

                if (!couldGetHealthCard)
                {
                    LogEntry("Couldn't find a matching healthcard.", LogLevels.Error);
                    CustomMessageBox.Show(cMultiLanguageSupport.GetItem("SearchBar.NoValidHealthcard"), "Load new case", enumHealthCardStateLevel.Error);
                    return false;
                }

                var requiredTables = cHealthCard.GetRequiredTables(HealthCardDataHelper.SelectedHealthCard);

                if (cF4SDCustomDialogConfig.Instance != null)
                {
                    var requiredTablesForCustomDialogs = cF4SDCustomDialogConfig.Instance.GetRequiredTables(HealthCardDataHelper.SelectedHealthCard.InformationClasses);
                    foreach (var table in requiredTablesForCustomDialogs)
                    {
                        requiredTables.Add(table);
                    }
                }


                if (isNewCase)
                    HealthCardDataHelper.LoadingHelper.LastDataRequest = DateTime.Now;

                var slimPageData = await HealthCardDataHelper.SlimCard.GetDataAsync();
                slimPage?.SetPropertyValues(slimPageData);

                return true;
            }
            catch (Exception E)
            {
                LogException(E);
            }

            return false;

        }


        public async Task ChangeHealthCardAsync(enumFasdInformationClass informationClass)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                await SetViewDataAsync(new List<enumFasdInformationClass>() { informationClass }, Identities, false);
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }


        public async Task ExchangeCaseIdentitiesAsync(cF4sdApiSearchResultRelation searchResultRelation)
        {

#if DEBUG
            var jsonRels = JsonConvert.SerializeObject(CaseRelations);
            if (lastCaseRelationsJson != null && lastCaseRelationsJson.Equals(jsonRels) is false)
                LogEntry($"CaseRelations changed: {jsonRels}", LogLevels.Debug);
            lastCaseRelationsJson = jsonRels;
            var jsonResult = JsonConvert.SerializeObject(searchResultRelation);
            var jsonIds = JsonConvert.SerializeObject(Identities);
#endif
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var ComputerRemoved = false;
                var ComputerAdded = false;
                List<enumFasdInformationClass> updatedInformationClasses = new List<enumFasdInformationClass>();
                foreach (var relationIdentity in searchResultRelation.Identities)
                {
                    var existingIdentity = Identities.FirstOrDefault(identity => identity.Class == relationIdentity.Class);

                    if (existingIdentity != null)
                    {
                        if (existingIdentity.Id.Equals(relationIdentity.Id) is false)
                        {
                            updatedInformationClasses.Add(existingIdentity.Class);
                            ComputerRemoved |= relationIdentity.Class == enumFasdInformationClass.Computer;
                            ComputerAdded |= relationIdentity.Class == enumFasdInformationClass.Computer;
                        }
                    }

                }

                HealthCardDataHelper.RemoveTablesWithUpdatedIdentities(updatedInformationClasses);

                if (ComputerRemoved)
                    await CurrentProvider?.DirectConnectionHelper?.DirectConnectionStopAsync();

                var searchResultInfoClass = cF4sdIdentityEntry.GetFromSearchResult(searchResultRelation.Type);
                await SetViewDataAsync(new List<enumFasdInformationClass>() { searchResultInfoClass }, Identities, false);

            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
#if DEBUG
                jsonRels = JsonConvert.SerializeObject(CaseRelations);
                jsonResult = JsonConvert.SerializeObject(searchResultRelation);
                jsonIds = JsonConvert.SerializeObject(Identities);
                if (lastCaseRelationsJson != null && lastCaseRelationsJson.Equals(jsonRels) is false)
                    LogEntry($"CaseRelations changed: {jsonRels}", LogLevels.Debug);
                lastCaseRelationsJson = jsonRels;
#endif
                LogMethodEnd(CM);
            }
        }

        #region Case Notes

        public static event EventHandler CaseNotesChanged;

        public void SaveCaseNotes() => CaseNotesChanged?.Invoke(this, EventArgs.Empty);

        #endregion
    }
}
