using C4IT.FASD.Base;
using C4IT.FASD.Cockpit.Communication;
using C4IT.Logging;
using C4IT.MultiLanguage;
using FasdDesktopUi.Basics;
using FasdDesktopUi.Basics.Enums;
using FasdDesktopUi.Basics.Helper;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Services;
using FasdDesktopUi.Basics.Services.Models;
using FasdDesktopUi.Basics.Services.RelationService;
using FasdDesktopUi.Basics.Services.SupportCaseSearchService;
using FasdDesktopUi.Basics.UiActions;
using FasdDesktopUi.Basics.UserControls;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
using static C4IT.Logging.cLogManager;
using static FasdDesktopUi.Basics.UserControls.TicketOverview;

namespace FasdDesktopUi.Pages.SearchPage
{
    public partial class SearchPageView : Window, ISearchUiProvider
    {
        private static SearchPageView _instance = null;

        #region Ticketübersicht

        private readonly HashSet<TileScope> _ticketOverviewNotificationScopesPrimed = new HashSet<TileScope>();
        private bool _ticketOverviewFirstEventHandled;
        private readonly HashSet<TileScope> _ticketOverviewInitWasEmptyScopes = new HashSet<TileScope>();
        private bool _ticketOverviewAutoContinueActive;
        private DispatcherTimer _ticketOverviewAutoContinueFallbackTimer;
        private EventHandler _ticketOverviewAutoContinueCaseChangedHandler;
        private bool _renderTicketOverviewUserNames = false;
        private readonly HashSet<cSearchHistorySearchResultEntry> _ticketOverviewHistoryEntries = new HashSet<cSearchHistorySearchResultEntry>();

        // Event zum auslösen wenn Toggle geändert wird
        public event EventHandler<bool> FilterToggleCheckedChanged;

        // Aktueller Zustand der Checkbox
        public bool IsFilterChecked => FilterCheckbox.IsChecked == true;

        #endregion
        public static SearchPageView Instance
        {
            get
            {
                return _instance ?? (_instance = new SearchPageView());
            }
        }

        private cF4sdPipeServer _pipeServer;
        private cHotKeyManager _hotKeyManager;

        private CancellationTokenSource _searchCancellationTokenSource = new CancellationTokenSource();

        private bool _isActivating = false;

        private cF4sdApiSearchResultRelation preSelectedRelation = null;

        private readonly IRelationService _relationService = new RelationService();
        public SupportCaseSearchService SearchService { get; }

