<script>
    /* A stranger, in full — the desktop's person drawer as a screen: the
       card their own client serves (what they wrote about themselves, their
       picture, what their uploads are doing), the likes and hates they
       declared to the server, and the comparison this phone makes of their
       share against the library. The comparison is the reason to open a
       stranger at all: every record they have by an artist in your library
       that you don't. Nothing is uploaded to work it out — their
       folder names against your albums, here (`overlap.rs`). */
    import Icon from "../../components/Icon.svelte";
    import Identicon from "../../components/Identicon.svelte";
    import * as api from "../../lib/api.js";
    import { slsk, openThread } from "../slsk.svelte.js";
    import { push, openHalf, openViewer } from "../nav.svelte.js";
    import { player } from "../player.svelte.js";
    import { note } from "../notice.svelte.js";
    import { ago } from "../util.js";

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

    /* the comparison keeps 120 named pairs; a screenful shows, the rest on
       demand — the cap Browse gives a big share */
    const GAPS_CAP = 40;
    let info = $state(null);        // slsk_user_info, once their client answers
    let infoErr = $state("");
    let interests = $state(null);   // slsk_user_interests, from the server
    let intErr = $state(false);
    let overlap = $state(null);     // the comparison, if this device made one
    let busy = $state(false);       // a comparison running: it browses them first
    let cap = $state(GAPS_CAP);

    const online = $derived(slsk.connection === "online");
    const picture = $derived(info?.picture_png_base64 ? "data:image/png;base64," + info.picture_png_base64 : null);
    const likes = $derived(interests?.likes ?? []);
    const hates = $derived(interests?.hates ?? []);

    /* both asked at once. The card comes from THEIR client, direct and peer
       to peer, so it can simply go unanswered; the interests come from the
       server, which answers or doesn't. */
    $effect(() => {
        const u = username;
        if (!u || !online) return;
        let live = true;
        info = null;
        infoErr = "";
        interests = null;
        intErr = false;
        api.slskUserInfo(u).then((v) => { if (live) info = v; }, (e) => { if (live) infoErr = String(e).replace(/^Error:\s*/, ""); });
        api.slskUserInterests(u).then((v) => { if (live) interests = v; }, () => { if (live) intErr = true; });
        return () => { live = false; };
    });

    /* the ledger is per username and kept on the device: opening a stranger
       reads what was compared before, never compares — comparing browses
       their whole share, which is a minute of someone else's bandwidth */
    async function readOverlap() {
        try {
            const all = (await api.overlapList()) || [];
            const k = String(username || "").trim().toLowerCase();
            overlap = all.find((o) => String(o.username).toLowerCase() === k) ?? null;
        } catch {
            overlap = null;
        }
    }
    $effect(() => { void username; cap = GAPS_CAP; void readOverlap(); });

    async function compare() {
        if (busy || !online) return;
        busy = true;
        try {
            await api.overlapCompute(username);
            await readOverlap();
            cap = GAPS_CAP;
        } catch (e) { player.error = String(e); }
        busy = false;
    }
    async function forget() {
        if (busy) return;
        busy = true;
        try {
            await api.overlapForget(username);
            overlap = null;
            note("Comparison forgotten");
        } catch (e) { player.error = String(e); }
        busy = false;
    }
    /* a record they have and the library doesn't: the catalog search that
       finds it, where Save can fetch it from whoever has it best */
    function findRecord(g) {
        push("search", { q: [g.artist, g.album].filter(Boolean).join(" ") }, screenEl);
    }
    const uploadLine = $derived.by(() => {
        if (!online) return "not signed in to Soulseek";
        if (infoErr) return "their client did not answer";
        if (!info) return "asking their client…";
        return (info.has_free_slot ? "a slot is free" : "no free slot")
            + " · queue " + (info.queue_size ?? 0)
            + " · " + (info.total_uploads ?? 0).toLocaleString() + " served";
    });
</script>

