@implements IDisposable
@using System.Linq
@using RolemasterDb.App.Frontend.AppState
@inject NavigationManager NavigationManager
@inject LookupService LookupService
@inject RolemasterDb.App.Frontend.AppState.PinnedTablesState PinnedTablesState
@inject RolemasterDb.App.Frontend.AppState.RecentTablesState RecentTablesState
@inject RolemasterDb.App.Frontend.AppState.TableContextState TableContextState
@inject RolemasterDb.App.Frontend.AppState.ShellOmniboxState ShellOmniboxState

@if (ShellOmniboxState.IsOpen)
{
    <button
        type="button"
        class="shell-omnibox-backdrop"
        aria-label="Close search panel"
        @onclick="CloseAsync"></button>

    <section class="shell-omnibox-palette" role="dialog" aria-modal="true" aria-label="Search tables and commands">
        <header class="shell-omnibox-header">
            <label class="shell-omnibox-search">
                <span class="visually-hidden">Search tables or commands</span>
                <input
                    @ref="queryInput"
                    class="input-shell shell-omnibox-input"
                    placeholder="Search tables or type /"
                    @bind="query"
                    @bind:event="oninput"
                    @onkeydown="HandleInputKeyDownAsync" />
            </label>

            <button type="button" class="shell-omnibox-close" @onclick="CloseAsync">
                Close
            </button>
        </header>

        <div class="shell-omnibox-body">
            @if (isLoading)
            {
                <InspectorSection Title="Searching" Description="Loading the critical table index and shortcuts.">
                    <StatusIndicator Tone="info">Loading searchable tables…</StatusIndicator>
                </InspectorSection>
            }
            else
            {
                @if (MatchingCommands.Count > 0)
                {
                    <InspectorSection Title="Commands" Description="Navigate directly to key workflows.">
                        <div class="shell-omnibox-results">
                            @foreach (var command in MatchingCommands)
                            {
                                <button type="button" class="shell-omnibox-result" @onclick="() => OpenCommandAsync(command.Href)">
                                    <span class="shell-omnibox-result-main">
                                        <strong>@command.Shortcut</strong>
                                        <span>@command.Description</span>
                                    </span>
                                    <span class="shell-omnibox-result-meta">
                                        <StatusChip Tone="neutral">@command.Label</StatusChip>
                                    </span>
                                </button>
                            }
                        </div>
                    </InspectorSection>
                }

                @if (MatchingPinned.Count > 0)
                {
                    <InspectorSection Title="Pinned tables" Description="Saved shortcuts available across destinations.">
                        <div class="shell-omnibox-results">
                            @foreach (var table in MatchingPinned)
                            {
                                <button type="button" class="shell-omnibox-result" @onclick="() => OpenTableAsync(table.Slug)">
                                    <span class="shell-omnibox-result-main">
                                        <strong>@table.Label</strong>
                                        <span>@table.Family</span>
                                    </span>
                                    <span class="shell-omnibox-result-meta">
                                        <StatusChip Tone="accent">Pinned</StatusChip>
                                    </span>
                                </button>
                            }
                        </div>
                    </InspectorSection>
                }

                @if (MatchingRecent.Count > 0)
                {
                    <InspectorSection Title="Recent tables" Description="Resume recently opened critical tables.">
                        <div class="shell-omnibox-results">
                            @foreach (var table in MatchingRecent)
                            {
                                <button type="button" class="shell-omnibox-result" @onclick="() => OpenTableAsync(table.Slug)">
                                    <span class="shell-omnibox-result-main">
                                        <strong>@table.Label</strong>
                                        <span>@table.Family</span>
                                    </span>
                                    <span class="shell-omnibox-result-meta">
                                        <StatusChip Tone="info">Recent</StatusChip>
                                    </span>
                                </button>
                            }
                        </div>
                    </InspectorSection>
                }

                @if (MatchingTables.Count > 0)
                {
                    <InspectorSection Title="Tables" Description="Open a critical table in reference mode.">
                        <div class="shell-omnibox-results">
                            @foreach (var table in MatchingTables)
                            {
                                <button type="button" class="shell-omnibox-result" @onclick="() => OpenTableAsync(table.Key)">
                                    <span class="shell-omnibox-result-main">
                                        <strong>@table.Label</strong>
                                        <span>@table.Family</span>
                                    </span>
                                    <span class="shell-omnibox-result-meta">
                                        @if (PinnedTablesState.IsPinned(table.Key))
                                        {
                                            <StatusChip Tone="accent">Pinned</StatusChip>
                                        }
                                        <span class="chip">@($"{table.CurationPercentage}%")</span>
                                    </span>
                                </button>
                            }
                        </div>
                    </InspectorSection>
                }

                @if (!HasResults)
                {
                    <InspectorSection Title="No matches" Description="Try a different table name, family, or slash command.">
                        <StatusIndicator Tone="warning">Nothing matches “@query”.</StatusIndicator>
                    </InspectorSection>
                }
            }
        </div>
    </section>
}

