@using DeepDrftPublic.Client.Common
@inject SeoOptions Seo
@inject SeoEnvironment SeoEnv
@inject NavigationManager Nav

@*
   The single reusable SEO head surface (Phase 22). Presentational and parameter-fed — owns no fetch and
   no business logic (C4); it reads the injected SeoOptions for defaults and NavigationManager for the
   current path. Renders <PageTitle> (the sole title source — pages drop their bare <PageTitle>) plus a
   <HeadContent> block carrying the full standard/OG/Twitter/JSON-LD surface (§3), projected into the
   <HeadOutlet> in App.razor so it is present in the prerendered HTML a crawler sees (C2/AC1).

   Identical output across the InteractiveAuto double render (AC6): every value comes from the parameter
   Model (built from the page's bridged PersistentComponentState) and config — never a browser API — so
   the prerender and WASM passes render byte-identical tags.

   Partial data (C6/AC4): a missing value falls back to config or omits its tag; og:image always resolves
   (the default guarantees presence) so there is never a content="" attribute or a broken node.
*@

<PageTitle>@_fullTitle</PageTitle>

<HeadContent>
    @* Standard / search *@
    <meta name="description" content="@_description" />
    <link rel="canonical" href="@_canonical" />
    <meta name="robots" content="@_robots" />
    <meta name="application-name" content="@Seo.SiteName" />

    @* Open Graph *@
    <meta property="og:title" content="@Model.Title" />
    <meta property="og:description" content="@_description" />
    <meta property="og:url" content="@_canonical" />
    <meta property="og:type" content="@_ogType" />
    <meta property="og:site_name" content="@Seo.SiteName" />
    <meta property="og:locale" content="@Seo.Locale" />
    <meta property="og:image" content="@_image" />
    @if (_hasCover)
    {
        <meta property="og:image:alt" content="@($"{Model.Title} cover art")" />
    }

    @* Music-vertical OG (release pages only) *@
    @if (!string.IsNullOrWhiteSpace(Model.Artist))
    {
        <meta property="music:musician" content="@Model.Artist" />
    }
    @if (Model.ReleaseDate is not null)
    {
        <meta property="music:release_date" content="@Model.ReleaseDate.Value.ToString("yyyy-MM-dd")" />
    }
    @if (_isoDuration is not null)
    {
        <meta property="music:duration" content="@_isoDuration" />
    }

    @* Twitter Card. No twitter:site / twitter:creator — no X account exists (OQ3). *@
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:title" content="@Model.Title" />
    <meta name="twitter:description" content="@_description" />
    <meta name="twitter:image" content="@_image" />

    @* JSON-LD structured data *@
    @if (!string.IsNullOrEmpty(Model.JsonLd))
    {
        <script type="application/ld+json">@((MarkupString)Model.JsonLd)</script>
    }
</HeadContent>

@code {
    /// <summary>The page's resolved SEO input, built via a <see cref="SeoModel"/> factory.</summary>
    [Parameter, EditorRequired] public required SeoModel Model { get; set; }

    private string _fullTitle = string.Empty;
    private string _description = string.Empty;
    private string _canonical = string.Empty;
    private string _robots = string.Empty;
    private string _ogType = "website";
    private string _image = string.Empty;
    private bool _hasCover;
    private string? _isoDuration;

    protected override void OnParametersSet()
    {
        _fullTitle = $"{Model.Title} · {Seo.TitleSuffix}";
        _description = string.IsNullOrWhiteSpace(Model.Description) ? Seo.DefaultDescription : Model.Description;
        // Default robots is environment-gated (non-production → noindex,nofollow) so beta/staging is never
        // crawled; an explicit per-page Robots still wins (e.g. the 404's / soft-404's noindex,follow).
        _robots = string.IsNullOrWhiteSpace(Model.Robots) ? SeoEnv.DefaultRobots : Model.Robots;
        _ogType = OgTypeString(Model.OgType);

        // Canonical: BaseUrl + the model's path, defaulting to the current relative path. The origin is
        // always config (no browser API) so prerender and WASM agree (§5).
        var path = Model.CanonicalPath ?? RelativePath();
        _canonical = SeoUrls.Absolute(Seo, path);

        _hasCover = !string.IsNullOrWhiteSpace(Model.ImagePath);
        _image = SeoUrls.CoverOrDefault(Seo, Model.ImagePath);
        _isoDuration = SeoUrls.IsoDuration(Model.DurationSeconds);
    }

    private string RelativePath()
    {
        var path = Nav.ToBaseRelativePath(Nav.Uri);
        var query = path.IndexOf('?');
        if (query >= 0) path = path[..query];
        return "/" + path;
    }

    private static string OgTypeString(SeoOgType type) => type switch
    {
        SeoOgType.MusicAlbum => "music.album",
        SeoOgType.MusicSong => "music.song",
        _ => "website",
    };
}