<div class="brw-head pr-head">
    {#if picture}
        <button class="pr-pic" onclick={() => openViewer(picture)} aria-label="Their picture"><img src={picture} alt="" /></button>
    {:else}
        <Identicon seed={username} size={44} />
    {/if}
    <div class="b">
        <div class="t">{username}</div>
        <div class="m">{uploadLine}</div>
    </div>
    <button class="iconbtn muted" onclick={() => openHalf("peer", { username })} aria-label="More"><Icon name="more" size={22} /></button>
</div>

<div class="brw-acts">
    <button class="btn btn-fill" onclick={() => { openThread(username); push("thread", { username }, screenEl); }}><Icon name="message" size={14} />Message</button>
    <button class="btn btn-ghost" onclick={() => push("browse", { username }, screenEl)}><Icon name="folder-open" size={14} />Browse their files</button>
</div>

{#if !online}
    <div class="sec-note">Sign in to Soulseek to ask their client for their card.</div>
{:else if infoErr}
    <div class="sec-note">Their client did not answer ({infoErr}) — offline, firewalled, or not sharing a card.</div>
{:else if !info}
    <div class="sec-note">Asking their client for the card — direct, peer to peer.</div>
{:else}
    <div class="pr-about">{info.description?.trim() || "They wrote nothing about themselves."}</div>
    {#if info.picture_note}<div class="sec-note">{info.picture_note}</div>{/if}
{/if}

<div class="sec"><h2>Interests <em>· what they declared on Soulseek</em></h2></div>
{#if likes.length || hates.length}
    {#if likes.length}
        <div class="sec-note">Likes</div>
        <div class="chips pr-tags">{#each likes as l, i (i)}<span class="chip quiet">{l}</span>{/each}</div>
    {/if}
    {#if hates.length}
        <div class="sec-note" style="padding-top: 10px">Hates</div>
        <div class="chips pr-tags">{#each hates as h, i (i)}<span class="chip quiet">{h}</span>{/each}</div>
    {/if}
{:else if intErr}
    <div class="sec-note">The server did not answer.</div>
{:else if interests}
    <div class="sec-note">None declared.</div>
{:else if online}
    <div class="sec-note">Asking the server…</div>
{:else}
    <div class="sec-note">Sign in to Soulseek to see what they declared.</div>
{/if}

<div class="sec"><h2>Library overlap <em>· compared on this phone</em></h2></div>
{#if overlap}
    {@const o = overlap}
    <div class="sec-note pr-sum">
        {o.their_albums
            ? `${o.albums_in_common_count} of their ${o.their_albums} albums are in your library · ${o.artists_in_common_count} artist${o.artists_in_common_count === 1 ? "" : "s"} in common · compared ${ago(o.computed_at)}`
            : `No album folders among ${o.audio_files} audio files · compared ${ago(o.computed_at)}`}
    </div>
    {#if o.gaps.length}
        <div class="sec-note" style="padding-top: 6px">They have {o.gaps_count} record{o.gaps_count === 1 ? "" : "s"} by artists in your library that you don't.{o.gaps.length > cap ? ` The first ${cap} are here.` : ""}</div>
        {#each o.gaps.slice(0, cap) as g, i (i)}
            <button class="row pr-gap" onclick={() => findRecord(g)}>
                <span class="lead"><Icon name="disc" size={20} /></span>
                <span class="b"><span class="t">{g.album}</span><span class="m">{g.artist}</span></span>
                <span class="r"><Icon name="search" size={17} /></span>
            </button>
        {/each}
        {#if o.gaps.length > cap}
            <div style="padding: 4px var(--pad) 8px"><button class="btn btn-ghost wide" onclick={() => (cap += 200)}>+ {o.gaps.length - cap} more records</button></div>
        {/if}
    {:else}
        <div class="sec-note" style="padding-top: 6px">Nothing they have that you lack.</div>
    {/if}
    <div class="brw-acts" style="padding-top: 8px">
        <button class="btn btn-ghost" onclick={compare} disabled={!online || busy}><Icon name="refresh" size={14} />{busy ? "Browsing…" : "Compare again"}</button>
        <button class="btn btn-ghost" onclick={forget} disabled={busy}><Icon name="trash" size={14} />Forget this comparison</button>
    </div>
{:else}
    <div class="stub"><b>Not compared yet.</b>Their share folders against your albums, on this phone — nothing is uploaded to do it.</div>
    <div style="padding: 0 var(--pad) 8px">
        <button class="btn btn-ghost wide" onclick={compare} disabled={!online || busy}><Icon name="library" size={14} />{busy ? "Browsing their shares…" : "Compare with my library"}</button>
    </div>
{/if}

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