using System;
using System.Collections.Generic;
using System.Linq;
using InABox.Clients;
using InABox.Core;

namespace InABox.DynamicGrid;

public interface IDynamicEditorHost
{
    /// <summary>
    /// Trigger the loading of the lookup values; the canonical implementation calls <see cref="ILookupEditor.Values(string, object[]?)"/>,
    /// and calls (either sync/async) <see cref="ILookupEditorControl.LoadLookups(CoreTable)"/> when the values are loaded.
    /// </summary>
    /// <param name="editor">The editor to load the lookups for.</param>
    void LoadLookups(ILookupEditorControl editor);

    /// <summary>
    /// Get a document for a given filename.
    /// </summary>
    /// <remarks>
    /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
    /// </remarks>
    /// <param name="filename">The filename of the document.</param>
    /// <returns>The document with the right filename, or <see langword="null"/> if not found.</returns>
    Document? FindDocument(string filename)
    {
        return new Client<Document>().Load(new Filter<Document>(x => x.FileName).IsEqualTo(filename)).FirstOrDefault();
    }

    /// <summary>
    /// Get a document for a given ID.
    /// </summary>
    /// <remarks>
    /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
    /// </remarks>
    /// <param name="id">The ID of the document.</param>
    /// <returns>The document, or <see langword="null"/> if not found.</returns>
    Document? GetDocument(Guid id)
    {
        return new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(id)).FirstOrDefault();
    }

    /// <summary>
    /// Saves a document.
    /// </summary>
    /// <remarks>
    /// The usual implementation will go through the <see cref="Client{TEntity}"/> interface.
    /// </remarks>
    /// <param name="document">The document to save.</param>
    void SaveDocument(Document document)
    {
        new Client<Document>().Save(document, "Updated by Editor");
    }

    /// <summary>
    /// Returns a list of the currently edited items; may be an empty array.
    /// 
    /// This should probably always be a <see cref="BaseObject"/>[].
    /// </summary>
    /// <returns>The items being edited.</returns>
    BaseObject[] GetItems();

    /// <summary>
    /// Return the type of the edited object.
    /// </summary>
    /// <returns></returns>
    Type GetEditorType();

    /// <summary>
    /// Um... I'm really not sure; achieves the same function as <see cref="DynamicEditorGrid.OnGetEditor"/> - if you know what that does, good job.
    /// </summary>
    /// <remarks>
    /// I think you should be fine to just return <paramref name="column"/>.Editor, as defined by <see cref="DynamicGridColumn"/>.
    /// </remarks>
    /// <param name="column">The column to get the editor of.</param>
    /// <returns>The editor, or <see langword="null"/> if it doesn't exist.</returns>
    BaseEditor? GetEditor(DynamicGridColumn column);
}

public class DefaultDynamicEditorHost<T> : IDynamicEditorHost
    where T : BaseObject
{
    public virtual T[]? Items { get; set; }

    public BaseEditor? GetEditor(DynamicGridColumn column) => column.Editor.CloneEditor();

    public BaseObject[] GetItems() => Items ?? Array.Empty<BaseObject>();

    public Type GetEditorType() => typeof(T);

    public void LoadLookups(ILookupEditorControl sender)
    {
        var editor = sender.EditorDefinition as ILookupEditor;
        var colname = sender.ColumnName;

        var values = editor.Values(colname, Items);
        sender.LoadLookups(values);
    }

}