using InABox.Configuration;
using InABox.Core;
using InABox.WPF;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace InABox.DynamicGrid;

public class DynamicGridSelectedFilterSettings
{
    public string? TemporaryFilter { get; set; }

    public List<CoreFilterDefinition> SelectedFilters { get; set; }

    public bool MultipleFilters { get; set; }

    public DynamicGridSelectedFilterSettings()
    {
        SelectedFilters = new List<CoreFilterDefinition>();
        MultipleFilters = false;
    }

    public DynamicGridSelectedFilterSettings(List<ICoreFilterDefinition> selectedFilters, bool multipleFilters, string? temporaryFilter)
    {
        SelectedFilters = selectedFilters.Select(x =>
        {
            if(x is CoreFilterDefinition def)
            {
                return def;
            }
            else
            {
                return new CoreFilterDefinition
                {
                    Name = x.Name,
                    Filter = ""
                };
            }
        }).ToList();
        MultipleFilters = multipleFilters || selectedFilters.Count > 1;
        TemporaryFilter = temporaryFilter;
    }
}

public class DynamicGridFilterComponentBuiltInFilter<T>(string name, Func<Filter<T>> getFilter) : ICoreFilterDefinition
{
    public string Name { get; set; } = name;

    public Func<Filter<T>> GetFilter { get; set; } = getFilter;

    public Filter<T1>? AsFilter<T1>()
    {
        return GetFilter() as Filter<T1>;
    }
}

public abstract class DynamicGridFilterComponent<T>
{
    #region Private Fields

    private class FilterItem
    {
        public string Name { get; set; }

        public Filter<T>? Filter { get; }

        public ICoreFilterDefinition Definition { get; set; }

        public FilterItem(string name, Filter<T>? filter, ICoreFilterDefinition definition)
        {
            Name = name;
            Filter = filter;
            Definition = definition;
        }
    }

    private List<FilterItem> SelectedFilters = new();

    private Filter<T>? TemporaryFilter = null;

    private int NumberOfFilters => SelectedFilters.Count + (TemporaryFilter is null ? 0 : 1);

    #endregion

    #region Public Properties

    public IConfiguration<CoreFilterDefinitions> GlobalConfiguration { get; set; }

    public IConfiguration<CoreFilterDefinitions> UserConfiguration { get; set; }

    public List<DynamicGridFilterComponentBuiltInFilter<T>> BuiltInFilters { get; init; } = new();

    private bool _multipleFilters = false;
    private bool MultipleFilters
    {
        get => _multipleFilters;
        set
        {
            if (_multipleFilters != value)
            {
                _multipleFilters = value;
                if (!_multipleFilters && NumberOfFilters > 1)
                {
                    ClearFilters(true);
                }
            }
        }
    }

    private string? _buttonText;
    public string? ButtonText
    {
        get => _buttonText;
        set
        {
            if (!string.Equals(_buttonText, value))
            {
                _buttonText = value;
                UpdateButton();
            }
        }
    }

    #endregion

    #region Events

    public delegate void FilterSelectedHandler(DynamicGridSelectedFilterSettings filters);

    public event FilterSelectedHandler? OnFiltersSelected;

    public delegate void FilterRefreshHandler();

    public event FilterRefreshHandler? OnFilterRefresh;

    #endregion

    public DynamicGridFilterComponent(
        IConfiguration<CoreFilterDefinitions> globalConfiguration,
        IConfiguration<CoreFilterDefinitions> userConfiguration)
    {
        GlobalConfiguration = globalConfiguration;
        UserConfiguration = userConfiguration;
    }

    public void SetSettings(DynamicGridSelectedFilterSettings settings, bool refresh)
    {
        _multipleFilters = settings.MultipleFilters;
        TemporaryFilter = Serialization.Deserialize<Filter<T>>(settings.TemporaryFilter);
        SelectFilters(settings.SelectedFilters, refresh);
    }

    #region Menu

    private ContextMenu? Menu;

    public void PopulateMenu(ContextMenu menu)
    {
        RebuildMenu(menu);
    }
    public void PopulateMenu(MenuItem menu)
    {
        RebuildMenu(menu);
    }

