@page "/tracks"
@page "/tracks/albums"
@page "/tracks/genres"
@page "/tracks/archive"
@using DeepDrftManager.Services
@using DeepDrftModels.Enums
@inject CmsTrackBrowserViewModel VM
@inject ICmsTrackService CmsTrackService
@inject ISnackbar Snackbar
@inject ILogger<TrackList> Logger
@inject NavigationManager NavigationManager
@attribute [Authorize]

<PageTitle>Tracks — DeepDrft CMS</PageTitle>

<MudContainer MaxWidth="MaxWidth.Large" Class="mt-8">
    <MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Class="mb-4">
        <MudText Typo="Typo.h3">Tracks</MudText>

        @if (VM.Mode == BrowseMode.Tracks)
        {
            <MudButton Variant="Variant.Outlined"
                       Color="Color.Primary"
                       StartIcon="@Icons.Material.Filled.AutoFixHigh"
                       Disabled="@(_bulkRunning || (_grid?.GetMissingCount() ?? 0) == 0)"
                       OnClick="GenerateAllMissingAsync">
                @if (_bulkRunning)
                {
                    <MudProgressCircular Class="mr-2" Size="Size.Small" Indeterminate="true" />
                    <span>Generating @_bulkDone / @_bulkTotal…</span>
                }
                else
                {
                    <span>Generate All Missing (@(_grid?.GetMissingCount() ?? 0))</span>
                }
            </MudButton>
        }
    </MudStack>

    @* Top-level browse dimension. The former three-way toggle (Tracks / Releases / Release Archive)
       collapsed to two (§8.A): "Releases" now hosts the in-page medium tab strip below, subsuming both
       the old Releases grid (as the ALL tab) and the retired Release Archive cards. *@
    <MudToggleGroup T="BrowseMode"
                    Value="VM.Mode"
                    ValueChanged="OnModeChanged"
                    SelectionMode="SelectionMode.SingleSelection"
                    Color="Color.Primary"
                    Size="Size.Small"
                    Class="mb-4">
        <MudToggleItem Value="BrowseMode.Tracks">Tracks</MudToggleItem>
        <MudToggleItem Value="BrowseMode.Albums">Releases</MudToggleItem>
    </MudToggleGroup>

    @if (VM.Mode == BrowseMode.Tracks)
    {
        <CmsTrackGrid @ref="_grid" ShowAddButton="true" PageSize="20" OnStatusLoaded="StateHasChanged" />
    }
    else if (VM.Mode == BrowseMode.Albums)
    {
        @* The Release Archive tab strip (§8.A): an ALL tab plus one tab per ReleaseMedium, ALL left-most.
           The medium tabs are enum-driven — a fourth medium adds a tab automatically; only a label-lookup
           entry (MediumTabLabels) and a content arm (MediumGrid) are needed, no markup fork. Selecting a
           tab swaps the grid below in place; no navigation to a separate page occurs. *@
        @* Medium-aware Add Track (§8.E): the button lives in the tab-strip chrome (not inside any grid
           component — 8.C owns those) and reflects the active tab. It pre-selects the upload form to the
           tab's medium via a single query-param (?medium=…); the ALL tab defaults to Cut. The medium is a
           seed only — the upload form's selector stays user-changeable after landing. *@
        <MudStack Row="true" Justify="Justify.FlexEnd" Class="mb-2">
            <MudButton Variant="Variant.Filled"
                       Color="Color.Primary"
                       StartIcon="@Icons.Material.Filled.Add"
                       Href="@AddTrackHref(ActiveMedium)">
                Add Track
            </MudButton>
        </MudStack>

        <MudTabs Elevation="0" Rounded="true" ApplyEffectsToContainer="true" PanelClass="pt-4"
                 @bind-ActivePanelIndex="_activeTabIndex">
            <MudTabPanel Text="ALL">
                <CmsAllReleasesGrid OnReleasesChanged="OnAlbumsChanged" />
            </MudTabPanel>
            @foreach (var medium in Enum.GetValues<ReleaseMedium>())
            {
                <MudTabPanel Text="@MediumTabLabels[medium]">
                    @MediumGrid(medium)
                </MudTabPanel>
            }
        </MudTabs>
    }
    else
    {
        @* Genre browse keeps its route (/tracks/genres) but lost its tab to Release Archive (§3.1).
           Reachable by direct URL; no longer in the toggle group. *@
        <CmsGenreBrowser Genres="VM.Genres"
                         IsLoading="VM.GenresLoading"
                         ExpandedGenre="@VM.ExpandedGenre"
                         OnExpandedGenreChanged="OnExpandedGenreChanged" />
    }
</MudContainer>

