using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Input;
using CommunityToolkit.WinUI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Data;
using Wino.Core.Domain;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.Menus;
using Wino.Helpers;

namespace Wino.Mail.WinUI.Controls;

public sealed partial class OperationCommandBar : CommandBar
{
    private const string MailOperationTemplateKey = "OperationCommandBarMailOperationTemplate";
    private const string FolderOperationTemplateKey = "OperationCommandBarFolderOperationTemplate";
    private const string AIActionsTemplateKey = "OperationCommandBarAIActionsTemplate";
    private const string PopOutTemplateKey = "OperationCommandBarThemeToggleTemplate";
    private const string ThemeToggleTemplateKey = "OperationCommandBarThemeToggleTemplate";
    private const string SeparatorTemplateKey = "OperationCommandBarSeparatorTemplate";

    private readonly IPreferencesService? _preferencesService;
    private readonly HashSet<INotifyPropertyChanged> _trackedMenuItems = [];

    [GeneratedDependencyProperty]
    public partial ObservableCollection<IMenuOperation>? MenuItems { get; set; }

    [GeneratedDependencyProperty]
    public partial ICommand? ItemInvokedCommand { get; set; }

    [GeneratedDependencyProperty]
    public partial bool IsAIActionsEnabled { get; set; }

    [GeneratedDependencyProperty]
    public partial bool IsAIActionsPaneToggleVisible { get; set; }

    [GeneratedDependencyProperty]
    public partial bool IsEditorThemeDark { get; set; }

    [GeneratedDependencyProperty]
    public partial bool IsEditorThemeToggleVisible { get; set; }

    [GeneratedDependencyProperty]
    public partial bool IsPopOutButtonVisible { get; set; }

    public event EventHandler<bool>? AIActionsEnabledChanged;
    public event EventHandler? PopOutClicked;

    public OperationCommandBar()
    {
        _preferencesService = App.Current.Services.GetService<IPreferencesService>();

        DefaultLabelPosition = CommandBarDefaultLabelPosition.Right;
        IsDynamicOverflowEnabled = true;
        OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Auto;

        Loaded += OnLoaded;
        DynamicOverflowItemsChanging += OperationCommandBar_DynamicOverflowItemsChanging;
    }

    partial void OnMenuItemsPropertyChanged(DependencyPropertyChangedEventArgs e)
    {
        if (e.OldValue is INotifyCollectionChanged oldCollection)
        {
            oldCollection.CollectionChanged -= MenuItems_CollectionChanged;
        }

        DetachTrackedMenuItemHandlers();

        if (e.NewValue is ObservableCollection<IMenuOperation> newItems)
        {
            newItems.CollectionChanged += MenuItems_CollectionChanged;
            TrackMenuItemHandlers((IEnumerable<IMenuOperation>)newItems);
        }

        RefreshCommands();
    }

    partial void OnIsAIActionsEnabledChanged(bool newValue)
    {
        AIActionsEnabledChanged?.Invoke(this, newValue);
    }

    partial void OnIsAIActionsPaneToggleVisibleChanged(bool newValue)
    {
        RefreshCommands();
    }

    partial void OnIsEditorThemeDarkChanged(bool newValue)
    {
        RefreshCommands();
    }

    partial void OnIsEditorThemeToggleVisibleChanged(bool newValue)
    {
        RefreshCommands();
    }

    partial void OnIsPopOutButtonVisibleChanged(bool newValue)
    {
        RefreshCommands();
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
        RefreshCommands();
    }

    private void OperationCommandBar_DynamicOverflowItemsChanging(CommandBar sender, DynamicOverflowItemsChangingEventArgs args)
    {
        if (args.Action == CommandBarDynamicOverflowAction.AddingToOverflow || sender.SecondaryCommands.Count > 0)
        {
            sender.OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Visible;
        }
        else
        {
            sender.OverflowButtonVisibility = CommandBarOverflowButtonVisibility.Collapsed;
        }
    }