    private void RebuildMenu(ItemsControl menu)
    {
        menu.Items.Clear();

        if (TemporaryFilter is null)
        {
            menu.AddCheckMenuItem("Filter...", TemporaryFilter_Click, isChecked: false);
        }
        else
        {
            menu.AddCheckMenuItem("<Custom Filter>", TemporaryFilter_Click, isChecked: true);
        }

        menu.AddMenuItem("Clear Filters", null, ClearFilter_Click);
        var multipleFilters = menu.AddCheckMenuItem("Use Multiple Filters", MultipleFilters_Click, MultipleFilters);
        multipleFilters.StaysOpenOnClick = true;

        menu.AddMenuSeparatorIfNeeded();

        var globalFilters = Security.IsAllowed<CanEditGlobalFilters>() ? GlobalConfiguration.Load() : new CoreFilterDefinitions();
        var userFilters = UserConfiguration.Load();
        var filters = new CoreFilterDefinitions(globalFilters.Count + userFilters.Count);
        foreach (var filter in BuiltInFilters.Concat<ICoreFilterDefinition>(globalFilters).Concat(userFilters))
        {
            var item = menu.AddCheckMenuItem(filter.Name, filter, Filter_Toggle);
            item.StaysOpenOnClick = true;
            if (SelectedFilters.Any(x => x.Name.Equals(filter.Name)))
            {
                item.IsChecked = true;
            }
            if(filter is CoreFilterDefinition coreFilter)
            {
                filters.Add(coreFilter);
            }
        }

        if (Security.IsAllowed<CanCustomiseFilters>())
        {
            menu.AddMenuSeparatorIfNeeded();
            menu.AddMenuItem("Manage Filters", null, () =>
            {
                var window = new DynamicGridFilterEditor(filters, typeof(T));
                if (window.ShowDialog() == true)
                {
                    globalFilters.Clear();
                    globalFilters.AddRange(filters.Where(x => !x.Private));

                    userFilters.Clear();
                    userFilters.AddRange(filters.Where(x => x.Private));

                    if (Security.IsAllowed<CanEditGlobalFilters>())
                    {
                        GlobalConfiguration.Save(globalFilters);
                    }
                    UserConfiguration.Save(userFilters);
                }
            });
        }
    }

    private void TemporaryFilter_Click(bool isChecked)
    {
        var window = new FilterEditorWindow();
        window.SetFilter(TemporaryFilter ?? new Filter<T>());
        if (window.ShowDialog() == true)
        {
            TemporaryFilter = window.GetFilter<T>();
            if (TemporaryFilter is not null && !MultipleFilters)
            {
                SelectedFilters.Clear();
            }
            UpdateFilters(true);
        }
    }

    protected void DoFilter()
    {
        Menu = new ContextMenu();

        RebuildMenu(Menu);

        Menu.IsOpen = true;
        Menu.Closed += Menu_Closed;
    }

    private void Menu_Closed(object sender, RoutedEventArgs e)
    {
        if (Menu == sender)
        {
            Menu = null;
        }
    }

    private void ClearFilter_Click()
    {
        ClearFilters(true);
    }

    private void MultipleFilters_Click(bool isChecked)
    {
        MultipleFilters = isChecked;
        if (Menu is not null)
        {
            RebuildMenu(Menu);
        }
        SaveSettings();
    }

    private void Filter_Toggle(ICoreFilterDefinition tag, bool isChecked)
    {
        if (MultipleFilters)
        {
            if (isChecked)
            {
                SelectFilter(tag, true, true);
            }
            else
            {
                DeselectFilter(tag, true);
            }
        }
        else
        {
            SelectFilter(tag, false, true);
            if (Menu is not null)
            {
                Menu.IsOpen = false;
            }
        }
    }

    #endregion

    public void ClearFilters(bool refresh)
    {
        if (NumberOfFilters > 0)
        {
            SelectedFilters.Clear();
            TemporaryFilter = null;
            UpdateFilters(refresh);
        }
    }

    public void DeselectFilter(ICoreFilterDefinition filter, bool refresh)
    {
        var removed = SelectedFilters.RemoveAll(x => x.Name.Equals(filter.Name));
        if (removed > 0)
        {
            UpdateFilters(refresh);
        }
    }

