<section class="tables-index-rail" aria-labelledby="tables-index-heading">
    <div class="tables-index-rail-header">
        <h2 id="tables-index-heading" class="tables-index-title">Table Index</h2>
        <p class="tables-index-copy">Choose a table, then read from the roll band across to the result you need.</p>
    </div>

    <div class="tables-index-controls" @onkeydown="HandleRailKeyDown">
        <label class="tables-index-search-label" for="tables-index-search">Search tables</label>
        <input
            id="tables-index-search"
            class="input-shell tables-index-search"
            type="search"
            placeholder="Search tables"
            value="@searchText"
            @oninput="HandleSearchInput" />

        @if (familyFilters.Count > 1)
        {
            <div class="tables-family-filters" aria-label="Filter table families">
                @foreach (var family in familyFilters)
                {
                    var isAllFilter = string.IsNullOrEmpty(family);
                    var label = isAllFilter ? "All families" : family;

                    <button
                        type="button"
                        class="@GetFamilyFilterCssClass(family)"
                        aria-pressed="@IsFamilyFilterSelected(family)"
                        @onclick="() => SelectFamilyFilter(family)">
                        @label
                    </button>
                }
            </div>
        }
    </div>

    @if (pinnedTables.Count > 0)
    {
        <div class="tables-index-section">
            <div class="tables-index-section-header">
                <h3>Pinned</h3>
                <span class="tables-index-section-count">@pinnedTables.Count</span>
            </div>
            <div class="tables-index-list" role="listbox" aria-label="Pinned critical tables">
                @foreach (var table in pinnedTables)
                {
                    @RenderTableOption(table)
                }
            </div>
        </div>
    }

    @if (recentTables.Count > 0)
    {
        <div class="tables-index-section">
            <div class="tables-index-section-header">
                <h3>Recent</h3>
                <span class="tables-index-section-count">@recentTables.Count</span>
            </div>
            <div class="tables-index-list" role="listbox" aria-label="Recent critical tables">
                @foreach (var table in recentTables)
                {
                    @RenderTableOption(table)
                }
            </div>
        </div>
    }

    <div class="tables-index-section">
        <div class="tables-index-section-header">
            <h3>All tables</h3>
            <span class="tables-index-section-count">@filteredTables.Count</span>
        </div>

        @if (filteredTables.Count == 0)
        {
            <p class="tables-index-empty">No tables match the current search.</p>
        }
        else
        {
            <div class="tables-index-list" role="listbox" aria-label="Critical tables">
                @foreach (var table in filteredTables)
                {
                    @RenderTableOption(table)
                }
            </div>
        }
    </div>
</section>

