<div class="table-scroll">
    <div class="critical-table-grid @BuildGridCssClass()" role="group" aria-label="@Detail.DisplayName" style="@gridTemplateStyle">
        @if (Detail.Groups.Count > 0)
        {
            <div class="critical-table-grid-header-cell critical-table-grid-corner" aria-hidden="true"></div>
            @foreach (var group in visibleGroups)
            {
                <div
                    class="@BuildGroupHeaderCssClass(group.Key)"
                    style="@BuildColumnSpanStyle(visibleColumns.Count)">
                    <span>@group.Label</span>
                </div>
            }
        }

        <div class="critical-table-grid-header-cell critical-table-grid-roll-band-header" aria-hidden="true"></div>
        @foreach (var displayColumn in displayColumns)
        {
            <div class="@BuildColumnHeaderCssClass(displayColumn.GroupKey, displayColumn.ColumnKey)">
                <span>@displayColumn.ColumnLabel</span>
            </div>
        }

        @foreach (var rollBand in Detail.RollBands)
        {
            <div class="@BuildRollBandCssClass(rollBand.Label)">@rollBand.Label</div>
            @foreach (var displayColumn in displayColumns)
            {
                if (TryGetCell(rollBand.Label, displayColumn.GroupKey, displayColumn.ColumnKey, out var resolvedCell) && resolvedCell is not null)
                {
                    var cell = resolvedCell;
                    var isSelectedCell = IsSelectedCell(cell);

                    @if (MatchesModeFilter(cell))
                    {
                        <div class="@GetCellCssClass(cell, displayColumn.GroupKey)" @onclick="() => SelectCell(cell)">
                            <div class="critical-table-cell-shell">
                                <div class="critical-table-cell-actions">
                                    @if (cell.IsCurated)
                                    {
                                        <span class="critical-cell-status-chip is-curated">Curated</span>
                                    }
                                    else
                                    {
                                        <span class="critical-cell-status-chip needs-curation">Needs Curation</span>
                                    }

                                    @if (isSelectedCell)
                                    {
                                        <div class="critical-cell-selected-actions">
                                            @if (!cell.IsCurated)
                                            {
                                                <button
                                                    type="button"
                                                    class="critical-cell-action-button is-curation"
                                                    title="Open the curation preview for this cell."
                                                    @onclick:stopPropagation="true"
                                                    @onclick="() => OnOpenCuration.InvokeAsync(cell.ResultId)">
                                                    Curate
                                                </button>
                                            }

                                            <button
                                                type="button"
                                                class="critical-cell-action-button is-edit"
                                                title="Open the full editor for this cell."
                                                @onclick:stopPropagation="true"
                                                @onclick="() => OnOpenEditor.InvokeAsync(cell.ResultId)">
                                                Edit
                                            </button>
                                        </div>
                                    }
                                </div>

                                <CompactCriticalCell
                                    Description="@(cell.Description ?? string.Empty)"
                                    Effects="@(cell.Effects ?? Array.Empty<CriticalEffectLookupResponse>())"
                                    Branches="@(cell.Branches ?? Array.Empty<CriticalBranchLookupResponse>())" />
                            </div>
                        </div>
                    }
                    else
                    {
                        <div class="critical-table-cell critical-table-cell-empty tables-filtered-cell">
                            <span class="empty-cell">Filtered</span>
                        </div>
                    }
                }
                else
                {
                    <div class="critical-table-cell critical-table-cell-empty">
                        <span class="empty-cell">—</span>
                    </div>
                }
            }
        }
    </div>
</div>

<TablesLegend LegendEntries="@(Detail.Legend ?? Array.Empty<CriticalTableLegendEntry>())" />

