using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using InABox.Clients;
using InABox.Configuration;
using InABox.Core;
using InABox.Wpf;
using Syncfusion.Data;
using Button = System.Windows.Controls.Button;

namespace InABox.DynamicGrid;

public interface IMultiSelectDialog
{
    bool ShowDialog(String? column = null, String? filter = null, FilterType filtertype = FilterType.Contains);
    Guid[] IDs();
    CoreTable Data();

    Entity[] Items(IColumns? columns = null);
    bool CanAdd { get; set; }
}

public class MultiSelectDialogSettings : BaseObject, IUserConfigurationSettings
{
    public DynamicGridSelectedFilterSettings Filters { get; set; } = new();
}

/// <summary>
/// Represents a dialog to select <typeparamref name="T"/>(s), given a filter and a set of columns. It can either do multi-selecting or single-selecting.
/// </summary>
/// <remarks>
/// This is the standard way to do a selection dialog. To access all selected IDs, use <see cref="IDs"/>; to access the data according to the columns
/// provided in the constructor, use <see cref="Data"/>; use <see cref="Items"/> if you want to load all the items with the selected IDs with a custom
/// set of columns.
/// </remarks>
public class MultiSelectDialog<T> : IMultiSelectDialog where T : Entity, IRemotable, IPersistent, new()
{
    //private Expression<Func<T, object>>[] _columns = new Expression<Func<T, object>>[] { };
    private readonly Columns<T>? _columns;

    private readonly Filter<T>? _filter;
    private readonly DynamicDataGrid<T> datagrid;
    //private readonly Grid grid;
    private readonly Button CancelButton;
    private readonly Button OKButton;
    private ThemableWindow? window;
    
    public bool CanAdd { get; set; } = true;
    
    public MultiSelectDialog(Filter<T>? filter, Columns<T>? columns, bool multiselect = true)
    {
        
        _filter = filter;

        //grid = new Grid();
        //grid.Margin = new Thickness(5);
        //grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1.0F, GridUnitType.Star) });
        //grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(40.0F, GridUnitType.Pixel) });

        //grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(80.0F, GridUnitType.Pixel) });
        //grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1.0F, GridUnitType.Star) });
        //grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(80.0F, GridUnitType.Pixel) });
        //grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(80.0F, GridUnitType.Pixel) });

        datagrid = new DynamicDataGrid<T>();
        datagrid.Margin = new Thickness(5);
        
        datagrid.Reconfigure(options =>
        {
            options.BeginUpdate();
            options.Clear();
            options.SelectColumns = true;
            options.FilterRows = true;
            if (multiselect)
                options.MultiSelect = true;
            options.AddRows = CanAdd;
            options.RecordCount = true;
            options.EndUpdate();
        });
        datagrid.Reconfigure();
        OKButton = datagrid.AddButton("OK", null, OKClick, DynamicGridButtonPosition.Right);
        OKButton.Width = 80;
        OKButton.IsEnabled = false;
        
        CancelButton = datagrid.AddButton("Cancel", null, CancelClick, DynamicGridButtonPosition.Right);
        CancelButton.Width = 80;
        
        datagrid.OnReload += Grid_OnReload;
        datagrid.OnDoubleClick += Grid_DoubleClick;
        datagrid.ColumnsTag = "MSD";
        if (columns != null)
        {
            _columns = columns;
            foreach (var column in columns)
                datagrid.AddHiddenColumn(column.Property);
            //datagrid.HiddenColumns.AddRange(columns);
        }
        else
        {
            var cols = LookupFactory.DefineColumns<T>()
                .AddColumns(ColumnTypeFlags.IncludeOptional | ColumnTypeFlags.IncludeForeignKeys | ColumnTypeFlags.IncludeLinked);
            foreach (var col in cols)
                datagrid.AddHiddenColumn(col.ToString());
        }

        var _settings = new UserConfiguration<MultiSelectDialogSettings>(datagrid.GetTag()).Load();
        datagrid.FilterComponent.SetSettings(_settings.Filters,false);
        datagrid.FilterComponent.OnFiltersSelected += (settings) =>
        {
            var _settings = new MultiSelectDialogSettings() { Filters = settings };
            new UserConfiguration<MultiSelectDialogSettings>(datagrid.GetTag()).Save(_settings);
        };
        
        datagrid.SetValue(Grid.RowProperty, 0);
        datagrid.SetValue(Grid.ColumnProperty, 0);
        datagrid.SetValue(Grid.ColumnSpanProperty, 4);
        datagrid.OnSelectItem += Datagrid_OnSelectItem;
        //grid.Children.Add(datagrid);
        
        // ClearButton = new Button();
        // ClearButton.Margin = new Thickness(0, 5, 5, 0);
        // ClearButton.Content = "Clear";
        // ClearButton.Click += ClearButton_Click;
        // ClearButton.SetValue(Grid.RowProperty, 1);
        // ClearButton.SetValue(Grid.ColumnProperty, 0);
        // grid.Children.Add(ClearButton);
        //
        // OKButton = new Button();
        // OKButton.Margin = new Thickness(5, 5, 0, 0);
        // OKButton.Content = "OK";
        // OKButton.Click += OKButton_Click;
        // OKButton.SetValue(Grid.RowProperty, 1);
        // OKButton.SetValue(Grid.ColumnProperty, 2);
        // OKButton.IsEnabled = false;
        // grid.Children.Add(OKButton);
        //
        // var CancelButton = new Button();
        // CancelButton.Margin = new Thickness(5, 5, 0, 0);
        // CancelButton.Content = "Cancel";
        // CancelButton.Click += CancelButton_Click;
        // CancelButton.SetValue(Grid.RowProperty, 1);
        // CancelButton.SetValue(Grid.ColumnProperty, 3);
        // grid.Children.Add(CancelButton);
    }

