@page "/tracks/new"
@using System.Security.Claims
@using DeepDrftManager.Services
@using DeepDrftModels.Enums
@attribute [Authorize]

@inject ICmsTrackService CmsTrackService
@inject ICmsReleaseService CmsReleaseService
@inject AuthenticationStateProvider AuthStateProvider
@inject NavigationManager Navigation
@inject ISnackbar Snackbar
@inject ILogger<TrackNew> Logger
@inject CmsTrackBrowserViewModel VM

<PageTitle>Add Track — DeepDrft CMS</PageTitle>

<MudContainer MaxWidth="MaxWidth.Medium" Class="mt-8">
    <MudText Typo="Typo.h4" GutterBottom="true">Add Track</MudText>

    <MudPaper Class="pa-6" Elevation="2">
        <MudStack Spacing="4">
            <MudText Typo="Typo.subtitle1">WAV file</MudText>
            <InputFile OnChange="OnFileSelected" accept=".wav,audio/wav,audio/x-wav" />
            @if (_selectedFile is not null)
            {
                <MudText Typo="Typo.body2">
                    Selected: @_selectedFile.Name (@FormatBytes(_selectedFile.Size))
                </MudText>
            }

            <MudTextField @bind-Value="_trackName" Label="Track Name" Required="true" RequiredError="Track Name is required" Variant="Variant.Outlined" />
            <MudTextField @bind-Value="_artist" Label="Artist" Required="true" RequiredError="Artist is required" Variant="Variant.Outlined" />
            <MudTextField @bind-Value="_album" Label="Album" Variant="Variant.Outlined" />
            <MudTextField @bind-Value="_genre" Label="Genre" Variant="Variant.Outlined" />

            <MediumFields @bind-Medium="_medium" @bind-ReleaseType="_releaseType" Disabled="_isUploading" />

            <MudField Label="Cover Art" Variant="Variant.Outlined" InnerPadding="false">
                <MudStack Spacing="3">
                    @if (_selectedImageFile is { } selectedImage)
                    {
                        <MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
                            <MudText Typo="Typo.body2" Color="Color.Default">Selected: @selectedImage.Name</MudText>
                            <MudIconButton Icon="@Icons.Material.Filled.Clear"
                                           Color="Color.Error"
                                           Size="Size.Small"
                                           Disabled="_isUploading"
                                           OnClick="ClearImage"
                                           aria-label="Cancel image selection" />
                        </MudStack>
                    }
                    else
                    {
                        <MudText Typo="Typo.body2" Color="Color.Default">No cover art — optional.</MudText>
                    }

                    <InputFile OnChange="HandleImageFileSelected" accept="image/*" disabled="@_isUploading" />
                    @if (_selectedImageFile is not null)
                    {
                        <MudText Typo="Typo.caption">Will upload on save.</MudText>
                    }
                </MudStack>
            </MudField>

            <MudTextField @bind-Value="_releaseDate" Label="Release Date (YYYY-MM-DD)" Placeholder="2024-01-15" Variant="Variant.Outlined" />

            @if (!string.IsNullOrEmpty(_errorMessage))
            {
                <MudAlert Severity="Severity.Error">@_errorMessage</MudAlert>
            }

            <MudStack Row="true" Spacing="2" Justify="Justify.FlexEnd">
                <MudButton Variant="Variant.Text"
                           OnClick="Cancel"
                           Disabled="_isUploading">
                    Cancel
                </MudButton>
                <MudButton Variant="Variant.Filled"
                           Color="Color.Primary"
                           OnClick="SubmitAsync"
                           Disabled="_isUploading">
                    @if (_isUploading)
                    {
                        <MudProgressCircular Indeterminate="true" Size="Size.Small" Class="mr-2" />
                        <text>Uploading…</text>
                    }
                    else
                    {
                        <text>Upload</text>
                    }
                </MudButton>
            </MudStack>
        </MudStack>
    </MudPaper>
</MudContainer>