@code {
    private readonly List<string> familyFilters = new();
    private readonly List<CriticalTableReference> filteredTables = new();
    private readonly List<CriticalTableReference> pinnedTables = new();
    private readonly List<CriticalTableReference> recentTables = new();
    private readonly List<CriticalTableReference> keyboardOptions = new();
    private string searchText = string.Empty;
    private string selectedFamily = string.Empty;
    private string? activeOptionSlug;

    [Parameter]
    public IReadOnlyList<CriticalTableReference> Tables { get; set; } = Array.Empty<CriticalTableReference>();

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

    [Parameter]
    public IReadOnlyList<string> PinnedTableSlugs { get; set; } = Array.Empty<string>();

    [Parameter]
    public IReadOnlyList<string> RecentTableSlugs { get; set; } = Array.Empty<string>();

    [Parameter]
    public Func<string, bool>? IsPinned { get; set; }

    [Parameter]
    public EventCallback<string> OnSelectTable { get; set; }

    protected override void OnParametersSet()
    {
        BuildFamilyFilters();
        BuildSections();
        EnsureActiveOption();
    }

    private bool GetIsPinned(string tableSlug) => IsPinned?.Invoke(tableSlug) ?? false;

    private void BuildFamilyFilters()
    {
        familyFilters.Clear();
        familyFilters.Add(string.Empty);

        foreach (var family in Tables
                     .Select(table => table.Family)
                     .Where(family => !string.IsNullOrWhiteSpace(family))
                     .Distinct(StringComparer.OrdinalIgnoreCase)
                     .OrderBy(family => family, StringComparer.OrdinalIgnoreCase))
        {
            familyFilters.Add(family);
        }
    }

    private void BuildSections()
    {
        filteredTables.Clear();
        pinnedTables.Clear();
        recentTables.Clear();
        keyboardOptions.Clear();

        foreach (var table in Tables)
        {
            if (!MatchesFilters(table))
            {
                continue;
            }

            filteredTables.Add(table);
        }

        foreach (var slug in PinnedTableSlugs)
        {
            var table = filteredTables.FirstOrDefault(item => string.Equals(item.Key, slug, StringComparison.OrdinalIgnoreCase));
            if (table is not null)
            {
                pinnedTables.Add(table);
            }
        }

        foreach (var slug in RecentTableSlugs)
        {
            var table = filteredTables.FirstOrDefault(item => string.Equals(item.Key, slug, StringComparison.OrdinalIgnoreCase));
            if (table is not null && pinnedTables.All(item => !string.Equals(item.Key, table.Key, StringComparison.OrdinalIgnoreCase)))
            {
                recentTables.Add(table);
            }
        }

        foreach (var table in pinnedTables)
        {
            AddKeyboardOption(table);
        }

        foreach (var table in recentTables)
        {
            AddKeyboardOption(table);
        }

        foreach (var table in filteredTables)
        {
            AddKeyboardOption(table);
        }
    }

    private void AddKeyboardOption(CriticalTableReference table)
    {
        if (keyboardOptions.Any(item => string.Equals(item.Key, table.Key, StringComparison.OrdinalIgnoreCase)))
        {
            return;
        }

        keyboardOptions.Add(table);
    }

    private bool MatchesFilters(CriticalTableReference table)
    {
        if (!string.IsNullOrEmpty(selectedFamily) &&
            !string.Equals(table.Family, selectedFamily, StringComparison.OrdinalIgnoreCase))
        {
            return false;
        }

        if (string.IsNullOrWhiteSpace(searchText))
        {
            return true;
        }

        return table.Label.Contains(searchText, StringComparison.OrdinalIgnoreCase)
            || table.Key.Contains(searchText, StringComparison.OrdinalIgnoreCase)
            || table.Family.Contains(searchText, StringComparison.OrdinalIgnoreCase);
    }

    private void HandleSearchInput(ChangeEventArgs args)
    {
        searchText = args.Value?.ToString()?.Trim() ?? string.Empty;
        BuildSections();
        EnsureActiveOption();
    }

    private void SelectFamilyFilter(string family)
    {
        selectedFamily = family;
        BuildSections();
        EnsureActiveOption();
    }

    private bool IsFamilyFilterSelected(string family) =>
        string.Equals(selectedFamily, family, StringComparison.OrdinalIgnoreCase);

    private string GetFamilyFilterCssClass(string family) =>
        IsFamilyFilterSelected(family)
            ? "tables-family-filter is-selected"
            : "tables-family-filter";

    private void HandleRailKeyDown(KeyboardEventArgs args)
    {
        if (keyboardOptions.Count == 0)
        {
            return;
        }

        if (string.Equals(args.Key, "ArrowDown", StringComparison.Ordinal))
        {
            MoveActiveOption(1);
            return;
        }

        if (string.Equals(args.Key, "ArrowUp", StringComparison.Ordinal))
        {
            MoveActiveOption(-1);
            return;
        }

        if (string.Equals(args.Key, "Enter", StringComparison.Ordinal) && !string.IsNullOrWhiteSpace(activeOptionSlug))
        {
            _ = OnSelectTable.InvokeAsync(activeOptionSlug);
        }
    }

    private void MoveActiveOption(int offset)
    {
        if (keyboardOptions.Count == 0)
        {
            activeOptionSlug = null;
            return;
        }

        var currentIndex = keyboardOptions.FindIndex(item => string.Equals(item.Key, activeOptionSlug, StringComparison.OrdinalIgnoreCase));
        if (currentIndex < 0)
        {
            currentIndex = keyboardOptions.FindIndex(item => string.Equals(item.Key, SelectedTableSlug, StringComparison.OrdinalIgnoreCase));
        }

        var nextIndex = currentIndex < 0
            ? 0
            : Math.Clamp(currentIndex + offset, 0, keyboardOptions.Count - 1);

        activeOptionSlug = keyboardOptions[nextIndex].Key;
    }

    private void EnsureActiveOption()
    {
        if (keyboardOptions.Count == 0)
        {
            activeOptionSlug = null;
            return;
        }

        if (!string.IsNullOrWhiteSpace(activeOptionSlug) &&
            keyboardOptions.Any(item => string.Equals(item.Key, activeOptionSlug, StringComparison.OrdinalIgnoreCase)))
        {
            return;
        }

        if (!string.IsNullOrWhiteSpace(SelectedTableSlug))
        {
            var selectedOption = keyboardOptions.FirstOrDefault(item => string.Equals(item.Key, SelectedTableSlug, StringComparison.OrdinalIgnoreCase));
            if (selectedOption is not null)
            {
                activeOptionSlug = selectedOption.Key;
                return;
            }
        }

        activeOptionSlug = keyboardOptions[0].Key;
    }

    private string GetCurationTone(CriticalTableReference table) =>
        table.CurationPercentage >= 100 ? "success" : "warning";

    private string GetTableOptionCssClass(CriticalTableReference table)
    {
        var classes = new List<string>();

        if (string.Equals(table.Key, SelectedTableSlug, StringComparison.OrdinalIgnoreCase))
        {
            classes.Add("is-selected");
        }

        if (string.Equals(table.Key, activeOptionSlug, StringComparison.OrdinalIgnoreCase))
        {
            classes.Add("is-active");
        }

        classes.Add(table.CurationPercentage >= 100 ? "is-curated" : "needs-curation");
        return string.Join(' ', classes);
    }

    private void SetActiveOption(string tableSlug) => activeOptionSlug = tableSlug;

    private RenderFragment RenderTableOption(CriticalTableReference table) => @<button
        type="button"
        role="option"
        aria-selected="@string.Equals(table.Key, SelectedTableSlug, StringComparison.OrdinalIgnoreCase)"
        class="table-index-option @GetTableOptionCssClass(table)"
        @onfocus="() => SetActiveOption(table.Key)"
        @onclick="() => OnSelectTable.InvokeAsync(table.Key)">
        <span class="table-index-option-copy">
            <strong class="table-index-option-title">@table.Label</strong>
            <span class="table-index-option-meta">@table.Family</span>
        </span>
        <span class="table-index-option-chips">
            @if (GetIsPinned(table.Key))
            {
                <StatusChip Tone="accent">Pinned</StatusChip>
            }
            <StatusChip Tone="@GetCurationTone(table)">@($"{table.CurationPercentage}%")</StatusChip>
        </span>
    </button>;
}
