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

namespace InABox.DynamicGrid;

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

    public bool MultipleFilters { get; set; }

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

    public DynamicGridSelectedFilterSettings(List<CoreFilterDefinition> selectedFilters, bool multipleFilters)
    {
        SelectedFilters = selectedFilters;
        MultipleFilters = multipleFilters || selectedFilters.Count > 1;
    }
}

public class DynamicGridFilterButtonComponent<T>
    where T : BaseObject, new()
{

    private Button FilterBtn = null!; // Late-initialised

    private bool _multipleFilters = false;

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

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

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

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

        public Filter<T> Filter { get; }

        public CoreFilterDefinition Definition { get; set; }

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

    private List<FilterItem> SelectedFilters = new();

    private IDynamicGrid Grid;

    private IConfiguration<CoreFilterDefinitions> Configuration;

    public delegate void FilterSelectedHandler(DynamicGridSelectedFilterSettings filters);

    public event FilterSelectedHandler OnFiltersSelected;

    public delegate void FilterRefreshHandler();

    public event FilterRefreshHandler OnFilterRefresh;

    public DynamicGridFilterButtonComponent(IDynamicGrid grid, IConfiguration<CoreFilterDefinitions> configuration)
    {
        Grid = grid;
        Configuration = configuration;

        FilterBtn = Grid.AddButton("", Wpf.Resources.filter.AsBitmapImage(), DoFilter);
        FilterBtn.Margin = new Thickness(0, 2, 7, 0);
        FilterBtn.Padding = new Thickness(0);
    }

    public void SetSettings(DynamicGridSelectedFilterSettings settings, bool refresh)
    {
        _multipleFilters = settings.MultipleFilters;
        SelectFilters(settings.SelectedFilters, refresh);
    }

    #region Menu

    private ContextMenu? Menu;

    private void RebuildMenu(ContextMenu menu)
    {
        menu.Items.Clear();
        menu.AddItem("Clear Filters", null, ClearFilter_Click);
        var multipleFilters = menu.AddCheckItem("Use Multiple Filters", MultipleFilters_Click, MultipleFilters);
        multipleFilters.StaysOpenOnClick = true;

        var filters = Configuration.Load();
        foreach (var filter in filters)
        {
            var item = menu.AddCheckItem(filter.Name, filter, Filter_Toggle);
            item.StaysOpenOnClick = true;
            if (SelectedFilters.Any(x => x.Name.Equals(filter.Name)))
            {
                item.IsChecked = true;
            }
        }

        if (Security.IsAllowed<CanCustomiseFilters>())
        {
            menu.AddSeparator();
            menu.AddItem("Manage Filters", null, () =>
            {
                var window = new DynamicGridFilterEditor(filters, typeof(T));
                if (window.ShowDialog() == true)
                {
                    Configuration.Save(filters);
                }
            });
        }
    }

    private bool DoFilter(Button button, CoreRow[] rows)
    {
        Menu = new ContextMenu();

        RebuildMenu(Menu);

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

        return false;
    }

    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(CoreFilterDefinition 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 (SelectedFilters.Count > 0)
        {
            SelectedFilters.Clear();
            UpdateFilters(refresh);
        }
    }

    public void DeselectFilter(CoreFilterDefinition 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(CoreFilterDefinition? 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();
        }
        if (filter is not null && !SelectedFilters.Any(x => x.Name.Equals(filter.Name)))
        {
            SelectedFilters.Add(new(filter.Name, Serialization.Deserialize<Filter<T>>(filter.Filter), filter));
        }

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

    public void SelectFilters(IEnumerable<CoreFilterDefinition> filters, bool refresh)
    {
        SelectedFilters.Clear();
        foreach (var filter in filters)
        {
            SelectedFilters.Add(new(filter.Name, Serialization.Deserialize<Filter<T>>(filter.Filter), filter));
        }
        if (SelectedFilters.Count > 1 && !MultipleFilters)
        {
            MultipleFilters = true;
        }
        UpdateFilters(refresh);
    }

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

    private void UpdateButton()
    {
        var image = SelectedFilters.Count > 0
            ? Wpf.Resources.filter_set
            : Wpf.Resources.filter;
        var text = string.Join(", ", SelectedFilters.Select(x => x.Name));
        if (!ButtonText.IsNullOrWhiteSpace())
        {
            if (text.IsNullOrWhiteSpace())
            {
                text = ButtonText;
            }
            else
            {
                text = $"{ButtonText}: {text}";
            }
        }
        Grid.UpdateButton(FilterBtn, image.AsBitmapImage(), text);
    }

    private void SaveSettings()
    {
        OnFiltersSelected?.Invoke(new DynamicGridSelectedFilterSettings(SelectedFilters.Select(x => x.Definition).ToList(), MultipleFilters));
    }

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