@code {
    // 1 GB ceiling matches DeepDrftAPI's per-request limit on api/track/upload; the
    // streaming path means the limit caps the request, not in-memory buffering.
    private const long MaxUploadBytes = 1_073_741_824L;

    private IBrowserFile? _selectedFile;
    private IBrowserFile? _selectedImageFile;
    private string? _imagePath;
    private string _trackName = string.Empty;
    private string _artist = string.Empty;
    private string _album = string.Empty;
    private string _genre = string.Empty;
    private string _releaseDate = string.Empty;
    private ReleaseType _releaseType = ReleaseType.Single;
    private ReleaseMedium _medium = ReleaseMedium.Cut;
    private string? _errorMessage;
    private bool _isUploading;

    private void OnFileSelected(InputFileChangeEventArgs e)
    {
        _selectedFile = e.File;
        _errorMessage = null;
    }

    private void HandleImageFileSelected(InputFileChangeEventArgs e)
    {
        _selectedImageFile = e.File;
        _imagePath = null;
    }

    private void ClearImage()
    {
        _selectedImageFile = null;
        _imagePath = null;
    }

    private async Task SubmitAsync()
    {
        _errorMessage = null;

        if (_selectedFile is null)
        {
            _errorMessage = "Please select a WAV file.";
            return;
        }

        if (!_selectedFile.Name.EndsWith(".wav", StringComparison.OrdinalIgnoreCase))
        {
            _errorMessage = "Selected file must be a .wav file.";
            return;
        }

        if (string.IsNullOrWhiteSpace(_trackName))
        {
            _errorMessage = "Track Name is required.";
            return;
        }

        if (string.IsNullOrWhiteSpace(_artist))
        {
            _errorMessage = "Artist is required.";
            return;
        }

        if (!string.IsNullOrWhiteSpace(_releaseDate)
            && !DateOnly.TryParseExact(_releaseDate, "yyyy-MM-dd", out _))
        {
            _errorMessage = "Release Date must be in YYYY-MM-DD format.";
            return;
        }

        var authState = await AuthStateProvider.GetAuthenticationStateAsync();
        var userIdValue = authState.User.FindFirstValue(ClaimTypes.NameIdentifier);
        if (!long.TryParse(userIdValue, out var createdByUserId))
        {
            // The page is gated by [HierarchicalRoleAuthorize(Admin)], so a missing or
            // unparseable id here is a configuration bug, not normal client state.
            Logger.LogError("Authenticated user has no parseable NameIdentifier claim: {Value}", userIdValue);
            _errorMessage = "Your session is missing a valid identifier. Please sign in again.";
            return;
        }

        _isUploading = true;
        try
        {
            // Upload any selected cover art first; abort the submit if it fails so we never
            // create a track expecting an image that was never stored in the vault.
            if (_selectedImageFile is { } imgFile)
            {
                await using var imgStream = imgFile.OpenReadStream(maxAllowedSize: 50_000_000);
                var imgResult = await CmsTrackService.UploadImageAsync(imgStream, imgFile.Name, imgFile.ContentType);
                if (!imgResult.Success)
                {
                    var imgError = imgResult.Messages.FirstOrDefault()?.Message ?? "Unknown error";
                    _errorMessage = $"Image upload failed: {imgError}";
                    return;
                }
                _imagePath = imgResult.Value;
            }

            // OpenReadStream streams chunks from the browser via the SignalR circuit; the
            // service wraps it in StreamContent so the whole file is never materialised in
            // memory before DeepDrftAPI receives it.
            await using var fileStream = _selectedFile.OpenReadStream(MaxUploadBytes);

            var result = await CmsTrackService.UploadTrackAsync(
                fileStream,
                _selectedFile.Name,
                _selectedFile.ContentType,
                _trackName,
                _artist,
                string.IsNullOrWhiteSpace(_album) ? null : _album,
                string.IsNullOrWhiteSpace(_genre) ? null : _genre,
                string.IsNullOrWhiteSpace(_releaseDate) ? null : _releaseDate,
                _selectedFile.Name,
                createdByUserId,
                _releaseType,
                trackNumber: 1,
                _medium);

            if (result.Success)
            {
                // Mix uploads fire the server-side high-res waveform trigger (§3.4) — the CMS computes
                // nothing. Non-blocking: a failed trigger is recoverable from the Mixes browser.
                if (_medium == ReleaseMedium.Mix && result.Value?.ReleaseId is { } mixReleaseId)
                {
                    var waveformResult = await CmsReleaseService.GenerateMixWaveformAsync(mixReleaseId);
                    if (!waveformResult.Success)
                    {
                        Logger.LogWarning("TrackNew: mix waveform trigger failed for release {ReleaseId}", mixReleaseId);
                        Snackbar.Add("Mix uploaded, but waveform generation failed. Retry from the Mixes browser.", Severity.Warning);
                    }
                }

                // The upload endpoint does not accept an imagePath, so link the cover art with a
                // follow-up metadata update — same two-step pattern TrackEdit uses.
                if (_imagePath is { } imgPath && result.Value is { } created)
                {
                    var linkResult = await CmsTrackService.UpdateAsync(
                        created.Id,
                        _trackName,
                        _artist,
                        string.IsNullOrWhiteSpace(_album) ? null : _album,
                        string.IsNullOrWhiteSpace(_genre) ? null : _genre,
                        string.IsNullOrWhiteSpace(_releaseDate) ? null : (DateOnly?)DateOnly.ParseExact(_releaseDate, "yyyy-MM-dd"),
                        imgPath);
                    if (!linkResult.Success)
                    {
                        // Track was created; image is in the vault but unlinked. Non-blocking —
                        // the user can attach it via Edit.
                        Snackbar.Add("Track uploaded, but cover art could not be linked. You can add it via Edit.", Severity.Warning);
                    }
                }

                Snackbar.Add($"Uploaded '{_trackName}'.", Severity.Success);
                VM.Invalidate();
                Navigation.NavigateTo("/tracks");
                return;
            }

            var error = result.Messages.FirstOrDefault()?.Message ?? "Unknown error";
            _errorMessage = $"Upload failed: {error}";
            Logger.LogWarning("CMS upload rejected: {Error}", error);
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Upload failed in TrackNew");
            _errorMessage = "Upload failed. Please try again.";
        }
        finally
        {
            _isUploading = false;
        }
    }

    private void Cancel()
    {
        Navigation.NavigateTo("/tracks");
    }

    private static string FormatBytes(long bytes)
    {
        const long KB = 1024;
        const long MB = KB * 1024;
        const long GB = MB * 1024;
        if (bytes >= GB) return $"{bytes / (double)GB:F2} GB";
        if (bytes >= MB) return $"{bytes / (double)MB:F2} MB";
        if (bytes >= KB) return $"{bytes / (double)KB:F2} KB";
        return $"{bytes} bytes";
    }
}