@code {
    private CmsTrackGrid? _grid;

    // Active Release-Archive tab. Panel 0 is ALL; panels 1.. map to Enum.GetValues<ReleaseMedium>() in
    // order. Drives the medium-aware Add Track button (§8.E).
    private int _activeTabIndex;

    // The medium the Add Track button pre-selects for the active tab. ALL (panel 0) defaults to Cut
    // (Daniel, 2026-06-13); each medium tab maps to its enum value by position, so a fourth medium tab
    // gets a correct Add Track for free — no markup fork.
    private ReleaseMedium ActiveMedium =>
        _activeTabIndex <= 0 ? ReleaseMedium.Cut : Enum.GetValues<ReleaseMedium>()[_activeTabIndex - 1];

    // Single query-param convention: the upload page reads ?medium=… and seeds its selector (which stays
    // user-changeable). Always explicit, including ALL→cut, so the link is unambiguous.
    private static string AddTrackHref(ReleaseMedium medium) =>
        $"/tracks/upload?medium={medium.ToString().ToLowerInvariant()}";

    // Medium → tab label. The one place medium display text lives for the tab strip; a future medium adds
    // one entry here and surfaces a tab automatically. Mirrors the extension discipline the retired
    // ReleaseArchiveBrowser used for its cards. The ALL tab is rendered separately (it is not a medium).
    private static readonly IReadOnlyDictionary<ReleaseMedium, string> MediumTabLabels =
        new Dictionary<ReleaseMedium, string>
        {
            [ReleaseMedium.Cut] = "CUTS",
            [ReleaseMedium.Session] = "SESSIONS",
            [ReleaseMedium.Mix] = "MIXES",
        };

    // Medium → embedded grid. Each medium's grid is its own component (Cut has no per-row action; Session
    // carries hero upload; Mix carries waveform generation), so the content dispatch is a per-medium
    // mapping by nature — but it is a single switch returning a fragment, not a markup fork. The browsers
    // render Embedded so their standalone page chrome (container, title, back button) is suppressed here.
    private RenderFragment MediumGrid(ReleaseMedium medium) => medium switch
    {
        ReleaseMedium.Cut => @<CmsCutBrowser />,
        ReleaseMedium.Session => @<CmsSessionBrowser Embedded="true" />,
        ReleaseMedium.Mix => @<CmsMixBrowser Embedded="true" />,
        _ => @<MudText Typo="Typo.body1" Class="mt-4">No grid for this medium.</MudText>
    };

    // The all-releases grid refreshes its own list after a delete; this notification lets us invalidate
    // the VM's genre cache so genre counts reflect the deletion on the next switch into Genre mode.
    private void OnAlbumsChanged()
    {
        VM.Invalidate();
        StateHasChanged();
    }

    // Local state for the parent-owned "Generate All Missing" bulk run.
    private bool _bulkRunning;
    private int _bulkTotal;
    private int _bulkDone;

    protected override async Task OnInitializedAsync()
    {
        // /tracks/archive and /tracks/albums both land on the Releases view (the tab strip); the old
        // separate Archive mode is retired (§8.A) but the route stays reachable rather than 404ing.
        var uri = NavigationManager.Uri;
        var initial =
            uri.Contains("/tracks/albums", StringComparison.OrdinalIgnoreCase) ? BrowseMode.Albums
            : uri.Contains("/tracks/archive", StringComparison.OrdinalIgnoreCase) ? BrowseMode.Albums
            : uri.Contains("/tracks/genres", StringComparison.OrdinalIgnoreCase) ? BrowseMode.Genres
            : BrowseMode.Tracks;
        await VM.SwitchModeAsync(initial);
    }

    private async Task OnModeChanged(BrowseMode mode)
    {
        await VM.SwitchModeAsync(mode);
        var path = mode switch
        {
            BrowseMode.Albums => "/tracks/albums",
            BrowseMode.Genres => "/tracks/genres",
            _ => "/tracks"
        };
        NavigationManager.NavigateTo(path, replace: true);
        StateHasChanged();
    }

    private void OnExpandedGenreChanged(string? genre)
    {
        VM.SetExpandedGenre(genre);
        StateHasChanged();
    }

    /// <summary>
    /// Backfill every track missing a waveform profile, one request at a time so a large backfill
    /// does not flood the API with concurrent WAV decodes. On completion, refreshes the grid's
    /// status map so the per-row icons reflect the new state.
    /// </summary>
    private async Task GenerateAllMissingAsync()
    {
        var statusResult = await CmsTrackService.GetWaveformStatusAsync();
        if (!statusResult.Success || statusResult.Value is null)
        {
            var error = statusResult.Messages.FirstOrDefault()?.Message ?? "Unknown error";
            Snackbar.Add($"Failed to load waveform status: {error}", Severity.Error);
            return;
        }

        var missing = statusResult.Value.Where(s => !s.HasProfile).ToList();
        if (missing.Count == 0)
        {
            return;
        }

        _bulkRunning = true;
        _bulkTotal = missing.Count;
        _bulkDone = 0;
        _grid?.SetBulkRunning(true);
        var failures = 0;

        foreach (var status in missing)
        {
            try
            {
                var result = await CmsTrackService.GenerateWaveformProfileAsync(status.EntryKey);
                if (!result.Success)
                {
                    failures++;
                }
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "Waveform generation failed for {EntryKey}", status.EntryKey);
                failures++;
            }
            _bulkDone++;
            StateHasChanged();
        }

        _bulkRunning = false;
        _grid?.SetBulkRunning(false);

        if (_grid is not null)
        {
            await _grid.RefreshWaveformStatusAsync();
        }

        var succeeded = missing.Count - failures;
        if (failures == 0)
        {
            Snackbar.Add($"Generated {succeeded} profile(s).", Severity.Success);
        }
        else
        {
            Snackbar.Add($"Generated {succeeded} profile(s); {failures} failed.", Severity.Warning);
        }
    }
}