@code {
    private static readonly IReadOnlyList<ShellOmniboxCommand> Commands =
    [
        new("/tables", "Reference", "Open the reference tables surface.", "/tables"),
        new("/curation", "Curation", "Open the queue-first curation workflow.", "/curation"),
        new("/tools", "Tools", "Open the developer tools hub.", "/tools"),
        new("/diag", "Diagnostics", "Open tooling diagnostics.", "/tools/diagnostics"),
        new("/api", "API", "Open API surface documentation.", "/tools/api")
    ];

    private ElementReference queryInput;
    private LookupReferenceData? referenceData;
    private bool isLoading;
    private bool shouldFocusInput;
    private string query = string.Empty;

    private bool HasResults =>
        MatchingCommands.Count > 0 ||
        MatchingPinned.Count > 0 ||
        MatchingRecent.Count > 0 ||
        MatchingTables.Count > 0;

    private IReadOnlyList<CriticalTableReference> MatchingTables =>
        referenceData?.CriticalTables
            .Where(MatchesTableQuery)
            .Take(8)
            .ToList()
        ?? [];

    private IReadOnlyList<PinnedTableEntry> MatchingPinned =>
        PinnedTablesState.Items
            .Where(item => MatchesText(item.Label, item.Family, item.Slug))
            .Take(6)
            .ToList();

    private IReadOnlyList<RecentTableEntry> MatchingRecent =>
        RecentTablesState.Items
            .Where(item => MatchesText(item.Label, item.Family, item.Slug))
            .Take(6)
            .ToList();

    private IReadOnlyList<ShellOmniboxCommand> MatchingCommands =>
        Commands
            .Where(command => QueryStartsCommandMode()
                ? command.Shortcut.Contains(query.Trim(), StringComparison.OrdinalIgnoreCase)
                : MatchesText(command.Shortcut, command.Label, command.Description))
            .Take(5)
            .ToList();

    protected override void OnInitialized()
    {
        ShellOmniboxState.Changed += HandleStateChanged;
        NavigationManager.LocationChanged += HandleLocationChanged;
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (shouldFocusInput && ShellOmniboxState.IsOpen)
        {
            shouldFocusInput = false;
            await queryInput.FocusAsync();
        }
    }

    public void Dispose()
    {
        ShellOmniboxState.Changed -= HandleStateChanged;
        NavigationManager.LocationChanged -= HandleLocationChanged;
    }

    private void HandleStateChanged()
    {
        _ = InvokeAsync(HandleStateChangedAsync);
    }

    private async Task HandleStateChangedAsync()
    {
        if (ShellOmniboxState.IsOpen)
        {
            shouldFocusInput = true;
            await EnsureLoadedAsync();
        }
        else
        {
            query = string.Empty;
            shouldFocusInput = false;
        }

        await InvokeAsync(StateHasChanged);
    }

    private void HandleLocationChanged(object? sender, LocationChangedEventArgs args)
    {
        if (!ShellOmniboxState.IsOpen)
        {
            return;
        }

        ShellOmniboxState.Close();
    }

    private async Task EnsureLoadedAsync()
    {
        if (referenceData is not null)
        {
            return;
        }

        isLoading = true;

        try
        {
            await RecentTablesState.InitializeAsync();
            await PinnedTablesState.InitializeAsync();
            referenceData = await LookupService.GetReferenceDataAsync();
        }
        finally
        {
            isLoading = false;
        }
    }

    private Task CloseAsync()
    {
        ShellOmniboxState.Close();
        return Task.CompletedTask;
    }

    private async Task HandleInputKeyDownAsync(KeyboardEventArgs args)
    {
        if (string.Equals(args.Key, "Escape", StringComparison.Ordinal))
        {
            await CloseAsync();
        }
    }

    private async Task OpenTableAsync(string tableSlug)
    {
        var snapshot = new TableContextSnapshot(
            TableSlug: tableSlug,
            Mode: TableContextMode.Reference);

        await TableContextState.PersistAsync("tables", snapshot);
        ShellOmniboxState.Close();
        NavigationManager.NavigateTo(TableContextState.BuildUri("/tables", snapshot));
    }

    private Task OpenCommandAsync(string href)
    {
        ShellOmniboxState.Close();
        NavigationManager.NavigateTo(href);
        return Task.CompletedTask;
    }

    private bool MatchesTableQuery(CriticalTableReference table) =>
        MatchesText(table.Label, table.Key, table.Family, table.SourceDocument);

    private bool MatchesText(params string?[] values)
    {
        var normalizedQuery = query.Trim();
        if (string.IsNullOrWhiteSpace(normalizedQuery))
        {
            return true;
        }

        return values.Any(value =>
            !string.IsNullOrWhiteSpace(value) &&
            value.Contains(normalizedQuery, StringComparison.OrdinalIgnoreCase));
    }

    private bool QueryStartsCommandMode() =>
        query.TrimStart().StartsWith("/", StringComparison.Ordinal);
}