    private bool OKClick(Button button, CoreRow[] rows)
    {
        window!.DialogResult = true;
        window!.Close();
        return false;
    }
    
    private bool CancelClick(Button button, CoreRow[] rows)
    {
        window!.DialogResult = false;
        window!.Close();
        return false;
    }

    public bool ShowDialog(string? column = null, string? value = null, FilterType filtertype = FilterType.Contains) =>
        ShowDialogInternal(null, column, value, filtertype);
    public bool ShowDialog(string title) =>
        ShowDialogInternal(title, null, null, default);

    private bool ShowDialogInternal(string? title, string? column, string? value, FilterType filtertype)
    {
        window = new ThemableWindow
        {
            Title = title ?? "Select Items",
            WindowStyle = WindowStyle.SingleBorderWindow,
            Content = datagrid
        };
        datagrid.Refresh(true, true);
        if (!column.IsNullOrWhiteSpace() && !value.IsNullOrWhiteSpace())
            datagrid.AddVisualFilter(column, value, filtertype);
        if (window.ShowDialog() == true)
            return true;
        return false;
    }

    private Window GetWindow([CallerMemberName] string methodName = "")
    {
        return window ?? throw new Exception($"Must call ShowDialog() before {methodName}()");
    }

    public Guid[] IDs()
    {
        var window = GetWindow();
        if (window.DialogResult == true)
        {
            return datagrid.SelectedRows.ToArray(r => r.Get<T, Guid>(x => x.ID));
        }
        else if (window.DialogResult == false)
        {
            return [Guid.Empty];
        }
        else
        {
            return Array.Empty<Guid>();
        }
    }

