<script>
    /* The whole discography — the desktop's CatalogDiscography.svelte, which
       the artist page reaches with "All" on its Discography head.

       The desktop's page is a stack of album-page heroes — the album page's
       own hero at `--cover-scale: 0.45`, a 108px cover, with the record's
       whole tracklist under it, fetched as the section scrolls near and its
       accent sampled from its own cover (CatalogDiscography.svelte:120-175,
       1555-1560). On a 411px screen that is one record per screenful and one
       `catalog_album` per row over mobile data, for rows that are a tap away
       on the record page — so none of those three comes over. What does is
       the four things the page has that the artist page's tabs do not: All,
       a sort, a grouping, and the editions kept whole.

       Filed by the same classifier the artist page uses. The state — tab,
       order, grouping — lives on the nav entry, so it survives a push and
       the sort sheet's `patch()` reaches it the way Library's does. */
    import { untrack } from "svelte";
    import Icon from "../../components/Icon.svelte";
    import Cover from "../Cover.svelte";
    import { art, wantArt } from "../art.svelte.js";
    import { albumsBy } from "../lib.svelte.js";
    import { push, patch, openHalf } from "../nav.svelte.js";
    import { catalog, lookupArtist, artistKey, yearOf, looseTitle, picture } from "../catalog.svelte.js";
    import { discoTabs, allTabs, orderFamilies, byYear, ORDER_LABEL } from "../disco.js";
    import { CAT_ONE } from "../../lib/localReleaseType.js";
    import { editionLabel } from "../../lib/editions.js";

    let { entry, screenEl = null } = $props();

    const name = $derived(entry.c.name);
    /* normally warm — the artist page that linked here looked the name up
       already — but a page that only works warm is a page that breaks the
       day something else links to it. `lookupArtist` is its own guard. */
    $effect(() => { const n = name, sd = entry.c.seed ?? null; untrack(() => lookupArtist(n, sd)); });

    const cat = $derived(catalog.artists[artistKey(name)] ?? null);
    const page = $derived(cat?.page ?? null);
    const artistId = $derived(page?.artist?.id ?? cat?.id ?? null);
    const overview = $derived(artistId != null ? catalog.overviews[artistId] ?? null : null);
    const shownName = $derived(page?.artist?.name || overview?.artist?.name || name);
    /* the exhaustive page when it is here, the overview's first round until
       then — never the other way round, the artist page's own rule */
    const discoAll = $derived(page?.albums ?? overview?.albums ?? []);

    /* ── the tabs, the sort, the grouping ────────────────────────────── */
    const catTabs = $derived(discoTabs(discoAll));
    const tabs = $derived(allTabs(catTabs));
    const tab = $derived(tabs.some((t) => t.key === entry.c.tab) ? entry.c.tab : (tabs[0]?.key ?? "all"));
    const order = $derived(entry.c.order || "new");
    /* Under a name sort the years would repeat out of order, so By year is
       not offered there and the grouping falls to none. The sheet says why. */
    const byName = $derived(order === "az" || order === "za");
    const grouped = $derived(!byName && (entry.c.group ?? "year") === "year");

    const active = $derived(tabs.find((t) => t.key === tab) ?? null);
    const items = $derived(orderFamilies(active?.items ?? [], order));
    /* the desktop's header line: a record with no track count makes the
       whole figure a floor, not a number */
    const known = $derived(items.reduce((n, r) => n + (r.track_count || 0), 0));
    const someUnknown = $derived(items.some((r) => r.track_count == null));
    /* which category each family was filed under, read off the per-category
       tabs rather than by asking the classifier again once per row */
    const kinds = $derived.by(() => {
        const m = new Map();
        for (const t of catTabs) for (const f of t.items) m.set(f.familyKey ?? f.key ?? f.id, CAT_ONE[t.key] ?? t.label);
        return m;
    });

    /* the cap. A prolific artist is hundreds of rows and the phone renders
       every one of them if it is asked to — Browse's rule (Browse.svelte:51):
       a slice that extends on demand, reset whenever the list underneath it
       changes. It is taken BEFORE the grouping, so the last year on screen
       can be a part of itself and the button says how much is left. */
    const CAP = 60;
    let cap = $state(CAP);
    $effect(() => { void tab; void order; void grouped; cap = CAP; });
    const groups = $derived(byYear(items.slice(0, cap), grouped));

    /* ── what this phone already holds ───────────────────────────────── */
    const albums = $derived(albumsBy(name));
    const inLib = (a) => albums.find((x) => looseTitle(x.title) === looseTitle(a.title)) || null;
    const familyLib = (f) => (f.editions || [f]).map(inLib).find(Boolean) || null;
    const familyKeyOf = (f) => f.familyKey ?? f.key ?? f.id;
    const subOf = (f, l) => [
        yearOf(f.release_date),
        kinds.get(familyKeyOf(f)) || "",
        f.track_count ? `${f.track_count} tracks` : "",
        l ? "in your library" : "",
    ].filter(Boolean).join(" · ");

    const picUrl = $derived(picture(page?.artist?.picture) || picture(cat?.seed?.picture) || null);
    const seedPath = $derived(albums.find((a) => a.artist_art_path)?.artist_art_path || albums[0]?.art_path || null);
    $effect(() => { if (seedPath) wantArt(seedPath); });
    const porSrc = $derived(picUrl || (seedPath ? art[seedPath] : "") || "");

    /* ── opening things ──────────────────────────────────────────────── */
    function openRow(r) {
        const l = inLib(r);
        if (l) push("album", { id: l.id }, screenEl);
        else push("catalogAlbum", { id: r.id, artist: shownName, title: r.title }, screenEl);
    }
    function openFamily(f) {
        const l = familyLib(f);
        if (l) push("album", { id: l.id }, screenEl);
        else push("catalogAlbum", { id: f.id, artist: shownName, title: f.title }, screenEl);
    }
    /* a long press is the record sheet the grid's already opens — Play, Save,
       Add to playlist, Go to record, Go to artist. No new actions here. */
    const moreFor = (f) => openHalf("catalogAlbum", { album: { ...(f.editions?.[0] ?? f), artist: shownName } });

    /* the editions opened in place, under their own record — Browse's folder
       and its files, rather than a rail that scrolls sideways inside a list
       that scrolls down */
    let openEds = $state(new Set());
    function toggleEds(key) {
        const s = new Set(openEds);
        if (s.has(key)) s.delete(key); else s.add(key);
        openEds = s;
    }
