using C4IT.FASD.Base;
using C4IT.FASD.Cockpit.Communication;
using C4IT.Logging;
using C4IT.MultiLanguage;
using FasdDesktopUi.Basics;
using FasdDesktopUi.Basics.CustomEvents;
using FasdDesktopUi.Basics.Enums;
using FasdDesktopUi.Basics.Helper;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.UiActions;
using FasdDesktopUi.Basics.UserControls;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using FasdDesktopUi.Basics.Services.SupportCaseSearchService;
using FasdDesktopUi.Basics.Services.RelationService;
using static C4IT.Logging.cLogManager;
using static FasdDesktopUi.Basics.UserControls.SearchBar;


namespace FasdDesktopUi.Pages.AdvancedSearchPage
{
    public partial class AdvancedSearchPage : Window, ISearchUiProvider, INotifyPropertyChanged
    {
        public SupportCaseSearchService SearchService { get; } = new SupportCaseSearchService(new RelationService());
        // New Advanced-Search-Page IN PROGRESS
        // Next steps:
        // 1. Open F4SD Cockpit when user is clicked
        // 2. Display filter name next to filter icon when filter is selected
        // 3. When F4SD Tray-Icon is clicked, open the new advanced search page


        SearchBar.ChangedSearchValueDelegate ChangedSearchValue { get; set; }
        private cF4sdApiSearchResultRelation preSelectedRelation = null;

        private static readonly object _currentSearchTaskLock = new object();
        private static Guid? CurrentSearchTaskId = null;
        private static CancellationTokenSource CurrentSearchTaskToken = null;

        private readonly DispatcherTimer _searchTimer = new DispatcherTimer();
        private string searchValue = null;