@code {
    private readonly Dictionary<(string RollBand, string? GroupKey, string ColumnKey), CriticalTableCellDetail> cellIndex = new();
    private readonly List<(string? GroupKey, string ColumnKey, string ColumnLabel)> displayColumns = new();
    private readonly List<CriticalGroupReference> visibleGroups = new();
    private readonly List<CriticalColumnReference> visibleColumns = new();
    private string gridTemplateStyle = string.Empty;

    [Parameter, EditorRequired]
    public CriticalTableDetail Detail { get; set; } = default!;

    [Parameter]
    public string CurrentMode { get; set; } = TablesReferenceMode.Reference;

    [Parameter]
    public string SelectedGroupKey { get; set; } = string.Empty;

    [Parameter]
    public string SelectedColumnKey { get; set; } = string.Empty;

    [Parameter]
    public string RollJumpValue { get; set; } = string.Empty;

    [Parameter]
    public string DensityMode { get; set; } = TablesDensityMode.Comfortable;

    [Parameter]
    public TablesCellSelection? SelectedCell { get; set; }

    [Parameter]
    public EventCallback<TablesCellSelection> OnSelectCell { get; set; }

    [Parameter]
    public EventCallback<int> OnOpenCuration { get; set; }

    [Parameter]
    public EventCallback<int> OnOpenEditor { get; set; }

    protected override void OnParametersSet()
    {
        cellIndex.Clear();
        displayColumns.Clear();
        visibleGroups.Clear();
        visibleColumns.Clear();

        foreach (var cell in Detail.Cells)
        {
            cellIndex[(cell.RollBand, cell.GroupKey, cell.ColumnKey)] = cell;
        }

        if (Detail.Groups.Count == 0)
        {
            foreach (var column in Detail.Columns.Where(MatchesColumnFilter))
            {
                visibleColumns.Add(column);
                displayColumns.Add((null, column.Key, column.Label));
            }
        }
        else
        {
            foreach (var group in Detail.Groups.Where(MatchesGroupFilter))
            {
                visibleGroups.Add(group);
            }

            foreach (var column in Detail.Columns.Where(MatchesColumnFilter))
            {
                visibleColumns.Add(column);
            }

            foreach (var group in visibleGroups)
            {
                foreach (var column in visibleColumns)
                {
                    displayColumns.Add((group.Key, column.Key, column.Label));
                }
            }
        }

        var dataColumnCount = displayColumns.Count;
        gridTemplateStyle = $"grid-template-columns: max-content repeat({dataColumnCount}, minmax(0, 1fr));";
    }

    private bool TryGetCell(string rollBand, string? groupKey, string columnKey, out CriticalTableCellDetail? cell) =>
        cellIndex.TryGetValue((rollBand, groupKey, columnKey), out cell);

    private bool MatchesGroupFilter(CriticalGroupReference group) =>
        string.IsNullOrWhiteSpace(SelectedGroupKey)
        || string.Equals(group.Key, SelectedGroupKey, StringComparison.OrdinalIgnoreCase);

    private bool MatchesColumnFilter(CriticalColumnReference column) =>
        string.IsNullOrWhiteSpace(SelectedColumnKey)
        || string.Equals(column.Key, SelectedColumnKey, StringComparison.OrdinalIgnoreCase);

    private bool MatchesModeFilter(CriticalTableCellDetail cell) =>
        CurrentMode switch
        {
            TablesReferenceMode.NeedsCuration => !cell.IsCurated,
            TablesReferenceMode.Curated => cell.IsCurated,
            _ => true
        };

    private string? ActiveRollBand =>
        !string.IsNullOrWhiteSpace(SelectedCell?.RollBand)
            ? SelectedCell.RollBand
            : ResolveRollJumpBandLabel();

    private string? ActiveColumnKey =>
        !string.IsNullOrWhiteSpace(SelectedCell?.ColumnKey)
            ? SelectedCell.ColumnKey
            : (!string.IsNullOrWhiteSpace(SelectedColumnKey) ? SelectedColumnKey : null);

    private string? ActiveGroupKey =>
        !string.IsNullOrWhiteSpace(SelectedCell?.GroupKey)
            ? SelectedCell.GroupKey
            : (!string.IsNullOrWhiteSpace(SelectedGroupKey) ? SelectedGroupKey : null);

    private string BuildGridCssClass() =>
        string.Equals(DensityMode, TablesDensityMode.Dense, StringComparison.Ordinal)
            ? "is-dense"
            : "is-comfortable";

    private string BuildGroupHeaderCssClass(string groupKey)
    {
        var classes = new List<string> { "critical-table-grid-header-cell", "critical-table-grid-group-header" };
        if (string.Equals(groupKey, ActiveGroupKey, StringComparison.OrdinalIgnoreCase))
        {
            classes.Add("is-active-group");
        }

        return string.Join(' ', classes);
    }

    private string BuildColumnHeaderCssClass(string? groupKey, string columnKey)
    {
        var classes = new List<string> { "critical-table-grid-header-cell", "critical-table-grid-column-header" };

        if (string.Equals(columnKey, ActiveColumnKey, StringComparison.OrdinalIgnoreCase))
        {
            classes.Add("is-active-column");
        }

        if (!string.IsNullOrWhiteSpace(groupKey) &&
            string.Equals(groupKey, ActiveGroupKey, StringComparison.OrdinalIgnoreCase))
        {
            classes.Add("is-active-group");
        }

        return string.Join(' ', classes);
    }

    private string BuildRollBandCssClass(string rollBandLabel)
    {
        var classes = new List<string> { "critical-table-grid-header-cell", "critical-table-grid-roll-band" };
        if (string.Equals(rollBandLabel, ActiveRollBand, StringComparison.OrdinalIgnoreCase))
        {
            classes.Add("is-active-row");
        }

        if (string.Equals(rollBandLabel, ResolveRollJumpBandLabel(), StringComparison.OrdinalIgnoreCase))
        {
            classes.Add("is-roll-target");
        }

        return string.Join(' ', classes);
    }

    private string GetCellCssClass(CriticalTableCellDetail cell, string? groupKey)
    {
        var classes = new List<string>
        {
            "critical-table-cell",
            cell.IsCurated ? "is-curated" : "needs-curation"
        };

        if (string.Equals(cell.RollBand, ActiveRollBand, StringComparison.OrdinalIgnoreCase))
        {
            classes.Add("is-active-row");
        }

        if (string.Equals(cell.ColumnKey, ActiveColumnKey, StringComparison.OrdinalIgnoreCase))
        {
            classes.Add("is-active-column");
        }

        if (!string.IsNullOrWhiteSpace(groupKey) &&
            string.Equals(groupKey, ActiveGroupKey, StringComparison.OrdinalIgnoreCase))
        {
            classes.Add("is-active-group");
        }

        if (SelectedCell is not null && cell.ResultId == SelectedCell.ResultId)
        {
            classes.Add("is-selected-cell");
        }

        if (string.Equals(cell.RollBand, ResolveRollJumpBandLabel(), StringComparison.OrdinalIgnoreCase))
        {
            classes.Add("is-roll-target");
        }

        return string.Join(' ', classes);
    }

    private string? ResolveRollJumpBandLabel()
    {
        if (!int.TryParse(RollJumpValue, out var targetRoll))
        {
            return null;
        }

        foreach (var rollBand in Detail.RollBands)
        {
            if (targetRoll < rollBand.MinRoll)
            {
                continue;
            }

            if (rollBand.MaxRoll is null || targetRoll <= rollBand.MaxRoll.Value)
            {
                return rollBand.Label;
            }
        }

        return null;
    }

    private Task SelectCell(CriticalTableCellDetail cell) =>
        OnSelectCell.InvokeAsync(new TablesCellSelection(cell.ResultId, cell.RollBand, cell.ColumnKey, cell.GroupKey));

    private bool IsSelectedCell(CriticalTableCellDetail cell) =>
        SelectedCell is not null && cell.ResultId == SelectedCell.ResultId;

    private static string BuildColumnSpanStyle(int span) => $"grid-column: span {span};";
}