        private SearchPageView()
        {
            try
            {
                InitializeComponent();
                SearchService = new SupportCaseSearchService(_relationService);
                Visibility = Visibility.Visible;
                _instance = this;

                GetPrimaryScreenSize();

                // FilterToggleCheckBox-Events registrieren
                FilterCheckbox.Checked += (s, e) => FilterToggleCheckedChanged?.Invoke(this, true);
                FilterCheckbox.Unchecked += (s, e) => FilterToggleCheckedChanged?.Invoke(this, false);

                Loaded += (s, e) =>
                {
                    Hide();
                    SearchBarUc.ActivateManualSearch();
                    ScheduleSearchResultMaxHeightUpdate();
                };
                SizeChanged += (s, e) => ScheduleSearchResultMaxHeightUpdate();
                SearchBarUc.SizeChanged += (s, e) => ScheduleSearchResultMaxHeightUpdate();
                WindowStateBarContainer.SizeChanged += (s, e) => ScheduleSearchResultMaxHeightUpdate();
                BodyStack_TicketOverview.SizeChanged += (s, e) => ScheduleSearchResultMaxHeightUpdate();
                SearchHistoryBorder.SizeChanged += (s, e) => ScheduleSearchResultMaxHeightUpdate();

                AddCustomEventHandlers();

                if (TicketOverviewUpdateService.Instance != null)
                {
                    TicketOverviewUpdateService.Instance.OverviewCountsChanged += TicketOverviewUpdateService_OverviewCountsChanged;
                }

                UiSettingsChanged(null, null);
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        public void GetPrimaryScreenSize()
        {
            UpdateSearchResultMaxHeight();
        }

        private void ScheduleSearchResultMaxHeightUpdate()
        {
            if (Dispatcher == null || Dispatcher.HasShutdownStarted)
                return;

            Dispatcher.BeginInvoke((Action)UpdateSearchResultMaxHeight, DispatcherPriority.Loaded);
        }

        private void UpdateSearchResultMaxHeight()
        {
            if (SearchResultBorder == null)
                return;

            var workAreaHeight = ActualHeight > 0 ? ActualHeight : SystemParameters.WorkArea.Height;
            var reservedHeight = 0.0;

            reservedHeight += GetVisibleHeightWithMargin(SearchBarUc);
            reservedHeight += GetVisibleHeightWithMargin(WindowStateBarContainer);
            reservedHeight += GetVisibleHeightWithMargin(BodyStack_TicketOverview);
            reservedHeight += GetVisibleHeightWithMargin(SearchHistoryBorder);

            if (MainBorder != null)
            {
                reservedHeight += MainBorder.Padding.Top + MainBorder.Padding.Bottom;
            }

            var searchResultMargin = SearchResultBorder.Margin;
            var availableHeight = workAreaHeight - reservedHeight - searchResultMargin.Top - searchResultMargin.Bottom;
            availableHeight = Math.Max(0, availableHeight);
            SearchResultBorder.MaxHeight = availableHeight;
        }

        private static double GetVisibleHeightWithMargin(FrameworkElement element)
        {
            if (element == null || element.Visibility != Visibility.Visible)
                return 0;

            var margin = element.Margin;
            return element.ActualHeight + margin.Top + margin.Bottom;
        }

        private void SetSearchResultVisibility(bool isVisible)
        {
            SearchResultBorder.Visibility = isVisible ? Visibility.Visible : Visibility.Collapsed;
            BodyStack_SearchResults.Visibility =
                (SearchResultBorder.Visibility == Visibility.Visible || SearchHistoryBorder.Visibility == Visibility.Visible)
                    ? Visibility.Visible
                    : Visibility.Collapsed;
            ScheduleSearchResultMaxHeightUpdate();
        }

        public void SetSearchHistoryVisibility(bool isVisible)
        {
            SearchHistoryBorder.Visibility = isVisible && !SearchHistory.IsEmpty() ? Visibility.Visible : Visibility.Collapsed;
            BodyStack_SearchResults.Visibility =
                (SearchResultBorder.Visibility == Visibility.Visible || SearchHistoryBorder.Visibility == Visibility.Visible)
                    ? Visibility.Visible
                    : Visibility.Collapsed;
            ScheduleSearchResultMaxHeightUpdate();
        }
        public void ShowLoadingTextItem(string itemText)
        {
            SetSearchHistoryVisibility(false);
            ResultMenu.ShowLoadingTextItem(itemText);
            BodyStack_SearchResults.Visibility = Visibility.Visible;
        }

        public void ShowSearchRelations(cSearchHistorySearchResultEntry searchHistoryEntry, IRelationService relationService, ISearchUiProvider searchUiProvider)
        {
            try
            {
                _renderTicketOverviewUserNames = searchHistoryEntry != null && _ticketOverviewHistoryEntries.Contains(searchHistoryEntry);
                Dispatcher.Invoke(() => ResultMenu.SetHeaderText(searchHistoryEntry.HeaderText, hideDetailsCheckbox: _renderTicketOverviewUserNames));

                cSearchManager.ResolveRelations(searchHistoryEntry.Relations);

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

                Dispatcher.Invoke(() =>
                {
                    ResultMenu.UpdateSearchRelations(relationsLookup);
                    ResultMenu.SetHeaderText(searchHistoryEntry.HeaderText, hideDetailsCheckbox: _renderTicketOverviewUserNames);
                });
            }
            catch (Exception ex)
            {
                LogException(ex);
            }

            enumFasdInformationClass GetInformationClass(cF4sdApiSearchResultRelation relation) => cF4sdIdentityEntry.GetFromSearchResult(relation.Type);

            cMenuDataBase GetMenuData(cF4sdApiSearchResultRelation relation, IRelationService subRelationService)
            {
                try
                {
                    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;
                    if (_renderTicketOverviewUserNames && relation.Infos != null && relation.Infos.TryGetValue("UserDisplayName", out var relationUserDisplayName))
                    {
                        trailingText = relationUserDisplayName;
                    }
                    string summaryText = null;
                    if (_renderTicketOverviewUserNames && relation.Infos != null && relation.Infos.TryGetValue("Summary", out var relationSummary))
                    {
                        summaryText = relationSummary;
                    }
                    return new cMenuDataSearchRelation(relation)
                    {
                        MenuText = relation.DisplayName,
                        SubMenuText = summaryText,
                        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
                        }
                    };
                }
                catch (Exception ex)
                {
                    LogException(ex);
                    // >>> Fallback NICHT null: einfacher, aktivierter Eintrag <<<
                    string fallbackTrailingText = null;
                    if (_renderTicketOverviewUserNames && relation.Infos != null && relation.Infos.TryGetValue("UserDisplayName", out var relationUserDisplayNameFallback))
                    {
                        fallbackTrailingText = relationUserDisplayNameFallback;
                    }
                    string fallbackSummaryText = null;
                    if (_renderTicketOverviewUserNames && relation.Infos != null && relation.Infos.TryGetValue("Summary", out var relationSummaryFallback))
                    {
                        fallbackSummaryText = relationSummaryFallback;
                    }
                    string fallbackDisabledReason = null;
                    bool fallbackIsEnabled = true;
                    try
                    {
                        var required = new List<enumFasdInformationClass> { cF4sdIdentityEntry.GetFromSearchResult(relation.Type) };
                        fallbackIsEnabled = cHealthCardDataHelper.HasAvailableHealthCard(required);
                        if (!fallbackIsEnabled)
                        {
                            fallbackDisabledReason = cMultiLanguageSupport.GetItem("Searchbar.NoValidHealthcard") ?? string.Empty;
                        }
                    }
                    catch
                    {
                        fallbackIsEnabled = true;
                        fallbackDisabledReason = string.Empty;
                    }
                    return new cMenuDataSearchRelation(relation)
                    {
                        MenuText = relation.DisplayName,
                        SubMenuText = fallbackSummaryText,
                        TrailingText = fallbackTrailingText,
                        UiAction = new cUiProcessSearchRelationAction(searchHistoryEntry, relation, relationService, searchUiProvider)
                        {
                            DisplayType = fallbackIsEnabled ? enumActionDisplayType.enabled : enumActionDisplayType.disabled,
                            Description = fallbackIsEnabled ? string.Empty : fallbackDisabledReason,
                            AlternativeDescription = fallbackIsEnabled ? string.Empty : fallbackDisabledReason
                        }
                    };
                }
            }
        }

        private void TriggeredSearch(string searchValue)
        {
            try
            {
                var visibleWindows = Application.Current.Windows.Cast<Window>().Where(window => window.IsVisible && (window is SearchPageView) == false).ToList();
                visibleWindows.ForEach(window => window.Visibility = Visibility.Hidden);
                SearchBarUc.SetSearchText(searchValue);
                Show();
                Activate();
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

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

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

                        if (!suppressUi && !_ticketOverviewAutoContinueActive)
                        {
                            Show();
                            Activate();
                        }
                        if (result.AutoContinue && result.Results?.Count == 1)
                        {
                            ResultMenu.IndexOfSelectedResultItem = 0;
                            ResultMenu.SelectCurrentResultItem();
                        }
                    }
                    catch (Exception E)
                    {
                        LogException(E);
                    }
                });
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        private void ActivateManualSearch()
        {
            CancledSearchAction();
            SearchBarUc.ActivateManualSearch();
            Show();
            Activate();

            if (cSearchManager.Instance.HistoryList.Count > 0)
            {
                SearchHistory.ShowSearchHistory();
                SetSearchHistoryVisibility(true);
            }
            else
                SetSearchHistoryVisibility(false);
        }

