using InABox.Core;
using InABox.DynamicGrid;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InABox.Wpf.Dashboard.Editor;

internal class DynamicDashboardDataQueryEditItem : BaseObject
{
    // Having to do custom OnPropertyChanged stuff because Fody isn't allowed in this project.
    private Type? _type;
    [ComboLookupEditor(typeof(TypeLookupGenerator))]
    [EditorSequence(1)]
    public Type? Type
    {
        get => _type;
        set
        {
            var oldValue = _type;
            _type = value;
            OnPropertyChanged(nameof(Type), oldValue, value);
        }
    }

    [EditorSequence(2)]
    public string Name { get; set; } = "";

    [FilterEditor]
    [EditorSequence(3)]
    public string Filter { get; set; } = "";

    [ColumnsEditor]
    [EditorSequence(4)]
    public string Columns { get; set; } = "";

    [SortOrderEditor]
    [EditorSequence(5)]
    public string SortOrder { get; set; } = "";

    public DynamicDashboardDataQueryEditItem()
    {
    }

    public DynamicDashboardDataQueryEditItem(IDynamicDashboardDataQuery query)
    {
        Name = query.Key;
        Type = query.Type;
        Filter = Serialization.Serialize(query.Filter);
        Columns = Serialization.Serialize(query.Columns);
        SortOrder = Serialization.Serialize(query.SortOrder);
    }

    private class TypeLookupGenerator : LookupGenerator<DynamicDashboardDataQueryEditItem>
    {
        public TypeLookupGenerator(DynamicDashboardDataQueryEditItem[]? items) : base(items)
        {
        }

        protected override void DoGenerateLookups()
        {
            foreach(var entity in CoreUtils.Entities.Where(x => x.IsSubclassOf(typeof(Entity)) && x.HasInterface<IRemotable>()).OrderBy(x => x.Name))
            {
                AddValue(entity, entity.Name);
            }
        }
    }

    protected override void DoPropertyChanged(string name, object? before, object? after)
    {
        base.DoPropertyChanged(name, before, after);

        if(name == nameof(Type))
        {
            Filter = "";
            Columns = "";
            SortOrder = "";
            if (Type is not null && (Name.IsNullOrWhiteSpace() || Name == (before as Type)?.Name))
            {
                Name = Type.Name;
            }
        }
    }

    public IDynamicDashboardDataQuery? ToQuery()
    {
        if(Type is not null)
        {
            var query = (Activator.CreateInstance(typeof(DynamicDashboardDataQuery<>).MakeGenericType(Type)) as IDynamicDashboardDataQuery)!;
            query.Key = Name;
            query.Filter = Serialization.Deserialize(typeof(Filter<>).MakeGenericType(Type), Filter) as IFilter;
            query.Columns = (Serialization.Deserialize(typeof(Columns<>).MakeGenericType(Type), Columns) as IColumns)
                ?? Core.Columns.None(Type);
            query.SortOrder = Serialization.Deserialize(typeof(SortOrder<>).MakeGenericType(Type), SortOrder) as ISortOrder;
            return query;
        }
        else
        {
            return null;
        }
    }
}

internal class DynamicDashboardDataQueryGrid : DynamicItemsListGrid<DynamicDashboardDataQueryEditItem>
{
    protected override void Init()
    {
        base.Init();

        ActionColumns.Add(new DynamicTextColumn(GetText)
        {
            HeaderText = "Table"
        });

        HiddenColumns.Add(x => x.Type);
    }

    private object? GetText(CoreRow? row)
    {
        return row?.Get<DynamicDashboardDataQueryEditItem, Type?>(x => x.Type)?.Name ?? "";
    }

    protected override void DoValidate(DynamicDashboardDataQueryEditItem[] items, List<string> errors)
    {
        base.DoValidate(items, errors);

        foreach(var item in items)
        {
            if(Items.Any(x => x != item && x.Name == item.Name))
            {
                errors.Add($"Duplicate key '{item.Name}'");
            }
        }
    }

    protected override void DoReconfigure(DynamicGridOptions options)
    {
        base.DoReconfigure(options);

        options.AddRows = true;
        options.EditRows = true;
        options.DeleteRows = true;
    }

    private readonly Column<DynamicDashboardDataQueryEditItem> FilterColumn = new(x => x.Filter);
    private readonly Column<DynamicDashboardDataQueryEditItem> ColumnsColumn = new(x => x.Columns);
    private readonly Column<DynamicDashboardDataQueryEditItem> SortOrderColumn = new(x => x.SortOrder);
    private readonly Column<DynamicDashboardDataQueryEditItem> TypeColumn = new(x => x.Type);

    protected override void CustomiseEditor(IDynamicEditorForm form, DynamicDashboardDataQueryEditItem[] items, DynamicGridColumn column, BaseEditor editor)
    {
        base.CustomiseEditor(form, items, column, editor);

        var item = items.First();

        if(FilterColumn.IsEqualTo(column.ColumnName) && editor is FilterEditor filterEditor)
        {
            filterEditor.Type = item.Type;
        }
        else if(ColumnsColumn.IsEqualTo(column.ColumnName) && editor is ColumnsEditor columnsEditor)
        {
            columnsEditor.Type = item.Type;
        }
        else if(SortOrderColumn.IsEqualTo(column.ColumnName) && editor is SortOrderEditor sortEditor)
        {
            sortEditor.Type = item.Type;
        }
    }

    protected override Dictionary<string, object?> EditorValueChanged(IDynamicEditorForm editor, DynamicDashboardDataQueryEditItem[] items, string name, object value)
    {
        var changed = base.EditorValueChanged(editor, items, name, value);
        if (TypeColumn.IsEqualTo(name))
        {
            if(value is Type type)
            {
                if(editor.TryFindEditor<FilterEditorControl>(FilterColumn.Property, out var filterEditor))
                {
                    filterEditor.FilterType = type;
                }
                if(editor.TryFindEditor<ColumnsEditorControl>(ColumnsColumn.Property, out var columnsEditor))
                {
                    columnsEditor.ColumnsType = type;
                }
            }
        }
        return changed;
    }
}