        public static AdvancedSearchPage advancedSearchPage;

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));


        // Mapping zwischen den Enum-Classes, da Integer-Values nicht alle identisch sind. Evtl. im nachgang anpassen, aber vorher alle Abhängigkeiten prüfen
        private enumFasdInformationClass MapSearchResultClassToInformationClass(enumF4sdSearchResultClass searchResultClass)
        {
            switch (searchResultClass)
            {
                case enumF4sdSearchResultClass.Computer: return enumFasdInformationClass.Computer;
                case enumF4sdSearchResultClass.User: return enumFasdInformationClass.User;
                case enumF4sdSearchResultClass.Ticket: return enumFasdInformationClass.Ticket;
                case enumF4sdSearchResultClass.VirtualSession: return enumFasdInformationClass.VirtualSession;
                case enumF4sdSearchResultClass.MobileDevice: return enumFasdInformationClass.MobileDevice;
                default: return enumFasdInformationClass.Unknown;
            }
        }

        // Alle Suchergebnisse (ungefilterte Sammlung)
        public cFasdApiSearchResultCollection SearchResults { get; set; }

        // Gefilterte Suchergebnisse (für die Anzeige in der UI)
        private cFasdApiSearchResultCollection _filteredSearchResults;
        public cFasdApiSearchResultCollection FilteredSearchResults
        {
            get => _filteredSearchResults;
            set
            {
                if (_filteredSearchResults != value)
                {
                    _filteredSearchResults = value;
                    OnPropertyChanged();
                }
            }
        }

        // Aktuell gesetzte Filterklassen
        public HashSet<enumFasdInformationClass> StoredFilterInformationClasses { get; set; }

        public static readonly DependencyProperty FilterInformationClassesProperty =
            DependencyProperty.Register("FilterInformationClasses", typeof(HashSet<enumFasdInformationClass>), typeof(AdvancedSearchPage), new PropertyMetadata(new HashSet<enumFasdInformationClass>()));

        // Ermittelte Filter bei der Suche
        public HashSet<enumFasdInformationClass> FilterInformationClasses
        {
            get { return (HashSet<enumFasdInformationClass>)GetValue(FilterInformationClassesProperty); }
            set { SetValue(FilterInformationClassesProperty, value); }
        }

        public eSearchStatus searchStatus { get; private set; } = eSearchStatus.message;

        public AdvancedSearchPage()
        {
            InitializeComponent();
            AddCustomEventHandlers();
            SearchBarUc.ActivateManualSearch();

            advancedSearchPage = this;
        }

        private void AddCustomEventHandlers()
        {
            AddHandler(cUiActionBase.UiActionClickedEvent, new cUiActionBase.UiActionEventHandlerDelegate(UiActionWasTriggered));
            SearchBarUc.ChangedSearchValue = ChangedSearchValueActionAsync;
            SearchBarUc.CancledSearchAction = CancledSearchAction;
        }


        private Task ChangedSearchValueActionAsync(cFilteredResults filteredResults)
        {
            this.Dispatcher.Invoke(new Action(() =>
            {
                try
                {
                    if (filteredResults?.Results is null)
                    {
                        SetSearchResultVisibility(false);
                        return;
                    }

                    // Speichern der Kompletten Suchergebnisse
                    SearchResults = filteredResults.Results;

                    // Verfügbare FilterIcons dynamisch ermitteln
                    UpdateFilterInformationClasses();

                    // Filter anwenden
                    ApplyFilters();

                    // Menü mit den gefilterten Ergebnissen anzeigen
                    string menuHeaderText = filteredResults?.PreSelectedRelation?.DisplayName;
                    ResultMenu.ShowSearchResults(filteredResults, menuHeaderText, this);

                    preSelectedRelation = filteredResults.PreSelectedRelation;
                    SetSearchResultVisibility(true);

                }
                catch (Exception E)
                {
                    cLogManager.LogException(E);
                }
            }));

            return Task.CompletedTask;
        }

        private void UpdateFilterInformationClasses()
        {
            if (SearchResults == null)
            {
                FilterInformationClasses = new HashSet<enumFasdInformationClass>();
                return;
            }

            var filter = new HashSet<enumFasdInformationClass>();

            foreach (var group in SearchResults)
            {
                foreach (var entry in group.Value)
                {
                    var mappedClass = MapSearchResultClassToInformationClass(entry.Type);
                    filter.Add(mappedClass);
                }
            }
            FilterInformationClasses = filter;
        }

        private void SearchFilterBar_FilterChanged(object sender, SearchFilterChangedEventArgs e)
        {
            // Filter speichern
            StoredFilterInformationClasses = new HashSet<enumFasdInformationClass>(e.InformationClassesToShow);

            // Filter anwenden
            ApplyFilters();

            // cFilteredResults erzeugen
            var filteredResultWrapper = new cFilteredResults()
            {
                Results = FilteredSearchResults,
                PreSelectedRelation = preSelectedRelation
            };

            // Ergebnisse im Menü neu anzeigen
            string menuHeaderText = preSelectedRelation?.DisplayName;
            ResultMenu.ShowSearchResults(filteredResultWrapper, menuHeaderText, this);
        }

        private void ApplyFilters()
        {
            if (SearchResults == null)
                return;

            // Wenn keine Filter ausgewählt wurden, alles übernehmen
            if (StoredFilterInformationClasses == null || StoredFilterInformationClasses.Count == 0)
            {
                FilteredSearchResults = SearchResults;
                return;
            }

            // Wenn ein oder mehrere Filter gesetzt, dann Ergebnisse entsprechend gefiltert anzeigen
            cFasdApiSearchResultCollection filtered = new cFasdApiSearchResultCollection();

            foreach (var group in SearchResults)
            {
                var filteredEntries = group.Value
                    .Where(entry =>
                        StoredFilterInformationClasses.Contains(MapSearchResultClassToInformationClass(entry.Type))
                        )
                    .ToList();

                if (filteredEntries.Any())
                {
                    filtered.Add(group.Key, filteredEntries);
                }
            }

            FilteredSearchResults = filtered;
        }

        public void ShowSearchRelations(cSearchHistorySearchResultEntry searchHistoryEntry, IRelationService relationService, ISearchUiProvider searchUiProvider)
        {
            try
            {
                ResultMenu.SetHeaderText(searchHistoryEntry.HeaderText, hideDetailsCheckbox: false);

                cSearchManager.ResolveRelations(searchHistoryEntry.Relations);

                ILookup<enumFasdInformationClass, cMenuDataBase> relationsLookup = searchHistoryEntry.Relations
                    .OrderBy(r => r.UsingLevel)
                    .ThenBy(r => r.LastUsed)
                    .ToLookup(GetInformationClass, GetMenuData);

                ResultMenu.UpdateSearchRelations(relationsLookup);
                ResultMenu.SetHeaderText(searchHistoryEntry.HeaderText, hideDetailsCheckbox: false);
            }
            catch (Exception ex)
            {
                LogException(ex);
            }

            cMenuDataBase GetMenuData(cF4sdApiSearchResultRelation relation)
            {
                var requiredInformationClasses = new List<enumFasdInformationClass>() { cF4sdIdentityEntry.GetFromSearchResult(relation.Type) };
                bool isEnabled = cHealthCardDataHelper.HasAvailableHealthCard(requiredInformationClasses);
                string disabledReason = null;

                if (!isEnabled)
                {
                    disabledReason = cMultiLanguageSupport.GetItem("Searchbar.NoValidHealthcard") ?? string.Empty;
                }

                string trailingText = null;

                return new cMenuDataSearchRelation(relation)
                {
                    MenuText = relation.DisplayName,
                    TrailingText = trailingText,
                    UiAction = new cUiProcessSearchRelationAction(searchHistoryEntry, relation, relationService, searchUiProvider)
                    {
                        DisplayType = isEnabled ? enumActionDisplayType.enabled : enumActionDisplayType.disabled,
                        Description = isEnabled ? string.Empty : disabledReason,
                        AlternativeDescription = isEnabled ? string.Empty : disabledReason
                    }
                };
            }
            enumFasdInformationClass GetInformationClass(cF4sdApiSearchResultRelation relation) => cF4sdIdentityEntry.GetFromSearchResult(relation.Type);

        }

        public void SetPendingInformationClasses(HashSet<enumFasdInformationClass> informationClasses) => ResultMenu.SetPendingInformationClasses(informationClasses);

        public void UpdatePendingInformationClasses(HashSet<enumFasdInformationClass> informationClasses) => ResultMenu.UpdatePendingInformationClasses(informationClasses);

        private async void UiActionWasTriggered(object sender, UiActionEventArgs e)
        {
            try
            {
                if (e.UiAction == null)
                    return;

                Mouse.OverrideCursor = Cursors.Wait;

                switch (e.UiAction)
                {
                    case cUiProcessSearchResultAction searchResultAction:
                        searchResultAction.PreSelectedSearchRelation = preSelectedRelation;
                        break;
                    case cUiProcessSearchRelationAction _:
                    case cUiProcessSearchHistoryEntry _:
                        break;
                    default:
                        var _t = e.UiAction?.GetType().Name;
                        Debug.Assert(true, $"The UI action '{_t}' is not supported in detailed page");
                        CancledSearchAction();
                        return;
                }

                SetSearchResultVisibility(true);
                if (!await e.UiAction.RunUiActionAsync(sender, null, false, null))
                    CancledSearchAction();
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                Mouse.OverrideCursor = null;
            }
        }

        private void SetSearchResultVisibility(bool isVisible)
        {
            SearchResultBorder.Visibility = isVisible ? Visibility.Visible : Visibility.Collapsed;
            BodyStack.Visibility = (isVisible || SearchResultBorder.IsVisible) ? Visibility.Visible : Visibility.Collapsed;
        }

        private void CancledSearchAction()
        {
            SearchBarUc.Clear();
            ResultMenu.ShowSearchResults(new cFilteredResults(), null, this);
            SetSearchResultVisibility(false);
            SetSearchHistoryVisibility(false);
            Visibility = Visibility.Hidden;
        }




        #region Cleanup when implementing search

        public void SetSearchHistoryVisibility(bool isVisible)
        {
            SearchHistoryBorder.Visibility = isVisible && !SearchHistory.IsEmpty() ? Visibility.Visible : Visibility.Collapsed;
            BodyStack.Visibility = (isVisible || SearchResultBorder.IsVisible) ? Visibility.Visible : Visibility.Collapsed;
        }

        private void ComputerDomainSearch(cComputerDomainSearchParameters searchInfo)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var _h = Task.Run(async () =>
                {

                    try
                    {
                        var _result = await cFasdCockpitCommunicationBase.Instance.GetComputerSearchResults(searchInfo.name, searchInfo.domain);
                        var strInfo = string.Format(cMultiLanguageSupport.GetItem("Searchbar.ComputerSearch.Info"), searchInfo.name);
                        ShowExternalSearchInfo(strInfo, _result, enumF4sdSearchResultClass.Computer);

                    }
                    catch (Exception E)
                    {
                        LogException(E);
                    }
                });
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

        }

        private void ShowExternalSearchInfo(string strInfo, cFasdApiSearchResultCollection resultEntry, enumF4sdSearchResultClass Class)
        {
            var filteredResults = new cFilteredResults(resultEntry) { AutoContinue = true };
            var _t = ShowExternalSearchInfoAsync(strInfo, filteredResults, Class);
        }

        private async Task ShowExternalSearchInfoAsync(string strInfo, cFilteredResults result, enumF4sdSearchResultClass Class)
        {
            try
            {
                await this.Dispatcher.InvokeAsync(async () =>
                {
                    try
                    {
                        await SearchBarUc.SetFixedSearchResultAsync(Class, strInfo, result);

                        if (result.AutoContinue && result.Results?.Count == 1)
                        {
                            ResultMenu.IndexOfSelectedResultItem = 0;
                            ResultMenu.SelectCurrentResultItem();
                        }

                        Show();
                    }
                    catch (Exception E)
                    {
                        LogException(E);
                    }
                });
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        public string SearchValue
        {
            get => searchValue;
            set
            {
                if (value != searchValue)
                {
                    searchValue = value;
                    if (searchStatus == eSearchStatus.active)
                        UpdateSearchResults();
                }
            }
        }

        private async Task UpdateSearchResultsAsync(string searchValue, CancellationToken cancellationToken)
        {
            try
            {
                if (string.IsNullOrEmpty(searchValue))
                {
                    await ChangedSearchValue.Invoke(null);
                }
                else
                {
                    cFasdApiSearchResultCollection filteredResultDictionary = null;

                    try
                    {
                        var taskId = await cFasdCockpitCommunicationBase.Instance.GetSearchResultsStart(searchValue, CancellationToken.None);
                        lock (_currentSearchTaskLock)
                        {
                            CurrentSearchTaskId = taskId;
                        }
                        var _canceled = false;
                        if (!cancellationToken.IsCancellationRequested && taskId != null)
                            try
                            {
                                filteredResultDictionary = await cFasdCockpitCommunicationBase.Instance.GetSearchResultsResult((Guid)taskId, cancellationToken);
                            }
                            catch (TaskCanceledException)
                            {
                                _canceled = true;
                                LogEntry("UpdateSearchResultsAsync was cancelled by token (4).", LogLevels.Debug);
                            }
                        _canceled |= cancellationToken.IsCancellationRequested;
                        taskId = null;
                        if (_canceled)
                            lock (_currentSearchTaskLock)
                            {
                                taskId = CurrentSearchTaskId;
                                CurrentSearchTaskId = null;
                            }
                        if (taskId != null)
                            await cFasdCockpitCommunicationBase.Instance.GetSearchResultsStop((Guid)taskId, CancellationToken.None);
                    }

                    catch (TaskCanceledException)
                    {
                        LogEntry("UpdateSearchResultsAsync was cancelled by token (3).", LogLevels.Debug);
                    }
                    catch (Exception E)
                    {
                        LogException(E);
                    }

                    if (cancellationToken.IsCancellationRequested)
                    {
                        LogEntry("UpdateSearchResultsAsync was cancelled by token (2).", LogLevels.Debug);
                        return;
                    }

                    cFilteredResults filteredResult = new cFilteredResults(filteredResultDictionary);
                    await ChangedSearchValue.Invoke(filteredResult);
                }
                LogEntry("UpdateSearchResultsAsync finished successful.", LogLevels.Warning);
            }
            catch (TaskCanceledException)
            {
                LogEntry("UpdateSearchResultsAsync was cancelled by token (1).", LogLevels.Warning);
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private void UpdateSearchResults()
        {
            MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }

            try
            {
                if (ChangedSearchValue == null)
                    return;

                if (_searchTimer.IsEnabled)
                    _searchTimer.Stop();

                _searchTimer.Start();

                var stopTask = Task.Run(() =>
                {
                    CancelRunningSearchTaskAsync();
                });
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                if (CM != null) LogMethodEnd(CM);
            }

        }

        private void CancelRunningSearchTaskAsync()
        {
            // cancel the search task, in case of a running search task
            lock (_currentSearchTaskLock)
            {
                if (CurrentSearchTaskToken != null)
                {
                    LogEntry("Canceling current running search...");
                    CurrentSearchTaskToken.Cancel(true);
                    CurrentSearchTaskToken = null;
                }
            }

        }

        public void ShowLoadingTextItem(string itemText)
        {
            SetSearchHistoryVisibility(false);
            ResultMenu.ShowLoadingTextItem(itemText);
        }

        #endregion

    }
}