    public CoreTable Data()
    {
        var window = GetWindow();

        var result = new CoreTable();
        result.Columns.Add(new CoreColumn { ColumnName = "ID", DataType = typeof(Guid) });
        if(_columns is not null)
        {
            foreach (var column in _columns.Where(x => !string.Equals(x.Property, "ID")))
                result.Columns.Add(new CoreColumn { ColumnName = column.Property, DataType = column.Type });
        }

        if (window.DialogResult == true)
        {

            if (datagrid?.Data != null && datagrid.SelectedRows.Length != 0)
                foreach (var sel in datagrid.SelectedRows)
                {
                    var row = result.NewRow();
                    foreach (var column in result.Columns)
                    {
                        var value = sel[column.ColumnName];
                        row.Set(column.ColumnName, value);
                    }
                    result.Rows.Add(row);
                }
        }
        else if (window.DialogResult == false)
        {
            var row = result.NewRow(true);
            result.Rows.Add(row);
        }

        return result;
    }
    
    public T[] Items(Columns<T>? columns = null)
    {
        var window = GetWindow();

        if (window.DialogResult == true)
        {
            var ids = datagrid.SelectedRows.ToArray(r => r.Get<T, Guid>(x => x.ID));
            if (ids.Length > 0)
            {
                return new Client<T>().Query(new Filter<T>(x => x.ID).InList(ids), columns).ToArray<T>();
            }
        }
        else if (window.DialogResult == false)
            return [new T()];

        return [];
    }

    Entity[] IMultiSelectDialog.Items(IColumns? columns) => Items(columns as Columns<T>);

    private void Grid_DoubleClick(object sender, HandledEventArgs args)
    {
        args.Handled = true;
        window!.DialogResult = true;
        window.Close();
    }

    private void Datagrid_OnSelectItem(object sender, DynamicGridSelectionEventArgs e)
    {
        OKButton.IsEnabled = e.Rows?.Any() == true;
    }

    // private void CancelButton_Click(object sender, RoutedEventArgs e)
    // {
    //     window!.DialogResult = false;
    //     window!.Close();
    // }
    //
    // private void OKButton_Click(object sender, RoutedEventArgs e)
    // {
    //     window!.DialogResult = true;
    //     window.Close();
    // }
    //
    // private void ClearButton_Click(object sender, RoutedEventArgs e)
    // {
    //     window!.DialogResult = false;
    //     window.Close();
    // }

    private void Grid_OnReload(object sender, Filters<T> criteria, Columns<T> columns, ref SortOrder<T>? sortby)
    {
        if (_filter != null)
            criteria.Add(_filter);
    }

    public static bool SelectItem([NotNullWhen(true)] out T? item, Filter<T>? filter = null, Columns<T>? columns = null, string? title = null)
    {
        var dlg = new MultiSelectDialog<T>(filter, columns, multiselect: false);
        if (dlg.ShowDialogInternal(title, null, null, default))
        {
            item = dlg.Data().ToObjects<T>().FirstOrDefault();
            return item is not null;
        }
        else
        {
            item = null;
            return false;
        }
    }

    public static bool SelectItems([NotNullWhen(true)] out T[]? items, Filter<T>? filter = null, Columns<T>? columns = null, string? title = null)
    {
        var dlg = new MultiSelectDialog<T>(filter, columns, multiselect: true);
        if (dlg.ShowDialogInternal(title, null, null, default))
        {
            items = dlg.Data().ToArray<T>();
            return true;
        }
        else
        {
            items = null;
            return false;
        }
    }
}

public static class MultiSelectDialog
{
    public static bool SelectItem<T>([NotNullWhen(true)] out T? item, Filter<T>? filter = null, Columns<T>? columns = null, string? title = null)
        where T : Entity, IRemotable, IPersistent, new()
    {
        return MultiSelectDialog<T>.SelectItem(out item, filter: filter, columns: columns, title: title);
    }

    public static bool SelectItems<T>([NotNullWhen(true)] out T[]? items, Filter<T>? filter = null, Columns<T>? columns = null, string? title = null)
        where T : Entity, IRemotable, IPersistent, new()
    {
        return MultiSelectDialog<T>.SelectItems(out items, filter: filter, columns: columns, title: title);
    }
}