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

using C4IT.FASD.Cockpit.Communication;
using C4IT.Logging;
using FasdDesktopUi.Basics.Models;
using FasdDesktopUi.Basics.Services;
using FasdDesktopUi.Basics.Services.Models;
using FasdDesktopUi.Pages.SearchPage;
using static C4IT.Logging.cLogManager;
namespace FasdDesktopUi.Basics.UserControls
{
    public partial class TicketOverview : UserControl
    {
        private readonly TicketOverviewModel _viewModel = new TicketOverviewModel();
        private bool _isInitialized;
        private static readonly (string Key, Action<TicketOverviewModel, int> Setter)[] CategorySetters = new[]
        {
            ("TicketsNew", new Action<TicketOverviewModel, int>((vm, value) => { vm.TicketsNew = value; })),
            ("TicketsActive", new Action<TicketOverviewModel, int>((vm, value) => { vm.TicketsActive = value; })),
            ("TicketsCritical", new Action<TicketOverviewModel, int>((vm, value) => { vm.TicketsCritical = value; })),
            ("TicketsNewInfo", new Action<TicketOverviewModel, int>((vm, value) => { vm.TicketsNewInfo = value; })),
            ("IncidentNew", new Action<TicketOverviewModel, int>((vm, value) => { vm.IncidentNew = value; })),
            ("IncidentActive", new Action<TicketOverviewModel, int>((vm, value) => { vm.IncidentActive = value; })),
            ("IncidentCritical", new Action<TicketOverviewModel, int>((vm, value) => { vm.IncidentCritical = value; })),
            ("IncidentNewInfo", new Action<TicketOverviewModel, int>((vm, value) => { vm.IncidentNewInfo = value; })),
            ("UnassignedTickets", new Action<TicketOverviewModel, int>((vm, value) => { vm.UnassignedTickets = value; })),
            ("UnassignedTicketsCritical", new Action<TicketOverviewModel, int>((vm, value) => { vm.UnassignedTicketsCritical = value; }))
        };
        private static readonly (string Key, Action<TicketOverviewModel, bool> Setter)[] HighlightSetters = new[]
        {
            ("TicketsNew", new Action<TicketOverviewModel, bool>((vm, value) => { vm.TicketsNewHighlighted = value; })),
            ("TicketsActive", new Action<TicketOverviewModel, bool>((vm, value) => { vm.TicketsActiveHighlighted = value; })),
            ("TicketsCritical", new Action<TicketOverviewModel, bool>((vm, value) => { vm.TicketsCriticalHighlighted = value; })),
            ("TicketsNewInfo", new Action<TicketOverviewModel, bool>((vm, value) => { vm.TicketsNewInfoHighlighted = value; })),
            ("IncidentNew", new Action<TicketOverviewModel, bool>((vm, value) => { vm.IncidentNewHighlighted = value; })),
            ("IncidentActive", new Action<TicketOverviewModel, bool>((vm, value) => { vm.IncidentActiveHighlighted = value; })),
            ("IncidentCritical", new Action<TicketOverviewModel, bool>((vm, value) => { vm.IncidentCriticalHighlighted = value; })),
            ("IncidentNewInfo", new Action<TicketOverviewModel, bool>((vm, value) => { vm.IncidentNewInfoHighlighted = value; })),
            ("UnassignedTickets", new Action<TicketOverviewModel, bool>((vm, value) => { vm.UnassignedTicketsHighlighted = value; })),
            ("UnassignedTicketsCritical", new Action<TicketOverviewModel, bool>((vm, value) => { vm.UnassignedTicketsCriticalHighlighted = value; }))
        };
        private static readonly (string Key, Action<TicketOverviewModel, string> Setter)[] ChangeHintSetters = new[]
        {
            ("TicketsNew", new Action<TicketOverviewModel, string>((vm, value) => { vm.TicketsNewChangeHint = value; })),
            ("TicketsActive", new Action<TicketOverviewModel, string>((vm, value) => { vm.TicketsActiveChangeHint = value; })),
            ("TicketsCritical", new Action<TicketOverviewModel, string>((vm, value) => { vm.TicketsCriticalChangeHint = value; })),
            ("TicketsNewInfo", new Action<TicketOverviewModel, string>((vm, value) => { vm.TicketsNewInfoChangeHint = value; })),
            ("IncidentNew", new Action<TicketOverviewModel, string>((vm, value) => { vm.IncidentNewChangeHint = value; })),
            ("IncidentActive", new Action<TicketOverviewModel, string>((vm, value) => { vm.IncidentActiveChangeHint = value; })),
            ("IncidentCritical", new Action<TicketOverviewModel, string>((vm, value) => { vm.IncidentCriticalChangeHint = value; })),
            ("IncidentNewInfo", new Action<TicketOverviewModel, string>((vm, value) => { vm.IncidentNewInfoChangeHint = value; })),
            ("UnassignedTickets", new Action<TicketOverviewModel, string>((vm, value) => { vm.UnassignedTicketsChangeHint = value; })),
            ("UnassignedTicketsCritical", new Action<TicketOverviewModel, string>((vm, value) => { vm.UnassignedTicketsCriticalChangeHint = value; }))
        };
        private readonly Dictionary<string, HighlightInfo> _personalHighlightStates = new Dictionary<string, HighlightInfo>(StringComparer.OrdinalIgnoreCase);
        private readonly Dictionary<string, HighlightInfo> _roleHighlightStates = new Dictionary<string, HighlightInfo>(StringComparer.OrdinalIgnoreCase);

