using InABox.Core;
using InABox.WPF;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;

namespace InABox.DynamicGrid;

public abstract class CheckBoxDynamicGridColumnFilter : IDynamicGridColumnFilter
{

    protected HashSet<object?>? SelectedFilters = null;
    private FilterItem[] _items = [];

    private FilterItem _selectAll;

    public event Action<IDynamicGridColumnFilter>? FilterChanged;

    public CheckBoxDynamicGridColumnFilter()
    {
        _selectAll = new(new(), "Select All", this);
    }

    #region Abstract Interface

    protected abstract FilterItem[] GetData();

    public abstract bool FilterRow(CoreRow row);

    #endregion

    protected class FilterItem(object? value, string display, CheckBoxDynamicGridColumnFilter parent) : INotifyPropertyChanged
    {
        private object? _value = value;
        public object? Value
        {
            get => _value;
            set
            {
                _value = value;
                OnPropertyChanged();
            }
        }

        private string _display = display;
        public string Display
        {
            get => _display;
            set
            {
                _display = value;
                OnPropertyChanged();
            }
        }

        public bool IsChecked
        {
            get => this == parent._selectAll
                ? (parent.SelectedFilters is null)
                : (parent.SelectedFilters is null || parent.SelectedFilters.Contains(Value));
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        private void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new(propertyName));
        }

        public void UpdateChecked()
        {
            OnPropertyChanged(nameof(IsChecked));
        }
    }

    public FrameworkElement CreateControl()
    {
        var grid = new Grid();
        grid.AddRow(GridUnitType.Auto);
        grid.AddRow(GridUnitType.Star);

        var searchBox = new TextBox
        {
            Background = Colors.LightYellow.ToBrush(),
            Margin = new(0.0, 0.0, 0.0, 5.0),
            Padding = new(5.0),
            VerticalContentAlignment = VerticalAlignment.Center
        };
        new TextBoxPlaceholderBehaviour { Text = "Search..." }.Attach(searchBox);

        _items = GetData();

        var itemList = new ListBox
        {
            Margin = new(0.0, 0.0, 0.0, 0.0)
        };
        itemList.ItemTemplate = TemplateGenerator.CreateDataTemplate(() =>
        {
            var box = new CheckBox();
            box.Height = 25;
            box.VerticalAlignment = VerticalAlignment.Center;
            box.VerticalContentAlignment = VerticalAlignment.Center;
            box.HorizontalAlignment = HorizontalAlignment.Stretch;
            box.Bind<FilterItem, string?>(
                CheckBox.ContentProperty, x => x.Display,
                mode: BindingMode.OneWay);
            box.Bind<FilterItem, bool>(
                CheckBox.IsCheckedProperty, x => x.IsChecked,
                mode: BindingMode.OneWay);
            box.Bind<FilterItem, object?>(CheckBox.TagProperty, x => x);
            box.Checked += Box_Checked;
            box.Unchecked += Box_Unchecked;
            return box;
        });
        itemList.HorizontalContentAlignment = HorizontalAlignment.Stretch;
        itemList.VerticalContentAlignment = VerticalAlignment.Center;
        itemList.ItemsSource = CoreUtils.One(_selectAll).Concat(_items);

        searchBox.TextChanged += (o, e) =>
        {
            itemList.ItemsSource = CoreUtils.One(_selectAll)
                .Concat(_items.Where(x => x.Display.Contains(searchBox.Text, StringComparison.CurrentCultureIgnoreCase)));
        };

        grid.AddChild(searchBox, 0, 0);
        grid.AddChild(itemList, 1, 0);

        return grid;
    }

    private bool _updating = false;

    private void Box_Unchecked(object sender, RoutedEventArgs e)
    {
        if (_updating || sender is not CheckBox box || box.Tag is not FilterItem tag) return;

        if(box.Tag == _selectAll)
        {
            if(SelectedFilters is null || SelectedFilters.Count > 0)
            {
                SelectedFilters = new();

                _updating = true;
                foreach(var item in _items)
                {
                    item.UpdateChecked();
                }
                _selectAll.UpdateChecked();
                _updating = false;

                FilterChanged?.Invoke(this);
            }
        }
        else
        {
            if(SelectedFilters is null)
            {
                SelectedFilters = _items.Select(x => x.Value).ToHashSet();
                SelectedFilters.Remove(tag.Value);

                _updating = true;
                tag.UpdateChecked();
                _selectAll.UpdateChecked();
                _updating = false;

                FilterChanged?.Invoke(this);
            }
            else if(SelectedFilters.Remove(tag.Value))
            {
                _updating = true;
                tag.UpdateChecked();
                _selectAll.UpdateChecked();
                _updating = false;

                FilterChanged?.Invoke(this);
            }
        }
    }

    private void Box_Checked(object sender, RoutedEventArgs e)
    {
        if (_updating || sender is not CheckBox box || box.Tag is not FilterItem tag) return;

        if(tag == _selectAll)
        {
            if(SelectedFilters is not null)
            {
                SelectedFilters = null;

                _updating = true;
                foreach(var item in _items)
                {
                    item.UpdateChecked();
                }
                _selectAll.UpdateChecked();
                _updating = false;

                FilterChanged?.Invoke(this);
            }
        }
        else
        {
            if(SelectedFilters is not null && SelectedFilters.Add(tag.Value))
            {
                _updating = true;
                tag.UpdateChecked();
                _updating = false;
                FilterChanged?.Invoke(this);
            }
        }
    }

    public void ClearFilter()
    {
        if(SelectedFilters is null)
        {
            return;
        }

        SelectedFilters = null;

        _updating = true;
        foreach(var item in _items)
        {
            item.UpdateChecked();
        }
        _selectAll.UpdateChecked();
        _updating = false;

        FilterChanged?.Invoke(this);
    }

    public bool IsFiltered()
    {
        return SelectedFilters is not null;
    }
}

public class FuncCheckBoxDynamicGridColumnFilter(
    Func<CoreRow, Predicate<object?>, bool> filter,
    Func<IEnumerable<Tuple<string, object?>>> data) : CheckBoxDynamicGridColumnFilter
{
    protected override FilterItem[] GetData()
    {
        return data().Select(x => new FilterItem(x.Item2, x.Item1, this)).ToArray();
    }

    public override bool FilterRow(CoreRow row)
    {
        return filter(row, x => SelectedFilters is null || SelectedFilters.Contains(x));
    }
}

public class StaticColumnFilter<T>(
    Func<CoreRow, T> rowData,
    Tuple<string, T>[] data) : CheckBoxDynamicGridColumnFilter
{
    protected override FilterItem[] GetData()
    {
        return data.ToArray(x => new FilterItem(x.Item2, x.Item1, this));
    }

    public override bool FilterRow(CoreRow row)
    {
        return SelectedFilters is null || SelectedFilters.Contains(rowData(row));
    }
}
public class DynamicColumnFilter<T>(
    Func<CoreRow, T> rowData,
    Func<DynamicColumnFilter<T>, IEnumerable<Tuple<string, T>>> data) : CheckBoxDynamicGridColumnFilter
{
    protected override FilterItem[] GetData()
    {
        return data(this).Select(x => new FilterItem(x.Item2, x.Item1, this)).ToArray();
    }

    public override bool FilterRow(CoreRow row)
    {
        return SelectedFilters is null || SelectedFilters.Contains(rowData(row));
    }
}