        public void PhoneCallSearch(cPhoneSearchParameters searchInfo)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                var _h = Task.Run(async () =>
                {

                    try
                    {
                        var _result = await cFasdCockpitCommunicationBase.Instance.GetPhoneSearchResults(searchInfo);
                        if (_result != null && _result.Count > 0 || cFasdCockpitConfig.Instance?.PhoneSupport?.ShowUnresolvedPhoneNumbers is true)
                        {
                            string strTxt;
                            if (string.IsNullOrEmpty(searchInfo.name))
                                strTxt = searchInfo.phone;
                            else
                                strTxt = $"{searchInfo.name} ({searchInfo.phone})";
                            var strInfo = string.Format(cMultiLanguageSupport.GetItem("Searchbar.Phonecall.Info"), strTxt);
                            ShowExternalSearchInfo(strInfo, _result, enumF4sdSearchResultClass.Phone);
                        }
                    }
                    catch (Exception E)
                    {
                        LogException(E);
                    }
                });
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

        }

        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 UserSidSearch(cUserSidSearchParameters searchInfo)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                _ = Task.Run(async () =>
                {
                    try
                    {
                        var lstSids = searchInfo.sids.Split(',').Select((v) => v.Trim()).ToList();
                        var searchResult = await cFasdCockpitCommunicationBase.Instance.GetUserSearchResults(searchInfo.name, lstSids);
                        var strInfo = string.Format(cMultiLanguageSupport.GetItem("Searchbar.UserSearch.Info"), searchInfo.name);
                        ShowExternalSearchInfo(strInfo, searchResult, enumF4sdSearchResultClass.User);
                    }
                    catch (Exception E)
                    {
                        LogException(E);
                    }
                });
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

        }

        private void TicketSearch(cTicketSearchParameters searchInfo)
        {
            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                if (!Guid.TryParse(searchInfo.ticketId, out var _ticketId))
                    return;
                _ = RunTicketSearchAsync(searchInfo.ticketName, _ticketId, searchInfo.userName, searchInfo.sids);
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }

        }

        private void PipeServer_PipeMessage(string reply)
        {
            try
            {
                var type = reply;
                if (type.Contains(':'))
                    type = reply.Substring(0, Math.Max(reply.IndexOf(':'), 0));
                type = type.ToLowerInvariant();

                if (string.IsNullOrWhiteSpace(type))
                {
                    LogEntry("No valid pipe reply", LogLevels.Warning);
                    return;
                }

                if (type == "hello")
                    return;

                if (DefaultLogger.IsDebug)
                {
                    var _msg = new List<string>() { "pipe message received:", reply };
                    DefaultLogger.LogList(LogLevels.Debug, _msg);
                }

                if (cConnectionStatusHelper.Instance?.ApiConnectionStatus is cConnectionStatusHelper.enumOnlineStatus.offline)
                    return;

                var value = reply.Remove(0, type.Length).TrimStart(':');

                switch (type)
                {
                    case "search":
                        try
                        {
                            Dispatcher.Invoke(() => TriggeredSearch(value));
                        }
                        catch (Exception E)
                        {
                            LogException(E);
                        }
                        break;
                    case "phonesearch":
                        try
                        {
                            var searchInfo = JsonConvert.DeserializeObject<cPhoneSearchParameters>(value);
                            PhoneCallSearch(searchInfo);
                        }
                        catch (Exception E)
                        {
                            LogException(E);
                        }
                        break;
                    case "computerdomainsearch":
                        try
                        {
                            var searchInfo = JsonConvert.DeserializeObject<cComputerDomainSearchParameters>(value);
                            ComputerDomainSearch(searchInfo);
                        }
                        catch (Exception E)
                        {
                            LogException(E);
                        }
                        break;
                    case "usersidsearch":
                        try
                        {
                            var searchInfo = JsonConvert.DeserializeObject<cUserSidSearchParameters>(value);
                            UserSidSearch(searchInfo);
                        }
                        catch (Exception E)
                        {
                            LogException(E);
                        }
                        break;
                    case "ticketsearch":
                        try
                        {
                            var searchInfo = JsonConvert.DeserializeObject<cTicketSearchParameters>(value);
                            TicketSearch(searchInfo);
                        }
                        catch (Exception E)
                        {
                            LogException(E);
                        }
                        break;
                    default:
                        break;
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);

            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {
                _pipeServer = new cF4sdPipeServer();
                _pipeServer.PipeMessage += PipeServer_PipeMessage;
                _pipeServer.Listen(App.PipeName, MaxSize: 4096, LowerIntegrity: true);
                LogEntry($"Start listening on named pipe '{App.PipeName}'...", LogLevels.Debug);

                IntPtr handle = new WindowInteropHelper(this).Handle;
                var hwndSource = HwndSource.FromHwnd(handle);
                hwndSource.AddHook(SearchViewWindowProc);
                hwndSource.AddHook(new HwndSourceHook(cUtility.WindowProc));

                var hotKeyDefinitions = new List<cHotKeyManager.cHotKeyDefinition>
                {
                    new cHotKeyManager.cHotKeyDefinition { Id = 1, Modifier = new List<ModifierKeys> { ModifierKeys.Control }, Key = Key.F3, HotKeyAction = HotKeyManager_ActivateSearch },
                    new cHotKeyManager.cHotKeyDefinition { Id = 2, Modifier = new List<ModifierKeys> { ModifierKeys.Control, ModifierKeys.Alt }, Key = Key.F3, HotKeyAction = HotKeyManager_CopyAndSearch }
                };

#if isDemo
                if (cFasdCockpitCommunicationBase.Instance?.IsDemo() == true)
                {
                    hotKeyDefinitions.Add(new cHotKeyManager.cHotKeyDefinition
                    {
                        Id = 3,
                        Modifier = new List<ModifierKeys> { ModifierKeys.Control, ModifierKeys.Alt },
                        Key = Key.T,
                        HotKeyAction = () => TicketOverviewUpdateService.Instance.SimulateDemoTicket()
                    });
                }
#endif

                _hotKeyManager = new cHotKeyManager(handle, hotKeyDefinitions);
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public void ActivateSearchView()
        {
            if (_isActivating)
                return;

            _isActivating = true;

            var CM = MethodBase.GetCurrentMethod();
            LogMethodBegin(CM);
            try
            {


                bool doShow = true;

                if (doShow)
                {
                    ActivateManualSearch();
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                _isActivating = false;
                LogMethodEnd(CM);
            }
        }

        private void HotKeyManager_ActivateSearch()
        {
            if (!IsVisible)
                ActivateSearchView();
        }

        public void BringToFrontPreserveState()
        {
            Dispatcher.Invoke(() =>
            {
                if (!IsVisible)
                    Show();

                if (WindowState == WindowState.Minimized)
                    WindowState = WindowState.Normal;

                Activate();
                Topmost = true;
                Topmost = false;
                Focus();
            });
        }

        private async void HotKeyManager_CopyAndSearch()
        {
            try
            {
                bool isHotKeyDown = true;
                while (isHotKeyDown)
                {
                    isHotKeyDown = _hotKeyManager.IsKeyDown(cHotKeyManager.VirtualKeyStates.VK_F3) || _hotKeyManager.IsKeyDown(cHotKeyManager.VirtualKeyStates.VK_CONTROL) || _hotKeyManager.IsKeyDown(cHotKeyManager.VirtualKeyStates.VK_MENU);
                    await Task.Delay(50);
                }

                cUtility.SendCtrlCToCurrentWindow();

                await Task.Delay(150);

                var copiedText = Clipboard.GetText();
                copiedText = cUtility.CleanPhoneString(copiedText);
                copiedText = copiedText.Trim().Substring(0, Math.Min(copiedText.Trim().Length, 75));

                ActivateSearchView();

                if (copiedText != null)
                {
                    await Dispatcher.BeginInvoke((Action)delegate
                    {
                        Activate();
                        SearchBarUc.SetSearchText(copiedText);
                    }, DispatcherPriority.Render);
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }

        }

        protected override void OnClosed(EventArgs e)
        {
            try
            {
                if (TicketOverviewUpdateService.Instance != null)
                    TicketOverviewUpdateService.Instance.OverviewCountsChanged -= TicketOverviewUpdateService_OverviewCountsChanged;
                _pipeServer?.Dispose();
            }
            catch { }
            _pipeServer = null;

            _hotKeyManager?.UnRegisterHotKeys();
            RemoveHandler(TicketOverview.SelectionRequestedEvent,
                new TicketOverviewSelectionRequestedEventHandler(TicketOverview_SelectionRequested));
            base.OnClosed(e);
        }

        private void AddCustomEventHandlers()
        {
            AddHandler(cUiActionBase.UiActionClickedEvent, new cUiActionBase.UiActionEventHandlerDelegate(UiActionWasTriggered));
            SearchBarUc.CancledSearchAction = CancledSearchAction;
            AddHandler(TicketOverview.SelectionRequestedEvent,
    new TicketOverviewSelectionRequestedEventHandler(TicketOverview_SelectionRequested), true);
            cFasdCockpitConfig.Instance.UiSettingsChanged += UiSettingsChanged;

            SearchBarUc.ChangedSearchValue = filteredResults =>
            {
                UpdateSearchResults(filteredResults);
                return Task.CompletedTask;
            };

            SearchBarUc.SearchValueChanged += HandleSearchValueChanged;
            SearchService.RelationsFound += HandleRelationsFound;
        }

        private async void HandleSearchValueChanged(object sender, string searchValue)
        {
            const int minSearchLength = 2;

            try
            {
                _searchCancellationTokenSource?.Cancel();
                _searchCancellationTokenSource = new CancellationTokenSource();

                if (string.IsNullOrWhiteSpace(searchValue))
                {
                    SearchBarUc.SetSpinnerVisibility(Visibility.Hidden);
                    UpdateSearchResults(new cFilteredResults());
                    return;
                }

                if (searchValue.Length < minSearchLength)
                    return;

                SearchBarUc.SetSpinnerVisibility(Visibility.Visible);
                cFasdApiSearchResultCollection searchResults = await SupportCaseSearchService.GetSearchResultsAsync(searchValue, _searchCancellationTokenSource.Token);
                SearchBarUc.SetSpinnerVisibility(Visibility.Hidden);
                UpdateSearchResults(new cFilteredResults(searchResults));
            }
            catch (Exception ex)
            {
                LogException(ex);
            }
        }

        private void HandleRelationsFound(object sender, StagedSearchResultRelationsEventArgs e)
        {
            try
            {
                Dispatcher.Invoke(() =>
                {
                    var first = e.RelatedTo.FirstOrDefault();
                    var relationSearchResult = new cSearchHistorySearchResultEntry(first.DisplayName, first.DisplayName, e.RelatedTo.ToList(), e.StagedResultRelations.Relations.ToList(), this);
                    ShowSearchRelations(relationSearchResult, e.RelationService, this);

                    UpdatePendingInformationClasses(e.StagedResultRelations.PendingInformationClasses);
                });
            }
            catch (Exception ex)
            {
                LogException(ex);
            }
        }

        #region Ticketübersicht

        private bool CheckTicketOverviewAvailability()
        {
            if (cFasdCockpitCommunicationBase.Instance?.IsDemo() == true)
                return true;

            var isSearchAvailable = cConnectionStatusHelper.Instance?.ApiConnectionStatus == cConnectionStatusHelper.enumOnlineStatus.online;

            return isSearchAvailable
                && cFasdCockpitConfig.Instance?.Global?.TicketConfiguration?.ShowOverview == true
                && IsTicketIntegrationActive()
                && cFasdCockpitConfig.Instance.HasM42Configuration();
        }

        private bool IsTicketIntegrationActive()
        {
            try
            {
                var healthCards = cF4SDCockpitXmlConfig.Instance?.HealthCardConfig?.HealthCards?.Values;
                if (healthCards == null)
                    return false;

                return healthCards.Any(card =>
                    card?.InformationClasses?.Contains(enumFasdInformationClass.Ticket) == true);
            }
            catch (Exception E)
            {
                LogException(E);
                return false;
            }
        }

        private void UpdateTicketOverviewAvailability()
        {
            var enabled = CheckTicketOverviewAvailability();
            var service = TicketOverviewUpdateService.Instance;
            service?.UpdateAvailability(enabled);
            if (enabled)
                _ = service?.FetchAsync();

            if (!enabled)
            {
                if (Dispatcher.CheckAccess())
                {
                    ApplyTicketOverviewDisabledState();
                }
                else
                {
                    Dispatcher.Invoke(ApplyTicketOverviewDisabledState);
                }
            }
        }

        private void ApplyTicketOverviewDisabledState()
        {
            _renderTicketOverviewUserNames = false;
            _ticketOverviewNotificationScopesPrimed.Clear();
            _ticketOverviewFirstEventHandled = false;
            _ticketOverviewInitWasEmptyScopes.Clear();
            SetTicketOverviewVisibility(false);
            (Application.Current as App)?.ClearTicketOverviewTrayNotification();
        }

        private void SetTicketOverviewVisibility(bool isVisible)
        {
            if (!CheckTicketOverviewAvailability())
                isVisible = false;

            BodyStack_TicketOverview.Visibility = isVisible ? Visibility.Visible : Visibility.Collapsed;
            TicketOverviewBorder.Visibility = isVisible ? Visibility.Visible : Visibility.Collapsed;
            FilterCheckbox.Visibility = isVisible ? Visibility.Visible : Visibility.Collapsed;
            RoleLabel.Visibility = isVisible ? Visibility.Visible : Visibility.Collapsed;
            OwnTicketsLabel.Visibility = isVisible ? Visibility.Visible : Visibility.Collapsed;
            TicketOverviewLabel.Visibility = isVisible ? Visibility.Visible : Visibility.Collapsed;
            ScheduleSearchResultMaxHeightUpdate();
        }

        public void ShowTicketOverviewPane()
        {
            Dispatcher.Invoke(() =>
            {
                bool overviewAlreadyVisible = TicketOverviewBorder.Visibility == Visibility.Visible;

                SetTicketOverviewVisibility(true);

                if (!overviewAlreadyVisible)
                {
                    SetSearchResultVisibility(false);
                    SetSearchHistoryVisibility(false);
                    TicketOverviewUc?.ResetSelection();
                }
                TicketOverviewUc?.RefreshHighlightState(IsFilterChecked);

                var app = Application.Current as FasdDesktopUi.App;
                app?.ClearTicketOverviewTrayNotification();
            });
        }

        public void ShowTicketOverviewPaneForScope(TileScope? scope)
        {
            if (scope.HasValue)
            {
                Dispatcher.Invoke(() =>
                {
                    var useRoleScope = scope.Value == TileScope.Role;
                    if (FilterCheckbox != null && FilterCheckbox.IsChecked != useRoleScope)
                        FilterCheckbox.IsChecked = useRoleScope;
                });
            }

            ShowTicketOverviewPane();
        }

        internal void CloseTicketOverviewResults()
        {
            _renderTicketOverviewUserNames = false;
            ResultMenu.ShowSearchResults(new cFilteredResults(), null, this);
            ResultMenu.SetHeaderText(string.Empty);
            SetSearchResultVisibility(false);
            SetTicketOverviewVisibility(true);
        }

        private void TicketOverviewUpdateService_OverviewCountsChanged(object sender, TicketOverviewCountsChangedEventArgs e)
        {
            try
            {
                ApplyLatestCounts();
                var positiveChanges = e.Changes?.Where(change => change.Delta > 0).ToList();
                var app = Application.Current as FasdDesktopUi.App;
                var service = TicketOverviewUpdateService.Instance;

                if (!_ticketOverviewFirstEventHandled)
                {
                    _ticketOverviewFirstEventHandled = true;
                    foreach (var scope in GetTicketOverviewEventScopes(e))
                    {
                        PrimeTicketOverviewScope(scope);
                        TrackEmptyInitScope(scope, e?.CurrentCounts);
                    }
                    app?.ClearTicketOverviewTrayNotification();
                    return;
                }

                if (e.InitializedScope.HasValue)
                {
                    PrimeTicketOverviewScope(e.InitializedScope.Value);
                    TrackEmptyInitScope(e.InitializedScope.Value, e?.CurrentCounts);
                    app?.ClearTicketOverviewTrayNotification();
                    return;
                }

                if (positiveChanges == null || positiveChanges.Count == 0)
                {
                    app?.ClearTicketOverviewTrayNotification();
                    return;
                }

                if (service != null && !service.AreAllScopesInitialized)
                {
                    app?.ClearTicketOverviewTrayNotification();
                    return;
                }

                var filteredChanges = FilterChangesForPrimedScopes(positiveChanges);
                if (filteredChanges.Count == 0)
                {
                    app?.ClearTicketOverviewTrayNotification();
                    return;
                }

                TicketOverviewUc?.SetHighlights(filteredChanges, IsFilterChecked);

                var pendingScope = ResolveSingleScope(filteredChanges);
                app?.SetTicketOverviewNotificationScope(pendingScope);

                var message = BuildNotificationMessage(filteredChanges);
                if (string.IsNullOrWhiteSpace(message))
                {
                    app?.ClearTicketOverviewTrayNotification();
                    return;
                }

                app?.ShowTicketOverviewTrayNotification(message);
                ShowTicketOverviewNotification(message);
            }
            catch (Exception ex)
            {
                LogException(ex);
            }
        }

        private Task TicketSearchFromOverviewRelationAsync(cF4sdApiSearchResultRelation relation)
        {
            if (relation == null || relation.Type != enumF4sdSearchResultClass.Ticket)
                return Task.CompletedTask;

            if (TryOpenTicketOverviewRelationExternally(relation))
                return Task.CompletedTask;

            var ticketName = string.IsNullOrWhiteSpace(relation.DisplayName) ? relation.Name : relation.DisplayName;
            var ticketId = relation.id;
            if (ticketId == Guid.Empty)
                return Task.CompletedTask;

            string userName = null;
            string sids = null;

            if (relation.Infos != null)
            {
                if (!relation.Infos.TryGetValue("UserDisplayName", out userName))
                    relation.Infos.TryGetValue("UserAccount", out userName);

                if (!relation.Infos.TryGetValue("Sids", out sids))
                    relation.Infos.TryGetValue("UserSid", out sids);
            }

            return RunTicketSearchAsync(ticketName, ticketId, userName, sids, suppressUi: true);
        }

        private bool TryOpenTicketOverviewRelationExternally(cF4sdApiSearchResultRelation relation)
        {
            return TicketDeepLinkHelper.TryOpenTicketRelationExternally(relation);
        }

        private Task RunTicketSearchAsync(string ticketName, Guid ticketId, string userName, string sids, bool suppressUi = false)
        {
            if (ticketId == Guid.Empty)
                return Task.CompletedTask;

            if (suppressUi)
                BeginTicketOverviewAutoContinue(TimeSpan.FromSeconds(6));

            return Task.Run(async () =>
            {
                try
                {
                    var lstSids = sids?.Split(',').Select((v) => v.Trim()).ToList();
                    var _result = await cFasdCockpitCommunicationBase.Instance.GetUserSearchResults(userName, lstSids);

                    if (_result is null || _result.Count == 0 || _result.First().Value.Count == 0)
                    {
                        LogEntry($"No corresponding user could be found for ticket '{ticketName}'", LogLevels.Warning);
                        if (suppressUi)
                            EndTicketOverviewAutoContinue(showSearch: true);
                        return;
                    }

                    var userId = _result.Values.First().First().id;
                    if (userId == Guid.Empty)
                    {
                        LogEntry($"No valid user id could be found for ticket '{ticketName}'", LogLevels.Warning);
                        if (suppressUi)
                            EndTicketOverviewAutoContinue(showSearch: true);
                        return;
                    }
                    var _ticketRelation = new cF4sdApiSearchResultRelation()
                    {
                        Type = enumF4sdSearchResultClass.Ticket,
                        Name = ticketName,
                        DisplayName = ticketName,
                        id = ticketId,
                        Status = enumF4sdSearchResultStatus.Active,
                        Identities = new cF4sdIdentityList
                        {
                            new cF4sdIdentityEntry()
                            {
                                Class = enumFasdInformationClass.User,
                                Id = userId
                            },
                            new cF4sdIdentityEntry()
                            {
                                Class = enumFasdInformationClass.Ticket,
                                Id = ticketId
                            },
                        }
                    };

                    var filteredResults = new cFilteredResults(_result) { AutoContinue = true, PreSelectedRelation = _ticketRelation };

                    var strInfo = string.Format(cMultiLanguageSupport.GetItem("Searchbar.TicketSearch.Info"), ticketName);
                    await ShowExternalSearchInfoAsync(strInfo, filteredResults, enumF4sdSearchResultClass.Ticket, suppressUi);
                }
                catch (Exception E)
                {
                    LogException(E);
                    if (suppressUi)
                        EndTicketOverviewAutoContinue(showSearch: true);
                }
            });
        }

        private void BeginTicketOverviewAutoContinue(TimeSpan fallbackTimeout)
        {
            if (Dispatcher.CheckAccess())
                BeginTicketOverviewAutoContinueCore(fallbackTimeout);
            else
                Dispatcher.Invoke(() => BeginTicketOverviewAutoContinueCore(fallbackTimeout));
        }

        private void BeginTicketOverviewAutoContinueCore(TimeSpan fallbackTimeout)
        {
            if (_ticketOverviewAutoContinueActive)
                return;

            _ticketOverviewAutoContinueActive = true;

            SearchBarUc.IsHitTestVisible = false;
            ResultMenu.IsHitTestVisible = false;
            SearchHistory.IsHitTestVisible = false;
            BodyStack_TicketOverview.IsHitTestVisible = false;
            FilterCheckbox.IsHitTestVisible = false;

            SetSearchHistoryVisibility(false);
            SetSearchResultVisibility(false);

            _ticketOverviewAutoContinueCaseChangedHandler = (sender, args) => EndTicketOverviewAutoContinue(showSearch: false);
            cSupportCaseDataProvider.CaseChanged += _ticketOverviewAutoContinueCaseChangedHandler;

            _ticketOverviewAutoContinueFallbackTimer = new DispatcherTimer { Interval = fallbackTimeout };
            _ticketOverviewAutoContinueFallbackTimer.Tick += TicketOverviewAutoContinueFallbackTimer_Tick;
            _ticketOverviewAutoContinueFallbackTimer.Start();
        }

        private void TicketOverviewAutoContinueFallbackTimer_Tick(object sender, EventArgs e)
        {
            EndTicketOverviewAutoContinue(showSearch: true);
        }

        private void EndTicketOverviewAutoContinue(bool showSearch)
        {
            if (Dispatcher.CheckAccess())
                EndTicketOverviewAutoContinueCore(showSearch);
            else
                Dispatcher.Invoke(() => EndTicketOverviewAutoContinueCore(showSearch));
        }

        private void EndTicketOverviewAutoContinueCore(bool showSearch)
        {
            if (!_ticketOverviewAutoContinueActive)
                return;

            _ticketOverviewAutoContinueActive = false;

            if (_ticketOverviewAutoContinueFallbackTimer != null)
            {
                _ticketOverviewAutoContinueFallbackTimer.Stop();
                _ticketOverviewAutoContinueFallbackTimer.Tick -= TicketOverviewAutoContinueFallbackTimer_Tick;
                _ticketOverviewAutoContinueFallbackTimer = null;
            }

            if (_ticketOverviewAutoContinueCaseChangedHandler != null)
            {
                cSupportCaseDataProvider.CaseChanged -= _ticketOverviewAutoContinueCaseChangedHandler;
                _ticketOverviewAutoContinueCaseChangedHandler = null;
            }

            SearchBarUc.IsHitTestVisible = true;
            ResultMenu.IsHitTestVisible = true;
            SearchHistory.IsHitTestVisible = true;
            BodyStack_TicketOverview.IsHitTestVisible = true;
            FilterCheckbox.IsHitTestVisible = true;

            if (showSearch)
            {
                SetSearchResultVisibility(true);
                Show();
                Activate();
            }
        }

        private void PrimeTicketOverviewScope(TileScope scope)
        {
            if (_ticketOverviewNotificationScopesPrimed.Add(scope))
            {
                TicketOverviewUc?.ClearHighlightsForScope(scope);
            }
        }

        private IReadOnlyList<TileCountChange> FilterChangesForPrimedScopes(IReadOnlyList<TileCountChange> changes)
        {
            if (changes == null || changes.Count == 0)
                return Array.Empty<TileCountChange>();

            var filteredChanges = new List<TileCountChange>(changes.Count);
            var unprimedScopes = new HashSet<TileScope>();
            var silentInitScopes = new HashSet<TileScope>();

            foreach (var change in changes)
            {
                if (_ticketOverviewInitWasEmptyScopes.Contains(change.Scope))
                {
                    silentInitScopes.Add(change.Scope);
                    continue;
                }

                if (_ticketOverviewNotificationScopesPrimed.Contains(change.Scope))
                {
                    filteredChanges.Add(change);
                }
                else
                {
                    unprimedScopes.Add(change.Scope);
                }
            }

            foreach (var scope in unprimedScopes)
            {
                PrimeTicketOverviewScope(scope);
            }

            if (silentInitScopes.Count > 0)
            {
                foreach (var scope in silentInitScopes)
                {
                    _ticketOverviewInitWasEmptyScopes.Remove(scope);
                    PrimeTicketOverviewScope(scope);
                }
            }

            return filteredChanges;
        }

        private void TrackEmptyInitScope(TileScope scope, IReadOnlyDictionary<string, TileCounts> counts)
        {
            if (IsScopeCountsEmpty(scope, counts))
            {
                _ticketOverviewInitWasEmptyScopes.Add(scope);
            }
        }

        private static bool IsScopeCountsEmpty(TileScope scope, IReadOnlyDictionary<string, TileCounts> counts)
        {
            if (counts == null || counts.Count == 0)
                return true;

            foreach (var kvp in counts)
            {
                var value = scope == TileScope.Role ? kvp.Value.Role : kvp.Value.Personal;
                if (value != 0)
                    return false;
            }

            return true;
        }

        private static IEnumerable<TileScope> GetTicketOverviewEventScopes(TicketOverviewCountsChangedEventArgs e)
        {
            if (e == null)
                return Enumerable.Empty<TileScope>();

            if (e.InitializedScope.HasValue)
                return new[] { e.InitializedScope.Value };

            if (e.Changes == null || e.Changes.Count == 0)
                return Enumerable.Empty<TileScope>();

            return e.Changes.Select(change => change.Scope).Distinct();
        }

        private static TileScope? ResolveSingleScope(IReadOnlyList<TileCountChange> changes)
        {
            if (changes == null || changes.Count == 0)
                return null;

            var scope = changes[0].Scope;
            for (int i = 1; i < changes.Count; i++)
            {
                if (changes[i].Scope != scope)
                    return null;
            }

            return scope;
        }

        private void ApplyLatestCounts()
        {
            var service = TicketOverviewUpdateService.Instance;
            if (service == null)
                return;
            var counts = service.GetCountsForScope(IsFilterChecked);
            if (counts == null || counts.Count == 0)
                return;

            TicketOverviewUc?.UpdateCounts(counts, IsFilterChecked);
            TicketOverviewUc?.RefreshHighlightState(IsFilterChecked);
        }

        private string BuildNotificationMessage(IReadOnlyList<TileCountChange> changes)
        {
            if (changes == null || changes.Count == 0)
                return string.Empty;

            var aggregates = new List<(string RowKey, string ColumnKey, TileScope Scope, int Delta)>();
            var indexMap = new Dictionary<(string RowKey, string ColumnKey, TileScope Scope), int>();

            foreach (var change in changes)
            {
                if (!TryResolveTicketOverviewLabels(change.Key, out var rowKey, out var columnKey))
                    continue;

                var delta = change.NewCount - change.OldCount;
                if (delta == 0)
                    continue;

                var aggregateKey = (rowKey, columnKey, change.Scope);
                if (indexMap.TryGetValue(aggregateKey, out var aggregateIndex))
                {
                    var existing = aggregates[aggregateIndex];
                    aggregates[aggregateIndex] = (existing.RowKey, existing.ColumnKey, existing.Scope, existing.Delta + delta);
                }
                else
                {
                    indexMap[aggregateKey] = aggregates.Count;
                    aggregates.Add((rowKey, columnKey, change.Scope, delta));
                }
            }

            if (aggregates.Count == 0)
                return string.Empty;

            var pieces = new List<string>(aggregates.Count);
            foreach (var aggregate in aggregates)
            {
                if (aggregate.Delta == 0)
                    continue;

                var row = cMultiLanguageSupport.GetItem(aggregate.RowKey) ?? aggregate.RowKey;
                var column = cMultiLanguageSupport.GetItem(aggregate.ColumnKey) ?? aggregate.ColumnKey;
                var scopeRowLabel = GetScopeRowLabel(aggregate.Scope, aggregate.RowKey, row);
                var deltaText = aggregate.Delta >= 0 ? $"+{aggregate.Delta}" : aggregate.Delta.ToString();
                pieces.Add($"{scopeRowLabel} – {column} ({deltaText})");
            }

            return string.Join(Environment.NewLine, pieces);
        }

        private static bool TryResolveTicketOverviewLabels(string key, out string rowKey, out string columnKey)
        {
            rowKey = null;
            columnKey = null;

            if (string.IsNullOrWhiteSpace(key))
                return false;

            var normalized = key.ToLowerInvariant();

            if (normalized.StartsWith("tickets"))
                rowKey = "TicketOverview.Row.Heading.Tickets";
            else if (normalized.StartsWith("incident"))
                rowKey = "TicketOverview.Row.Heading.Incidents";
            else if (normalized.StartsWith("unassigned"))
                rowKey = "TicketOverview.Row.Heading.UnassignedTickets";
            else
                return false;

            columnKey = ResolveColumnTranslationKey(normalized);
            return columnKey != null;
        }

        private static string ResolveColumnTranslationKey(string normalizedKey)
        {
            if (normalizedKey.EndsWith("newinfo"))
                return "TicketOverview.Column.Heading.NewInfo";
            if (normalizedKey.EndsWith("critical"))
                return "TicketOverview.Column.Heading.Critical";
            if (normalizedKey.EndsWith("active"))
                return "TicketOverview.Column.Heading.Active";
            return "TicketOverview.Column.Heading.New";
        }

        private static string GetScopeRowLabel(TileScope scope, string rowKey, string rowLabel)
        {
            string suffix = null;
            if (string.Equals(rowKey, "TicketOverview.Row.Heading.Tickets", StringComparison.OrdinalIgnoreCase))
                suffix = "Tickets";
            else if (string.Equals(rowKey, "TicketOverview.Row.Heading.Incidents", StringComparison.OrdinalIgnoreCase))
                suffix = "Incidents";
            else if (string.Equals(rowKey, "TicketOverview.Row.Heading.UnassignedTickets", StringComparison.OrdinalIgnoreCase))
                suffix = "UnassignedTickets";

            var translationKey = scope == TileScope.Role
                ? $"TicketOverview.ScopeRow.Role.{suffix}"
                : $"TicketOverview.ScopeRow.Personal.{suffix}";

            return cMultiLanguageSupport.GetItem(translationKey) ?? rowLabel;
        }

        private void ShowTicketOverviewNotification(string message)
        {
            if (string.IsNullOrWhiteSpace(message))
                return;

            try
            {
                var notificationTitle = cMultiLanguageSupport.GetItem("TicketOverview.Notification.Title") ?? "Ticketübersicht";
                var app = Application.Current as App;
                app?.notifyIcon?.ShowBalloonTip(3000, notificationTitle, message, System.Windows.Forms.ToolTipIcon.Info);
            }
            catch (Exception ex)
            {
                LogException(ex);
            }
        }
        private async void TicketOverview_SelectionRequested(object sender, TicketOverviewSelectionRequestedEventArgs e)
        {
            MethodBase CM = null;
            if (cLogManager.DefaultLogger.IsDebug)
            {
                CM = MethodBase.GetCurrentMethod();
                LogMethodBegin(CM);
            }

            try
            {
                var app = Application.Current as FasdDesktopUi.App;
                app?.ClearTicketOverviewTrayNotification();

                _renderTicketOverviewUserNames = true;
                SetSearchHistoryVisibility(false);
                SetTicketOverviewVisibility(true);
                SetSearchResultVisibility(true);
                var header = BuildHeaderText(e.Key, e.UseRoleScope, e.Count);
                ResultMenu.SetHeaderText(header, hideDetailsCheckbox: true);

                ShowLoadingTextItem(cMultiLanguageSupport.GetItem("Searchbar.Loading.CaseData"));

                SetPendingInformationClasses(new HashSet<enumFasdInformationClass> { enumFasdInformationClass.Ticket });

                var relations = await LoadRelationsForTileAsync(e.Key, e.UseRoleScope, Math.Max(0, e.Count));
                var firstRelation = relations.FirstOrDefault();
                string displayText = header;
                if (firstRelation != null)
                {
                    string firstSummary = null;
                    if (firstRelation.Infos != null && firstRelation.Infos.TryGetValue("Summary", out var summaryValue))
                    {
                        firstSummary = summaryValue;
                    }

                    displayText = string.IsNullOrWhiteSpace(firstSummary)
                        ? $"{header} → {firstRelation.DisplayName}"
                        : $"{header} → {firstRelation.DisplayName} – {firstSummary}";
                }

                var entry = new cSearchHistorySearchResultEntry(
                    displayText,
                    header,
                    new List<cFasdApiSearchResultEntry>(),
                    relations,
                    this
                )
                { isSeen = true };
                _ticketOverviewHistoryEntries.Add(entry);

                var lookup = relations.ToLookup(
                    r => cF4sdIdentityEntry.GetFromSearchResult(r.Type),
                    r =>
                    {
                        var required = new List<enumFasdInformationClass> { cF4sdIdentityEntry.GetFromSearchResult(r.Type) };
                        bool isEnabled = cHealthCardDataHelper.HasAvailableHealthCard(required);
                        string disabledReason = null;
                        if (!isEnabled)
                        {
                            disabledReason = cMultiLanguageSupport.GetItem("Searchbar.NoValidHealthcard") ?? string.Empty;
                        }
                        string trailingUser = null;
                        if (_renderTicketOverviewUserNames && r.Infos != null && r.Infos.TryGetValue("UserDisplayName", out var userDisplayName))
                        {
                            trailingUser = userDisplayName;
                        }
                        return (cMenuDataBase)new cMenuDataSearchRelation(r)
                        {
                            MenuText = r.DisplayName,
                            TrailingText = trailingUser,
                            UiAction = new cUiProcessTicketOverviewRelationAction(r.DisplayName, () => TicketSearchFromOverviewRelationAsync(r))
                            {
                                DisplayType = isEnabled ? enumActionDisplayType.enabled : enumActionDisplayType.disabled,
                                Description = isEnabled ? string.Empty : disabledReason,
                                AlternativeDescription = isEnabled ? string.Empty : disabledReason
                            }
                        };
                    }
                );
                ResultMenu.UpdateSearchRelations(lookup);
                ResultMenu.SetHeaderText(header, hideDetailsCheckbox: true);

                UpdatePendingInformationClasses(new HashSet<enumFasdInformationClass>());
            }
            catch (Exception ex)
            {
                LogException(ex);
                CancledSearchAction();
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }
        private string BuildHeaderText(string key, bool useRoleScope, int? count = null)
        {
            string scopeKey = useRoleScope ? "Searchbar.Header.Scope.Role" : "Searchbar.Header.Scope.Personal";
            string entityKey = DetermineFallbackEntityKey(key);
            string filterKey = DetermineFallbackFilterKey(key);

            string scope = cMultiLanguageSupport.GetItem(scopeKey) ?? scopeKey;
            string entity = cMultiLanguageSupport.GetItem(entityKey) ?? entityKey;
            string filter = cMultiLanguageSupport.GetItem(filterKey) ?? filterKey;
            string template = cMultiLanguageSupport.GetItem("Searchbar.Header.Template") ?? "{0} - {1} ({2})";

            bool expectsCount = template.IndexOf("{3}", StringComparison.Ordinal) >= 0;
            try
            {
                if (expectsCount)
                {
                    int countValue = count ?? 0;
                    return string.Format(template, entity, filter, scope, countValue);
                }

                return string.Format(template, entity, filter, scope);
            }
            catch (FormatException)
            {
                return string.Format("{0} - {1} ({2})", entity, filter, scope);
            }
        }

        private string DetermineFallbackEntityKey(string key)
        {
            if (string.IsNullOrWhiteSpace(key))
                return "Searchbar.Header.Entity.Tickets";

            var keyLower = key.ToLowerInvariant();
            return keyLower.Contains("incident")
                ? "Searchbar.Header.Entity.Incidents"
                : "Searchbar.Header.Entity.Tickets";
        }

        private string DetermineFallbackFilterKey(string key)
        {
            if (string.IsNullOrWhiteSpace(key))
                return "Searchbar.Header.Filter.Overview";

            var keyLower = key.ToLowerInvariant();

            if (keyLower.Contains("newinfo"))
                return "Searchbar.Header.Filter.NewInfo";
            if (keyLower.Contains("new"))
                return "Searchbar.Header.Filter.New";
            if (keyLower.Contains("active"))
                return "Searchbar.Header.Filter.Active";
            if (keyLower.Contains("critical"))
                return "Searchbar.Header.Filter.Critical";
            if (keyLower.Contains("unassigned"))
                return "Searchbar.Header.Filter.Unassigned";
            if (keyLower.Contains("hold") || keyLower.Contains("onhold") || keyLower.Contains("wait"))
                return "Searchbar.Header.Filter.OnHold";
            if (keyLower.Contains("closed"))
                return "Searchbar.Header.Filter.Closed";

            return "Searchbar.Header.Filter.Overview";
        }

        private async Task<List<cF4sdApiSearchResultRelation>> LoadRelationsForTileAsync(string key, bool useRoleScope, int count)
        {
            var communication = cFasdCockpitCommunicationBase.Instance;
            if (communication == null)
                return new List<cF4sdApiSearchResultRelation>();
            try
            {
                var relations = await communication.GetTicketOverviewRelations(key, useRoleScope, Math.Max(0, count)).ConfigureAwait(false);
                var list = relations?.ToList() ?? new List<cF4sdApiSearchResultRelation>();

                if (TicketOverviewUpdateService.Instance != null)
                    foreach (var demoRelation in TicketOverviewUpdateService.Instance.GetDemoRelations(key, useRoleScope))
                    {
                        if (!list.Any(r => r.id == demoRelation.id))
                            list.Add(demoRelation);
                    }

                return list;
            }
            catch (Exception ex)
            {
                LogException(ex);
                return new List<cF4sdApiSearchResultRelation>();
            }
        }

        #endregion

        #region Click-Through fuer transparente SearchView-Bereiche

        private const int WM_NCHITTEST = 0x0084;
        private const int HTTRANSPARENT = -1;

        private IntPtr SearchViewWindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == WM_NCHITTEST)
            {
                if (!IsPointWithinInteractiveBounds(lParam))
                {
                    handled = true;
                    return new IntPtr(HTTRANSPARENT);
                }
            }

            return IntPtr.Zero;
        }

        private bool IsPointWithinInteractiveBounds(IntPtr lParam)
        {
            if (!IsLoaded || MainBorder == null || !MainBorder.IsLoaded)
                return true;

            if (MainBorder.ActualWidth <= 0 || MainBorder.ActualHeight <= 0)
                return true;

            var screenPoint = ExtractScreenPoint(lParam);
            var windowPoint = PointFromScreen(screenPoint);

            if (!(InputHitTest(windowPoint) is DependencyObject hitElement))
                return false;

            return IsDescendantOf(hitElement, MainBorder);
        }

        private static Point ExtractScreenPoint(IntPtr lParam)
        {
            int value = unchecked((int)(long)lParam);
            short x = (short)(value & 0xFFFF);
            short y = (short)((value >> 16) & 0xFFFF);
            return new Point(x, y);
        }

        private static bool IsDescendantOf(DependencyObject element, DependencyObject ancestor)
        {
            while (element != null)
            {
                if (element == ancestor)
                    return true;

                element = GetParent(element);
            }

            return false;
        }

        private static DependencyObject GetParent(DependencyObject current)
        {
            if (current == null)
                return null;

            if (current is Visual || current is System.Windows.Media.Media3D.Visual3D)
                return VisualTreeHelper.GetParent(current);

            if (current is System.Windows.ContentElement contentElement)
                return System.Windows.ContentOperations.GetParent(contentElement);

            return LogicalTreeHelper.GetParent(current);
        }

        #endregion

        private void UpdateSearchResults(cFilteredResults filteredResults)
        {
            this.Dispatcher.Invoke(new Action(() =>
            {
                try
                {
                    _renderTicketOverviewUserNames = false;

                    if (filteredResults?.Results is null)
                    {
                        SetSearchResultVisibility(false);
                        SetTicketOverviewVisibility(true);
                        return;
                    }

                    string menuHeaderText = filteredResults?.PreSelectedRelation?.DisplayName;

                    if (filteredResults?.PreSelectedRelation != null)
                    {
                        List<cFasdApiSearchResultEntry> selectedResult = filteredResults.Results.Values.FirstOrDefault();
                        var processSearchResult = new cUiProcessSearchResultAction(selectedResult.FirstOrDefault()?.DisplayName, this, selectedResult) { PreSelectedSearchRelation = filteredResults.PreSelectedRelation };
                        Dispatcher.Invoke(async () =>
                        {
                            bool isSearchOngoing = await processSearchResult.RunUiActionAsync(this, this, false, null);
                            if (!isSearchOngoing)
                                Hide();
                        });
                        return;
                    }

                    ResultMenu.ShowSearchResults(filteredResults, menuHeaderText, this);
                    preSelectedRelation = filteredResults.PreSelectedRelation;

                    SetSearchResultVisibility(true);
                    SetTicketOverviewVisibility(false);
                }
                catch (Exception E)
                {
                    LogException(E);
                }
            }));
        }

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

        private void SlimPageWindowStateBar_ClickedClose(object sender, RoutedEventArgs e)
        {
            CancledSearchAction();
        }

        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 _:
                    case cUiProcessTicketOverviewRelationAction _:
                        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);
                var actionResult = await e.UiAction.RunUiActionAsync(sender, null, false, null);
                if (!(e.UiAction is cUiProcessTicketOverviewRelationAction) && !actionResult)
                    CancledSearchAction();
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                Mouse.OverrideCursor = null;
            }
        }

        private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Up:
                    if (ResultMenu.IndexOfSelectedResultItem == int.MinValue)
                        ResultMenu.IndexOfSelectedResultItem = ResultMenu.GetSearchResultCount() - 1;
                    else
                        ResultMenu.IndexOfSelectedResultItem--;
                    break;
                case Key.Down:
                    if (ResultMenu.IndexOfSelectedResultItem == int.MinValue)
                        ResultMenu.IndexOfSelectedResultItem = 0;
                    else
                        ResultMenu.IndexOfSelectedResultItem++;
                    break;
                case Key.Enter:
                    ResultMenu.SelectCurrentResultItem();
                    e.Handled = true;
                    break;
                case Key.Escape:
                    CancledSearchAction();
                    break;
            }
        }

        private void UiSettingsChanged(object sender, EventArgs e)
        {
            try
            {
                var positionAlignement = cFasdCockpitConfig.Instance.Global.SmallViewAlignment;

                switch (positionAlignement)
                {
                    case enumF4sdHorizontalAlignment.Center:
                        Dispatcher.Invoke(() => MainBorder.HorizontalAlignment = HorizontalAlignment.Center);
                        break;
                    case enumF4sdHorizontalAlignment.Right:
                        Dispatcher.Invoke(() => MainBorder.HorizontalAlignment = HorizontalAlignment.Right);
                        break;
                    default:
                        Dispatcher.Invoke(() => MainBorder.HorizontalAlignment = HorizontalAlignment.Left);
                        break;
                }

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

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

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

        private void ResultMenu_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            var scrollViewer = FindParent<ScrollViewer>((DependencyObject)sender);

            if (scrollViewer != null)
            {
                if (e.Delta > 0)
                    scrollViewer.LineUp();
                else
                    scrollViewer.LineDown();

                e.Handled = true;
            }
        }

        private T FindParent<T>(DependencyObject child) where T : DependencyObject
        {
            DependencyObject parent = VisualTreeHelper.GetParent(child);

            while (parent != null)
            {
                if (parent is T typed)
                    return typed;

                parent = VisualTreeHelper.GetParent(parent);
            }
            return null;
        }
    }
}