    private void MenuItems_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Reset)
        {
            DetachTrackedMenuItemHandlers();

            if (sender is IEnumerable<IMenuOperation> refreshedItems)
            {
                TrackMenuItemHandlers(refreshedItems);
            }
        }
        else
        {
            UntrackMenuItemHandlers(e.OldItems);
            TrackMenuItemHandlers(e.NewItems);
        }

        RefreshCommands();
    }

    private void MenuItem_PropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        if (string.IsNullOrEmpty(e.PropertyName)
            || e.PropertyName == nameof(IMenuOperation.IsEnabled)
            || e.PropertyName == nameof(IMenuOperation.IsSecondaryMenuPreferred)
            || e.PropertyName == nameof(MenuOperationItemBase<MailOperation>.Operation)
            || e.PropertyName == nameof(MenuOperationItemBase<MailOperation>.Identifier))
        {
            RefreshCommands();
        }
    }

    private void TrackMenuItemHandlers(IEnumerable<IMenuOperation> items)
    {
        foreach (var item in items)
        {
            if (item is INotifyPropertyChanged propertyChanged && _trackedMenuItems.Add(propertyChanged))
            {
                propertyChanged.PropertyChanged += MenuItem_PropertyChanged;
            }
        }
    }

    private void TrackMenuItemHandlers(System.Collections.IList? items)
    {
        if (items == null)
        {
            return;
        }

        foreach (var item in items)
        {
            if (item is IMenuOperation menuItem)
            {
                TrackMenuItemHandlers([menuItem]);
            }
        }
    }

    private void UntrackMenuItemHandlers(System.Collections.IList? items)
    {
        if (items == null)
        {
            return;
        }

        foreach (var item in items)
        {
            if (item is INotifyPropertyChanged propertyChanged && _trackedMenuItems.Remove(propertyChanged))
            {
                propertyChanged.PropertyChanged -= MenuItem_PropertyChanged;
            }
        }
    }

    private void DetachTrackedMenuItemHandlers()
    {
        foreach (var item in _trackedMenuItems)
        {
            item.PropertyChanged -= MenuItem_PropertyChanged;
        }

        _trackedMenuItems.Clear();
    }

    private void RefreshCommands()
    {
        ClearGeneratedCommands();

        if (IsAIActionsPaneToggleVisible)
        {
            PrimaryCommands.Add(CreateAIActionsToggleButton());
        }

        if (IsPopOutButtonVisible)
        {
            PrimaryCommands.Add(CreatePopOutButton());
        }

        if (IsEditorThemeToggleVisible)
        {
            PrimaryCommands.Add(CreateThemeToggleButton());
        }

        if (MenuItems == null)
        {
            UpdateOverflowButtonVisibility();
            return;
        }

        foreach (var item in MenuItems)
        {
            var element = CreateCommandElement(item);
            if (element == null)
            {
                continue;
            }

            if (item.IsSecondaryMenuPreferred)
            {
                SecondaryCommands.Add(element);
            }
            else
            {
                PrimaryCommands.Add(element);
            }
        }

        UpdateOverflowButtonVisibility();
    }

    private void ClearGeneratedCommands()
    {
        DetachCommandHandlers(PrimaryCommands);
        DetachCommandHandlers(SecondaryCommands);

        PrimaryCommands.Clear();
        SecondaryCommands.Clear();
    }

    private void DetachCommandHandlers(IEnumerable<ICommandBarElement> commands)
    {
        foreach (var command in commands)
        {
            switch (command)
            {
                case AppBarButton button:
                    button.Click -= OperationButton_Click;
                    button.Click -= ThemeButton_Click;
                    button.Click -= PopOutButton_Click;
                    break;
                case AppBarToggleButton toggleButton:
                    toggleButton.ClearValue(AppBarToggleButton.IsCheckedProperty);
                    break;
            }
        }
    }

    private ICommandBarElement? CreateCommandElement(IMenuOperation item)
    {
        if (item is MailOperationMenuItem mailOperation && mailOperation.Operation == MailOperation.Seperator)
        {
            return LoadCommandBarElementTemplate(SeparatorTemplateKey, new SeparatorCommandBarItemViewModel());
        }

        if (item is MailOperationMenuItem mailOperationItem)
        {
            var button = LoadCommandBarElementTemplate(
                MailOperationTemplateKey,
                new OperationCommandBarMenuOperationItemViewModel(
                    mailOperationItem,
                    XamlHelpers.GetOperationString(mailOperationItem.Operation),
                    XamlHelpers.GetWinoIconGlyph(mailOperationItem.Operation),
                    GetOperationLabelPosition(XamlHelpers.GetOperationString(mailOperationItem.Operation))))
                as AppBarButton;

            if (button == null)
            {
                return null;
            }

            button.Tag = mailOperationItem;
            button.Click += OperationButton_Click;
            return button;
        }

        if (item is FolderOperationMenuItem folderOperationItem)
        {
            var label = XamlHelpers.GetOperationString(folderOperationItem.Operation);
            var button = LoadCommandBarElementTemplate(
                FolderOperationTemplateKey,
                new OperationCommandBarMenuOperationItemViewModel(
                    folderOperationItem,
                    label,
                    XamlHelpers.GetPathGeometry(folderOperationItem.Operation),
                    GetOperationLabelPosition(label)))
                as AppBarButton;

            if (button == null)
            {
                return null;
            }

            button.Tag = folderOperationItem;
            button.Click += OperationButton_Click;
            return button;
        }

        return null;
    }

    private AppBarToggleButton CreateAIActionsToggleButton()
    {
        var button = (AppBarToggleButton)LoadCommandBarElementTemplate(
            AIActionsTemplateKey,
            new OperationCommandBarAIActionsItemViewModel(Translator.Composer_AiActions, "\uE945"));

        button.SetBinding(AppBarToggleButton.IsCheckedProperty, new Binding
        {
            Mode = BindingMode.TwoWay,
            Path = new PropertyPath(nameof(IsAIActionsEnabled)),
            Source = this
        });

        return button;
    }

    private AppBarButton CreateThemeToggleButton()
    {
        var label = IsEditorThemeDark ? Translator.Composer_LightTheme : Translator.Composer_DarkTheme;
        var icon = IsEditorThemeDark ? WinoIconGlyph.LightEditor : WinoIconGlyph.DarkEditor;

        var button = (AppBarButton)LoadCommandBarElementTemplate(
            ThemeToggleTemplateKey,
            new OperationCommandBarThemeItemViewModel(label, icon));

        button.Click += ThemeButton_Click;
        return button;
    }

    private AppBarButton CreatePopOutButton()
    {
        var button = (AppBarButton)LoadCommandBarElementTemplate(
            PopOutTemplateKey,
            new OperationCommandBarThemeItemViewModel(Translator.Buttons_PopOut, WinoIconGlyph.OpenInNewWindow));

        button.Click += PopOutButton_Click;
        return button;
    }

    private void OperationButton_Click(object sender, RoutedEventArgs e)
    {
        if (sender is AppBarButton button && button.Tag is IMenuOperation operation)
        {
            ItemInvokedCommand?.Execute(operation);
        }
    }

    private void ThemeButton_Click(object sender, RoutedEventArgs e)
    {
        IsEditorThemeDark = !IsEditorThemeDark;
    }

    private void PopOutButton_Click(object sender, RoutedEventArgs e)
    {
        PopOutClicked?.Invoke(this, EventArgs.Empty);
    }

    private object? FindTemplateResource(string key)
    {
        if (TryGetResourceRecursive(Resources, key, out var resource))
        {
            return resource;
        }

        return TryGetResourceRecursive(Application.Current.Resources, key, out resource) ? resource : null;
    }

    private static bool TryGetResourceRecursive(ResourceDictionary dictionary, string key, out object? resource)
    {
        if (dictionary.TryGetValue(key, out resource))
        {
            return true;
        }

        foreach (var mergedDictionary in dictionary.MergedDictionaries)
        {
            if (TryGetResourceRecursive(mergedDictionary, key, out resource))
            {
                return true;
            }
        }

        resource = null;
        return false;
    }

    private ICommandBarElement LoadCommandBarElementTemplate(string resourceKey, object dataContext)
    {
        var template = FindTemplateResource(resourceKey) as DataTemplate
                       ?? throw new InvalidOperationException($"Unable to resolve resource '{resourceKey}'.");

        if (template.LoadContent() is not ICommandBarElement element)
        {
            throw new InvalidOperationException($"Resource '{resourceKey}' did not create an ICommandBarElement.");
        }

        if (element is FrameworkElement frameworkElement)
        {
            frameworkElement.DataContext = dataContext;
        }

        return element;
    }

    private CommandBarLabelPosition GetOperationLabelPosition(string label)
    {
        return string.IsNullOrWhiteSpace(label) || _preferencesService == null || !_preferencesService.IsShowActionLabelsEnabled
            ? CommandBarLabelPosition.Collapsed
            : CommandBarLabelPosition.Default;
    }

    private void UpdateOverflowButtonVisibility()
    {
        OverflowButtonVisibility = SecondaryCommands.Count > 0
            ? CommandBarOverflowButtonVisibility.Visible
            : CommandBarOverflowButtonVisibility.Auto;
    }

    public void InvalidateCommands()
    {
        RefreshCommands();
    }

    private sealed class SeparatorCommandBarItemViewModel;
}

public sealed class OperationCommandBarMenuOperationItemViewModel
{
    public OperationCommandBarMenuOperationItemViewModel(IMenuOperation operation, string label, WinoIconGlyph icon, CommandBarLabelPosition labelPosition)
    {
        Operation = operation;
        Label = label;
        Icon = icon;
        ToolTip = label;
        LabelPosition = labelPosition;
    }

    public IMenuOperation Operation { get; }
    public string Label { get; }
    public WinoIconGlyph Icon { get; }
    public string ToolTip { get; }
    public bool IsEnabled => Operation.IsEnabled;
    public CommandBarLabelPosition LabelPosition { get; }
}

public sealed class OperationCommandBarAIActionsItemViewModel
{
    public OperationCommandBarAIActionsItemViewModel(string toolTip, string glyph)
    {
        ToolTip = toolTip;
        Glyph = glyph;
    }

    public string ToolTip { get; }
    public string Glyph { get; }
}

public sealed class OperationCommandBarThemeItemViewModel
{
    public OperationCommandBarThemeItemViewModel(string toolTip, WinoIconGlyph icon)
    {
        ToolTip = toolTip;
        Icon = icon;
    }

    public string ToolTip { get; }
    public WinoIconGlyph Icon { get; }
}
