using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using InABox.Clients;
using InABox.Core;

namespace InABox.DynamicGrid;

public class CodePopupEditorControl : DynamicEditorControl<Guid, CodePopupEditor>
{
    static CodePopupEditorControl()
    {
        //DynamicEditorControlFactory.Register<CodePopupEditorControl, CodePopupEditor>();
    }
    
    private Type _type;
    private Guid _value = Guid.Empty;
    private TextBox Description;
    private TextBox Editor;

    private string origvalue = "";

    public string CodeColumn { get; set; }

    protected override FrameworkElement CreateEditor()
    {
        var Grid = new Grid
        {
            VerticalAlignment = VerticalAlignment.Stretch,
            HorizontalAlignment = HorizontalAlignment.Stretch
        };
        Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(131, GridUnitType.Pixel) });
        Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(19, GridUnitType.Pixel) });
        Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });


        Editor = new TextBox
        {
            CharacterCasing = CharacterCasing.Upper,
            VerticalAlignment = VerticalAlignment.Stretch,
            VerticalContentAlignment = VerticalAlignment.Center,
            HorizontalAlignment = HorizontalAlignment.Stretch
        };
        Editor.BorderThickness = new Thickness(1,1,0,1);
        Editor.BorderBrush = new SolidColorBrush(Colors.Silver);
        Editor.SetValue(Grid.ColumnProperty, 0);
        Editor.SetValue(Grid.RowProperty, 0);

        Editor.PreviewKeyDown += Editor_PreviewKeyDown;
        Editor.GotFocus += Editor_GotFocus;
        Editor.AcceptsTab = true;
        Editor.LostFocus += Editor_LostFocus;
        Grid.Children.Add(Editor);

        var Select = new Button
        {
            VerticalAlignment = VerticalAlignment.Stretch,
            VerticalContentAlignment = VerticalAlignment.Center,
            HorizontalAlignment = HorizontalAlignment.Stretch,
            Content = "..",
            Focusable = false
        };
        Select.BorderThickness = new Thickness(1);
        Select.BorderBrush = new SolidColorBrush(Colors.DimGray);
        Select.SetValue(Grid.ColumnProperty, 1);
        Select.SetValue(Grid.RowProperty, 0);
        Select.Click += Select_Click;
        Grid.Children.Add(Select);

        Description = new TextBox
        {
            VerticalAlignment = VerticalAlignment.Stretch,
            VerticalContentAlignment = VerticalAlignment.Center,
            HorizontalAlignment = HorizontalAlignment.Stretch,
            Margin = new Thickness(5, 0, 0, 0),
            IsReadOnly = true,
            Focusable = false,
            Background = new SolidColorBrush(Colors.WhiteSmoke),
            BorderBrush = new SolidColorBrush(Colors.Silver)
        };
        Description.SetValue(Grid.ColumnProperty, 2);
        Description.SetValue(Grid.RowProperty, 0);
        Grid.Children.Add(Description);

        return Grid;
    }

    private void Editor_GotFocus(object sender, RoutedEventArgs e)
    {
        origvalue = Editor.Text;
    }

    private void Editor_LostFocus(object sender, RoutedEventArgs e)
    {
        if (Editor.Text.Equals(origvalue))
            return;

        var id = _value;
        LookupValue(CodeColumn, Editor.Text);
        if (id == Guid.Empty)
            CheckChanged();
    }


    private void Editor_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key.Equals(Key.Enter) || e.Key.Equals(Key.Tab))
        {
            if (string.IsNullOrEmpty(Editor.Text))
            {
                if (!Editor.Text.Equals(origvalue))
                {
                    LookupValue("ID", Guid.Empty);
                    CheckChanged();
                }

                Editor.MoveFocus(new TraversalRequest(Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)
                    ? FocusNavigationDirection.Previous
                    : FocusNavigationDirection.Next));
            }
            else
            {
                if (!Editor.Text.Equals(origvalue))
                {
                    var code = Editor.Text;
                    LookupValue(CodeColumn, code);
                    if (_value != Guid.Empty)
                    {
                        CheckChanged();
                        Editor.MoveFocus(new TraversalRequest(Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)
                            ? FocusNavigationDirection.Previous
                            : FocusNavigationDirection.Next));
                    }
                    else
                    {
                        if (DoSearch(code))
                            Editor.MoveFocus(new TraversalRequest(Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)
                                ? FocusNavigationDirection.Previous
                                : FocusNavigationDirection.Next));
                    }
                }
                else
                {
                    Editor.MoveFocus(new TraversalRequest(Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)
                        ? FocusNavigationDirection.Previous
                        : FocusNavigationDirection.Next));
                }
            }

            e.Handled = true;
        }
    }

    private void Editor_PreviewKeyUp(object sender, KeyEventArgs e)
    {
    }

    private void Editor_KeyUp(object sender, KeyEventArgs e)
    {
    }

    private void Select_Click(object sender, RoutedEventArgs e)
    {
        DoSearch(Editor.Text);
    }

    private bool DoSearch(string code)
    {
        var result = false;
        if (_type != null)
        {
            var columns = LookupFactory.DefineLookupColumns(Host.GetEditorType(), _type, ColumnName);

            var list = new PopupList(_type, _value,
                columns.ColumnNames().Concat(CoreUtils.One(CodeColumn)).ToArray(),
                new Dictionary<string, string> { { CodeColumn, code } });

            list.OnDefineFilter += t => LookupFactory.DefineLookupFilter(Host.GetEditorType(), t, ColumnName, Host.GetItems());

            list.OnCustomiseGrid += grid =>
            {
                if (EditorDefinition.CanAdd)
                {
                    grid.OnDoubleClick += (o, args) => args.Handled = true; 
                    grid.OnReconfigure += options =>
                    {
                        options.AddRows = Security.CanEdit(_type);
                        options.EditRows = Security.CanEdit(_type);
                        options.EditRows = Security.CanDelete(_type);
                        options.ReadOnly = false;
                    };
                    grid.OnCreateItem += (_, item) =>
                    {
                        LookupFactory.OnCreateItem(Host.GetEditorType(), ColumnName, Host.GetItems(), item);
                    };
                    grid.Reconfigure();
                }
            };

            if (list.ShowDialog() == true)
            {
                result = true;

                _value = list.ID;

                foreach (var col in columns)
                    OtherValues[col.Property] = list.OtherValues[col.Property];

                CheckChanged();

                Editor.Text = string.Format("{0}", list.OtherValues[CodeColumn]);

                var display = new Dictionary<string, object?>();
                foreach (var key in list.OtherValues.Keys.Where(x => !string.Equals(x, CodeColumn)))
                    display[key] = list.OtherValues[key];

                Description.Text =
                    LookupFactory.FormatLookup(_type, display, new[] { CodeColumn });
            }
        }

        return result;
    }

    public override int DesiredHeight()
    {
        return 25;
    }

    public override int DesiredWidth()
    {
        return int.MaxValue;
    }

    protected override Guid RetrieveValue()
    {
        return _value;
    }

    protected override void UpdateValue(Guid value)
    {
        var oldvalue = _value;
        _value = value;
        if (oldvalue != value)
        {
            if (Equals(value, Guid.Empty))
                SetEmptyValue();
            else
                LookupValue("ID", value);
        }
    }

    private void SetEmptyValue()
    {
        Editor.Text = "";
        Description.Text = "";
    }

    private void LookupValue(string column, object value)
    {
        var client = ClientFactory.CreateClient(_type);
        var columns = LookupFactory.DefineLookupColumns(Host.GetEditorType(), _type, ColumnName);
        columns.Add(CodeColumn);

        var sort = LookupFactory.DefineSort(_type);
        var filter = Filter.Create(_type, column).IsEqualTo(value);

        var lookup = client.Query(filter, columns, sort);
        var display = new Dictionary<string,object?>();
        var code = "";
        var id = Guid.Empty;
        var displaycols = LookupFactory.DefineLookupColumns(Host.GetEditorType(), _type, ColumnName).ColumnNames();
        var row = lookup.Rows.FirstOrDefault();
        row ??= lookup.NewRow(true);

        code = row[CodeColumn]?.ToString();
        id = row.Get<Guid>("ID");
        
        foreach (var col in displaycols.Where(x => !x.Equals("ID") && !x.Equals(CodeColumn)))
            display[col] = row[col];

        foreach(var col in columns)
        {
            OtherValues[col.Property] = row[col.Property];
        }
        
        _value = id;
        Editor.Text = code;
        
        Description.Text = LookupFactory.FormatLookup(_type, display, new[] { CodeColumn });
    }

    private string GetCodeColumn(Type type)
    {
        var prop = DatabaseSchema.Properties(type)
            .Where(x => x.Editor is UniqueCodeEditor && !x.HasParentEntityLink())
            .FirstOrDefault() ?? throw new Exception($"No code property for {type.EntityName()}");
        return prop.Name;
    }

    public override void Configure()
    {
        _type = EditorDefinition.Type;

        CodeColumn = !string.IsNullOrEmpty(EditorDefinition.CodeColumn) ? EditorDefinition.CodeColumn : GetCodeColumn(_type);
    }

    public override void SetFocus()
    {
        Editor.Focus();
    }

    public override void SetColor(Color color)
    {
        Editor.Background = IsEnabled ?  new SolidColorBrush(color) : new SolidColorBrush(Colors.WhiteSmoke);
    }
}