</script>

<div class="dg-head">
    <span class="dg-por">
        {#if porSrc}<img src={porSrc} alt="" />{:else}<Icon name="users" size={22} />{/if}
    </span>
    <div class="b">
        <div class="t">{shownName}</div>
        <div class="m">{items.length} release{items.length === 1 ? "" : "s"} · {someUnknown ? "≥" : ""}{known} tracks</div>
    </div>
</div>

{#if tabs.length > 1}
    <div class="segtabs dg-tabs" role="tablist" aria-label="Discography">
        {#each tabs as t (t.key)}
            <button class:on={tab === t.key} onclick={() => patch({ tab: t.key })} role="tab" aria-selected={tab === t.key} aria-label={t.label + ", " + t.items.length}>{t.label}<span class="count" aria-hidden="true">{t.items.length}</span></button>
        {/each}
    </div>
{/if}

{#if items.length}
    <div class="sec dg-sec">
        <h2>{active?.label ?? "All"} <em>· {items.length} release{items.length === 1 ? "" : "s"}</em></h2>
        <button class="all" onclick={() => openHalf("discosort")} aria-label="Sort and group">{ORDER_LABEL[order] ?? ORDER_LABEL.new}<Icon name="chevron-down" size={14} /></button>
    </div>
    {#each groups as g (g.key ?? "_all")}
        {#if g.key}
            <div class="dg-group">{g.key} <em>· {g.items.length} release{g.items.length === 1 ? "" : "s"}</em></div>
        {/if}
        {#each g.items as f (familyKeyOf(f))}
            {@const l = familyLib(f)}
            {@const key = familyKeyOf(f)}
            {@const eds = (f.editions?.length ?? 1) > 1 ? f.editions : null}
            <div class="dg-item">
                <button class="row dg-row" onclick={() => openFamily(f)} oncontextmenu={(e) => { e.preventDefault(); moreFor(f); }}>
                    <span class="art">
                        {#if f.cover}<img src={f.cover} alt="" loading="lazy" />
                        {:else if l?.art_path}<Cover path={l.art_path} size={20} />
                        {:else}<Icon name="disc" size={20} />{/if}
                    </span>
                    <span class="b">
                        <span class="t">{f.title}</span>
                        <span class="m">{subOf(f, l)}</span>
                    </span>
                </button>
                {#if eds}
                    <button class="dg-eline" onclick={() => toggleEds(key)} aria-expanded={openEds.has(key)}>
                        <Icon name={openEds.has(key) ? "chevron-up" : "chevron-down"} size={13} />{openEds.has(key) ? "Hide editions" : `${eds.length} editions`}
                    </button>
                    {#if openEds.has(key)}
                        <div class="eds dg-eds">
                            {#each eds as e, i (e.id)}
                                <button class="ed" onclick={() => openRow(e)}>
                                    <span class="ed-art">{#if e.cover}<img src={e.cover} alt="" loading="lazy" />{:else}<Icon name="disc" size={16} />{/if}</span>
                                    <span class="ed-copy">
                                        <span class="n">{i === 0 ? "Original" : editionLabel(e, f)}</span>
                                        <span class="y">{[yearOf(e.release_date), e.track_count ? `${e.track_count} tracks` : ""].filter(Boolean).join(" · ")}</span>
                                    </span>
                                </button>
                            {/each}
                        </div>
                    {/if}
                {/if}
            </div>
        {/each}
    {/each}
    {#if items.length > cap}
        <div style="padding: 10px var(--pad) 4px"><button class="btn btn-ghost wide dg-more" onclick={() => (cap += 120)}>+ {items.length - cap} more releases</button></div>
    {/if}
{:else if !cat || cat.status === "loading"}
    <div class="sec-note" style="padding-top: 16px">Asking the catalog…</div>
{:else}
    <div class="stub"><b>Nothing to list.</b>The catalog has no releases for {shownName}{cat.status === "error" ? " — it did not answer" : ""}.</div>
{/if}

<div class="endpad"></div>
