using DbFirst.BlazorWebApp.Models.Grid;
using DbFirst.BlazorWebApp.Services;
using DevExpress.Blazor;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;

namespace DbFirst.BlazorWebApp.Components;

public abstract class BandGridBase<TItem> : ComponentBase
{
    [Inject] protected BandLayoutService BandLayoutService { get; set; } = default!;

    // --- Abstract: jedes Grid definiert diese selbst ---
    protected abstract string LayoutKey { get; }
    protected abstract List<ColumnDefinition> ColumnDefinitions { get; }

    // --- Band-Layout Felder ---
    protected BandLayout bandLayout = new();
    protected Dictionary<string, string> columnBandAssignments = new();
    protected List<BandOption> bandOptions = new();
    protected Dictionary<string, ColumnDefinition> columnLookup = new();
    protected string? layoutUser;
    protected bool gridLayoutApplied;
    protected IGrid? gridRef;

    // --- SizeMode ---
    protected SizeMode _sizeMode = SizeMode.Medium;
    protected static readonly List<SizeMode> _sizeModes = Enum.GetValues<SizeMode>().ToList();

    private const string LayoutType = "GRID_BANDS";

    // --- Lifecycle ---
    protected async Task InitializeBandLayoutAsync()
    {
        columnLookup = ColumnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase);
        layoutUser = await BandLayoutService.EnsureLayoutUserAsync();
        bandLayout = await BandLayoutService.LoadBandLayoutAsync(LayoutType, LayoutKey, layoutUser, columnLookup);
        columnBandAssignments = BandLayoutService.BuildAssignmentsFromLayout(bandLayout);
        ApplyColumnLayoutFromStorage();
        _sizeMode = bandLayout.SizeMode;
        UpdateBandOptions();
    }

    protected async Task ApplyGridLayoutAfterRenderAsync()
    {
        if (!gridLayoutApplied && gridRef != null && bandLayout.GridLayout != null)
        {
            gridRef.LoadLayout(bandLayout.GridLayout);
            gridLayoutApplied = true;
            await InvokeAsync(StateHasChanged);
        }
    }

    // --- Layout speichern / zurücksetzen ---
    protected async Task SaveLayoutAsync()
    {
        if (string.IsNullOrWhiteSpace(layoutUser)) return;
        CaptureColumnLayoutFromGrid();
        await BandLayoutService.SaveBandLayoutAsync(LayoutType, LayoutKey, layoutUser, bandLayout);
    }

    protected async Task ResetLayoutAsync()
    {
        if (string.IsNullOrWhiteSpace(layoutUser)) return;
        await BandLayoutService.ResetBandLayoutAsync(LayoutType, LayoutKey, layoutUser);
        bandLayout = new BandLayout();
        columnBandAssignments.Clear();
        UpdateBandOptions();
        foreach (var column in ColumnDefinitions)
            column.Width = null;
        columnLookup = ColumnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase);
        _sizeMode = SizeMode.Medium;
        gridRef?.LoadLayout(new GridPersistentLayout());
        gridLayoutApplied = false;
    }

    private void CaptureColumnLayoutFromGrid()
    {
        if (gridRef == null) return;
        var layout = gridRef.SaveLayout();
        bandLayout.GridLayout = layout;
        bandLayout.SizeMode = _sizeMode;
        var orderedColumns = layout.Columns
            .Where(c => !string.IsNullOrWhiteSpace(c.FieldName))
            .OrderBy(c => c.VisibleIndex)
            .ToList();
        bandLayout.ColumnOrder = orderedColumns.Select(c => c.FieldName).ToList();
        bandLayout.ColumnWidths = orderedColumns
            .Where(c => !string.IsNullOrWhiteSpace(c.Width))
            .ToDictionary(c => c.FieldName, c => c.Width, StringComparer.OrdinalIgnoreCase);
    }

    private void ApplyColumnLayoutFromStorage()
    {
        foreach (var column in ColumnDefinitions)
        {
            if (bandLayout.ColumnWidths.TryGetValue(column.FieldName, out var width) && !string.IsNullOrWhiteSpace(width))
                column.Width = width;
        }
        columnLookup = ColumnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase);
    }

    // --- Band-Methoden ---
    protected bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser);

    protected virtual bool ShowCommandColumn => true;

    protected void AddBand()
    {
        bandLayout.Bands.Add(new BandDefinition { Id = Guid.NewGuid().ToString("N"), Caption = "Band" });
        UpdateBandOptions();
    }

    protected void RemoveBand(BandDefinition band)
    {
        bandLayout.Bands.Remove(band);
        foreach (var key in columnBandAssignments.Where(p => p.Value == band.Id).Select(p => p.Key).ToList())
            columnBandAssignments.Remove(key);
        UpdateBandOptions();
        SyncBandsFromAssignments();
    }

    protected void UpdateBandCaption(BandDefinition band, string value)
    {
        band.Caption = value;
        UpdateBandOptions();
    }

    protected void UpdateColumnBand(string fieldName, string? bandId)
    {
        if (string.IsNullOrWhiteSpace(bandId))
            columnBandAssignments.Remove(fieldName);
        else
            columnBandAssignments[fieldName] = bandId;
        SyncBandsFromAssignments();
    }

    protected string GetColumnBand(string fieldName)
        => columnBandAssignments.TryGetValue(fieldName, out var bandId) ? bandId : string.Empty;

    protected void SyncBandsFromAssignments()
    {
        foreach (var band in bandLayout.Bands)
        {
            band.Columns = ColumnDefinitions
                .Where(c => columnBandAssignments.TryGetValue(c.FieldName, out var id) && id == band.Id)
                .Select(c => c.FieldName)
                .ToList();
        }
        StateHasChanged();
    }

    protected void UpdateBandOptions()
    {
        bandOptions = new List<BandOption> { new() { Id = string.Empty, Caption = "Ohne Band" } };
        bandOptions.AddRange(bandLayout.Bands.Select(b => new BandOption { Id = b.Id, Caption = b.Caption }));
    }

    // --- SizeMode ---
    protected string FormatSizeText(SizeMode size) => size switch
    {
        SizeMode.Small => "Klein",
        SizeMode.Medium => "Mittel",
        SizeMode.Large => "Groß",
        _ => size.ToString()
    };

    protected void OnSizeChange(DropDownButtonItemClickEventArgs args)
    {
        _sizeMode = Enum.Parse<SizeMode>(args.ItemInfo.Id);
    }

    // --- RenderColumns / BuildDataColumn ---
    protected RenderFragment RenderColumns() => builder =>
    {
        var seq = 0;
        if (ShowCommandColumn)
        {
            builder.OpenComponent<DxGridCommandColumn>(seq++);
            builder.AddAttribute(seq++, "Width", "120px");
            builder.CloseComponent();
        }

        var grouped = bandLayout.Bands.SelectMany(b => b.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase);
        foreach (var column in ColumnDefinitions.Where(c => !grouped.Contains(c.FieldName)))
            BuildDataColumn(builder, ref seq, column);

        foreach (var band in bandLayout.Bands)
        {
            if (band.Columns.Count == 0) continue;
            builder.OpenComponent<DxGridBandColumn>(seq++);
            builder.AddAttribute(seq++, "Caption", band.Caption);
            builder.AddAttribute(seq++, "Columns", (RenderFragment)(bandBuilder =>
            {
                var bandSeq = 0;
                foreach (var columnName in band.Columns)
                {
                    if (columnLookup.TryGetValue(columnName, out var column))
                        BuildDataColumn(bandBuilder, ref bandSeq, column);
                }
            }));
            builder.CloseComponent();
        }
    };

    protected void BuildDataColumn(RenderTreeBuilder builder, ref int seq, ColumnDefinition column)
    {
        builder.OpenComponent<DxGridDataColumn>(seq++);
        builder.AddAttribute(seq++, "FieldName", column.FieldName);
        builder.AddAttribute(seq++, "Caption", column.Caption);
        if (!string.IsNullOrWhiteSpace(column.Width))
            builder.AddAttribute(seq++, "Width", column.Width);
        if (!string.IsNullOrWhiteSpace(column.DisplayFormat))
            builder.AddAttribute(seq++, "DisplayFormat", column.DisplayFormat);
        if (column.ReadOnly)
            builder.AddAttribute(seq++, "ReadOnly", true);
        builder.CloseComponent();
    }
}