        #region Properties
        public static TicketOverview Instance { get; private set; }
                
        public static readonly DependencyProperty IsSelectedProperty =
            DependencyProperty.RegisterAttached(
                "IsSelected",
                typeof(bool),
                typeof(TicketOverview),
                new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public static void SetIsSelected(UIElement element, bool value) =>
            element.SetValue(IsSelectedProperty, value);

        public static bool GetIsSelected(UIElement element) =>
            (bool)element.GetValue(IsSelectedProperty);

        public static readonly DependencyProperty IsHighlightedProperty =
            DependencyProperty.RegisterAttached(
                "IsHighlighted",
                typeof(bool),
                typeof(TicketOverview),
                new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public static void SetIsHighlighted(UIElement element, bool value) =>
            element.SetValue(IsHighlightedProperty, value);

        public static bool GetIsHighlighted(UIElement element) =>
            (bool)element.GetValue(IsHighlightedProperty);
        #endregion

        public delegate void TicketOverviewSelectionRequestedEventHandler(object sender, TicketOverviewSelectionRequestedEventArgs e);

        public static readonly RoutedEvent SelectionRequestedEvent =
            EventManager.RegisterRoutedEvent(
                "SelectionRequested",
                RoutingStrategy.Bubble,
                typeof(TicketOverviewSelectionRequestedEventHandler),
                typeof(TicketOverview)); 
        
        public event TicketOverviewSelectionRequestedEventHandler SelectionRequested
        {
            add { AddHandler(SelectionRequestedEvent, value); }
            remove { RemoveHandler(SelectionRequestedEvent, value); }
        }

        #region Initial
        public TicketOverview()
        {
            InitializeComponent();
            DataContext = _viewModel;
            Loaded += TicketOverview_Loaded;
            Instance = this;
        }
        #endregion

        #region Click-Events
        private void Label_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (!(sender is Label lbl) || !(lbl.Tag is string propName) || !(DataContext is TicketOverviewModel vm))
                return;

            var vmType = vm.GetType();
            var boolProp = vmType.GetProperty(propName);
            if (boolProp?.PropertyType != typeof(bool))
                return;

            var wasSelected = (bool)(boolProp.GetValue(vm) ?? false);

            var keyBase = propName.EndsWith("Selected", StringComparison.Ordinal)
                ? propName.Substring(0, propName.Length - "Selected".Length)
                : propName;

            int count = 0;
            var countProp = vmType.GetProperty(keyBase) ?? vmType.GetProperty(keyBase + "Count");
            if (countProp?.PropertyType == typeof(int))
                count = (int)(countProp.GetValue(vm) ?? 0);

            if (count <= 0)
            {
                boolProp.SetValue(vm, false);
                ClearHighlight(keyBase, SearchPageView.Instance?.IsFilterChecked == true);
                SearchPageView.Instance?.CloseTicketOverviewResults();
                return;
            }

            if (wasSelected)
            {
                boolProp.SetValue(vm, false);
                ClearHighlight(keyBase, SearchPageView.Instance?.IsFilterChecked == true);
                SearchPageView.Instance?.CloseTicketOverviewResults();
                return;
            }

            vm.ResetSelection(); // setzt alle ...Selected auf false
            boolProp.SetValue(vm, true);

            bool useRoleScope = false;
            try { useRoleScope = SearchPageView.Instance?.IsFilterChecked == true; } catch { }

            ClearHighlight(keyBase, useRoleScope);

            var args = new TicketOverviewSelectionRequestedEventArgs(
                SelectionRequestedEvent,
                this,
                keyBase,
                useRoleScope,
                count
            );
            RaiseEvent(args);
        }
        #endregion

        #region Methods 
        private async void TicketOverview_Loaded(object sender, RoutedEventArgs e)
        {
            MethodBase CM = null;
            if (cLogManager.DefaultLogger.IsDebug)
            {
                CM = MethodBase.GetCurrentMethod();
                LogMethodBegin(CM);
            }

            try
            {
                if (_isInitialized)
                    return;

                _isInitialized = true;

                bool filter = SearchPageView.Instance?.IsFilterChecked == true;
                await UpdateDataContextAsync(filter);

                if (SearchPageView.Instance != null)
                {
                    SearchPageView.Instance.FilterToggleCheckedChanged += async (s, isChecked) =>
                    {
                        try
                        {
                            await UpdateDataContextAsync(isChecked);
                        }
                        catch (Exception ex)
                        {
                            LogException(ex);
                        }
                    };
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        public void ResetSelection()
        {
            if (DataContext is TicketOverviewModel vm)
                vm.ResetSelection();
        }
        /// <summary>
        /// Prüft welche Daten für die Ansicht geladen werden sollen
        /// Wenn UseRoledTickets = true dann Rolebased Tickets und Incidents laden
        /// Wenn UseRoledTickets = false dann meine Eigenen Tickets und Incidents laden
        /// </summary>
        /// <param name="useRoleTickets"></param>
        private Task UpdateDataContextAsync(bool useRoleTickets)
        {
            return RefreshCountsAsync(useRoleTickets);
        }

        private async Task RefreshCountsAsync(bool useRoleTickets)
        {
            MethodBase CM = null;
            if (cLogManager.DefaultLogger.IsDebug)
            {
                CM = MethodBase.GetCurrentMethod();
                LogMethodBegin(CM);
            }

            Dictionary<string, int> counts = null;
            try
            {
                var service = TicketOverviewUpdateService.Instance;

                if (service != null)
                {
                    try
                    {
                        var scope = useRoleTickets ? TileScope.Role : TileScope.Personal;
                        await service.FetchAsync(scope).ConfigureAwait(false);
                        counts = service.GetCountsForScope(useRoleTickets);
                    }
                    catch (Exception ex)
                    {
                        LogException(ex);
                        counts = null;
                    }
                }

                if (counts == null || counts.Count == 0)
                    counts = await LoadCountsFallbackAsync(useRoleTickets).ConfigureAwait(false);

                await Dispatcher.InvokeAsync(() =>
                {
                    foreach (var (key, setter) in CategorySetters)
                    {
                        counts.TryGetValue(key, out var value);
                        setter(_viewModel, value);
                    }

                    _viewModel.ResetSelection();
                    SearchPageView.Instance?.CloseTicketOverviewResults();
                    UpdateHighlightState(useRoleTickets);
                }, DispatcherPriority.Background);
            }
            catch (Exception E)
            {
                LogException(E);
            }
            finally
            {
                LogMethodEnd(CM);
            }
        }

        private async Task<Dictionary<string, int>> LoadCountsFallbackAsync(bool useRoleTickets)
        {
            MethodBase CM = null;
            if (cLogManager.DefaultLogger.IsDebug)
            {
                CM = MethodBase.GetCurrentMethod();
                LogMethodBegin(CM);
            }

            try
            {
                var counts = CategorySetters.ToDictionary(item => item.Key, _ => 0);
                var communication = cFasdCockpitCommunicationBase.Instance;
                if (communication == null)
                    return counts;

                var tasks = CategorySetters.ToDictionary(
                    item => item.Key,
                    item => communication.GetTicketOverviewRelations(item.Key, useRoleTickets, 0));

                foreach (var kvp in tasks)
                {
                    try
                    {
                        var relations = await kvp.Value.ConfigureAwait(false);
                        counts[kvp.Key] = relations?.Count ?? 0;
                    }
                    catch (Exception ex)
                    {
                        LogException(ex);
                        counts[kvp.Key] = 0;
                    }
                }

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

            return CategorySetters.ToDictionary(item => item.Key, _ => 0);
        }

        public void UpdateCounts(IDictionary<string, int> counts, bool useRoleTickets)
        {
            if (counts == null)
                return;

            Dispatcher.Invoke(() =>
            {
                foreach (var (key, setter) in CategorySetters)
                {
                    counts.TryGetValue(key, out var value);
                    setter(_viewModel, value);
                }

                UpdateHighlightState(useRoleTickets);
            }, DispatcherPriority.Background);
        }

        public void ClearHighlightsForScope(TileScope scope)
        {
            var highlightStates = scope == TileScope.Role ? _roleHighlightStates : _personalHighlightStates;
            if (highlightStates.Count == 0)
                return;

            highlightStates.Clear();
            UpdateHighlightState(scope == TileScope.Role);
        }

        public void SetHighlights(IEnumerable<TileCountChange> changes, bool useRoleTickets)
        {
            if (changes != null)
            {
                foreach (var change in changes)
                {
                    if (change.Delta <= 0)
                        continue;

                    var target = change.Scope == TileScope.Personal ? _personalHighlightStates : _roleHighlightStates;

                    if (!target.TryGetValue(change.Key, out var info))
                    {
                        info = new HighlightInfo(change.OldCount);
                        target[change.Key] = info;
                    }

                    info.Current = change.NewCount;

                    if (info.Current == info.Baseline)
                        target.Remove(change.Key);
                }
            }

            UpdateHighlightState(useRoleTickets);
        }

        public void RefreshHighlightState(bool useRoleTickets)
        {
            UpdateHighlightState(useRoleTickets);
        }

        private void UpdateHighlightState(bool useRoleTickets)
        {
            Dispatcher.Invoke(() =>
            {
                var highlightStates = useRoleTickets ? _roleHighlightStates : _personalHighlightStates;

                foreach (var (key, setter) in HighlightSetters)
                {
                    setter(_viewModel, highlightStates.ContainsKey(key));
                }

                foreach (var (key, setter) in ChangeHintSetters)
                {
                    if (highlightStates.TryGetValue(key, out var info))
                        setter(_viewModel, FormatDelta(info.Current - info.Baseline));
                    else
                        setter(_viewModel, null);
                }
            }, DispatcherPriority.Background);
        }

        private void ClearHighlight(string key, bool useRoleScope)
        {
            var highlightStates = useRoleScope ? _roleHighlightStates : _personalHighlightStates;
            if (!highlightStates.Remove(key))
                return;

            UpdateHighlightState(useRoleScope);
        }


        #endregion

        private static string FormatDelta(int delta)
        {
            if (delta == 0)
                return null;

            var prefix = delta > 0 ? "+" : string.Empty;
            return prefix + delta.ToString();
        }

        private sealed class HighlightInfo
        {
            public HighlightInfo(int baseline)
            {
                Baseline = baseline;
                Current = baseline;
            }

            public int Baseline { get; }

            public int Current { get; set; }
        }
    }
}
