using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Radzen.Blazor.Rendering;

#nullable enable
using Radzen.Documents.Spreadsheet;
namespace Radzen.Blazor.Spreadsheet;

/// <summary>
/// Renders an inline cell editor for a spreadsheet.
/// </summary>
public partial class CellEditor : ComponentBase, IDisposable
{
    /// <summary>
    /// Gets or sets the sheet.
    /// </summary>
    [Parameter]
    public Worksheet Worksheet { get; set; } = default!;

    /// <summary>
    /// Gets or sets the editor.
    /// </summary>
    [Parameter]
    public Editor Editor { get; set; } = default!;

    /// <summary>
    /// Gets or sets the virtual grid context.
    /// </summary>
    [Parameter]
    public IVirtualGridContext Context { get; set; } = default!;

    /// <summary>
    /// Gets or sets the spreadsheet instance that contains this cell editor.
    /// </summary>
    [CascadingParameter]
    public ISpreadsheet Spreadsheet { get; set; } = default!;

    /// <summary>
    /// Gets or sets the registered custom cell types.
    /// </summary>
    [CascadingParameter]
    public Dictionary<string, SpreadsheetCellType>? CellTypes { get; set; }

    private string? cellStyle;
    private string? editorStyle;
    private string? className;
    private Type? customEditorType;

    private readonly EventBinding<Editor> editorBinding;

    /// <summary>
    /// Initializes a new instance of the <see cref="CellEditor"/> class.
    /// </summary>
    public CellEditor()
    {
        editorBinding = new EventBinding<Editor>(
            e =>
            {
                e.Changed += OnEditModeChanged;
                e.ValueChanged += OnEditorValueChanged;
            },
            e =>
            {
                e.Changed -= OnEditModeChanged;
                e.ValueChanged -= OnEditorValueChanged;
            });
    }

    /// <inheritdoc />
    protected override void OnParametersSet()
    {
        editorBinding.Bind(Worksheet is not null ? Editor : null);

        if (Worksheet is not null)
        {
            Render();
        }
    }

    private async Task OnBlurAsync()
    {
        if (Editor.Mode == EditMode.Cell)
        {
            await Spreadsheet.AcceptAsync();
        }
    }

    private void OnFocus()
    {
        if (Worksheet.Selection.Cell != CellRef.Invalid)
        {
            var cell = Worksheet.Cells[Worksheet.Selection.Cell];
            Editor.StartEdit(cell.Address, Editor.Mode != EditMode.None ? Editor.Value : cell.GetValue(), EditMode.Cell);
        }
    }

    private void OnEditorValueChanged()
    {
        if (Editor.Mode == EditMode.Formula)
        {
            StateHasChanged();
        }
    }

    private void Render()
    {
        var address = Worksheet.Selection.Cell;

        if (address == CellRef.Invalid)
        {
            cellStyle = null;
            className = "rz-spreadsheet-cell-editor";
            return;
        }

        var cell = Worksheet.Cells[address];

        if (address != CellRef.Invalid)
        {
            var rect = Context.GetRectangle(address.Row, address.Column);
            var sb = StringBuilderCache.Acquire();
            rect.AppendStyle(sb);

            cell = Worksheet.Cells[address];

            cellStyle = StringBuilderCache.GetStringAndRelease(sb);

            var fontSb = StringBuilderCache.Acquire();
            cell.GetEffectiveFormat()?.AppendFontStyle(fontSb);
            var fontStyle = StringBuilderCache.GetStringAndRelease(fontSb);
            editorStyle = fontStyle.Length > 0 ? fontStyle : null;
        }
        else
        {
            cellStyle = null;
        }

        className = ClassList.Create("rz-spreadsheet-cell-editor")
            .Add("rz-spreadsheet-frozen-column", Worksheet.Selection.Cell != CellRef.Invalid && Worksheet.Selection.Cell.Column < Worksheet.Columns.Frozen)
            .Add("rz-spreadsheet-frozen-row", Worksheet.Selection.Cell != CellRef.Invalid && Worksheet.Selection.Cell.Row < Worksheet.Rows.Frozen)
            .Add($"rz-spreadsheet-cell-editor-{cell.ValueType.ToString().ToLowerInvariant()}", cell is not null)
            .ToString();
    }

    private Type? ResolveEditorType()
    {
        if (CellTypes is not { Count: > 0 } || Worksheet is null)
        {
            return null;
        }

        var address = Worksheet.Selection.Cell;

        if (address == CellRef.Invalid)
        {
            return null;
        }

        var typeName = Worksheet.Cells.GetCustomType(address.Row, address.Column);

        if (typeName is not null && CellTypes.TryGetValue(typeName, out var cellType))
        {
            return cellType.EditorType;
        }

        return null;
    }

    private Dictionary<string, object> GetEditorParameters()
    {
        var address = Worksheet.Selection.Cell;
        var cell = address != CellRef.Invalid ? Worksheet.Cells[address] : null;
        var formattedValue = cell?.Value?.ToString();

        var context = new SpreadsheetCellEditContext(
            formattedValue, cell!, Worksheet, Editor, Spreadsheet
        );

        return new Dictionary<string, object>
        {
            { "Context", context }
        };
    }

    private void OnEditModeChanged()
    {
        Render();
        customEditorType = ResolveEditorType();
        StateHasChanged();
    }

    void IDisposable.Dispose()
    {
        editorBinding.Dispose();
    }
}