    /// <summary>
    /// Select a new filter. If <paramref name="additional"/> is <see langword="true"/>, adds it to the list of selected filters;
    /// otherwise, it sets this as the only filter.
    /// </summary>
    /// <param name="filter"></param>
    /// <param name="additional"></param>
    /// <param name="refresh"></param>
    public void SelectFilter(ICoreFilterDefinition? filter, bool additional, bool refresh)
    {
        var originalCount = SelectedFilters.Count;
        var alreadyHas = filter is null || SelectedFilters.Any(x => x.Name.Equals(filter.Name));

        if (!additional)
        {
            SelectedFilters.Clear();
            TemporaryFilter = null;
        }
        if (filter is not null && !SelectedFilters.Any(x => x.Name.Equals(filter.Name)))
        {
            SelectedFilters.Add(new(filter.Name, filter.AsFilter<T>(), filter));
        }

        if (SelectedFilters.Count != originalCount || !alreadyHas)
        {
            UpdateFilters(refresh);
        }
    }

    public void SelectFilters(IEnumerable<ICoreFilterDefinition> filters, bool refresh)
    {
        SelectedFilters.Clear();
        foreach (var filter in filters)
        {
            var builtIn = BuiltInFilters.FirstOrDefault(x => x.Name == filter.Name);
            if(builtIn is not null)
            {
                SelectedFilters.Add(new(filter.Name, builtIn.AsFilter<T>(), builtIn));
            }
            else
            {
                SelectedFilters.Add(new(filter.Name, filter.AsFilter<T>(), filter));
            }
        }
        if (NumberOfFilters > 1 && !MultipleFilters)
        {
            MultipleFilters = true;
        }
        UpdateFilters(refresh);
    }

    private void UpdateFilters(bool refresh)
    {
        UpdateButton();
        SaveSettings();
        if (refresh)
        {
            OnFilterRefresh?.Invoke();
        }
    }

    protected abstract void UpdateButtonText(Bitmap image, string text);

    private void UpdateButton()
    {
        var image = NumberOfFilters > 0
            ? Wpf.Resources.filter_set
            : Wpf.Resources.filter;

        var filterNames = SelectedFilters.Select(x => x.Name);
        if (TemporaryFilter is not null)
        {
            filterNames = CoreUtils.One("Custom").Concat(filterNames);
        }

        var text = string.Join(", ", filterNames);
        if (!ButtonText.IsNullOrWhiteSpace())
        {
            if (text.IsNullOrWhiteSpace())
            {
                text = ButtonText;
            }
            else
            {
                text = $"{ButtonText}: {text}";
            }
        }
        UpdateButtonText(image, text);
    }

    private void SaveSettings()
    {
        OnFiltersSelected?.Invoke(new DynamicGridSelectedFilterSettings(
            SelectedFilters.Select(x => x.Definition).ToList(),
            MultipleFilters,
            TemporaryFilter is not null ? Serialization.Serialize(TemporaryFilter) : null));
    }

    public Filter<T>? GetFilter()
    {
        var filters = new Filters<T>();
        filters.AddRange(SelectedFilters.Select(x => x.Filter));
        filters.Add(TemporaryFilter);
        return filters.Combine();
    }
}

public class DynamicGridFilterButtonComponent<T> : DynamicGridFilterComponent<T>
    where T : BaseObject, new()
{
    private readonly IDynamicGrid Grid;

    private Button FilterBtn = null!;


    private bool _showFilterList;
    public bool ShowFilterList
    {
        get => _showFilterList;
        set
        {
            _showFilterList = value;
            if (FilterBtn != null)
                FilterBtn.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
        }
    }

    public DynamicGridFilterButtonComponent(
        IDynamicGrid grid,
        IConfiguration<CoreFilterDefinitions> globalConfiguration,
        IConfiguration<CoreFilterDefinitions> userConfiguration) : base(globalConfiguration, userConfiguration)
    {
        Grid = grid;

        CreateButton();
    }

    private void CreateButton()
    {
        FilterBtn = Grid.AddButton("", Wpf.Resources.filter.AsBitmapImage(), Click);
        FilterBtn.Margin = new Thickness(0, 2, 7, 0);
        FilterBtn.Padding = new Thickness(0);
    }

    private bool Click(Button _arg1, CoreRow[] _arg2)
    {
        DoFilter();
        return false;
    }

    protected override void UpdateButtonText(Bitmap image, string text)
    {
        Grid.UpdateButton(FilterBtn, image.AsBitmapImage(), text);
    }
}