// ===========================================================
// CollectionAppV2 — the logged-in My Collection page (v2).
// Collection reads collection_items (store.collectionV2); the add
// flow is MyCollectionAdd_v2 with the public-collection consent gate.
// Wishlist is unchanged (store.wishlist from `records`, old add flow).
// Reuses globals defined elsewhere: Sleeve, AnimatedMoney, WishCard,
// AddRecordFlow, MyCollectionAdd_v2, BBR_store, BBR_CONDITIONS.
// ===========================================================
const { useState: useV2State, useMemo: useV2Memo, useEffect: useV2Effect } = React;

function v2gbp(n) { return "£" + Math.round(n || 0).toLocaleString("en-GB"); }
// DB enum grade -> display code (VG_PLUS -> VG+).
function gradeDisplay(code) { return code ? code.replace("_PLUS", "+") : "—"; }
function v2condClass(code) {
  const c = gradeDisplay(code);
  return c === "VG+" ? "VGplus" : (["M", "NM", "VG"].includes(c) ? c : "low");
}
function v2decade(y) { return y ? (Math.floor(y / 10) * 10 + "s") : "—"; }

// Resolve a collection_items row into a render object (album for <Sleeve>).
function resolveV2(item, albums) {
  const album = item.slug
    ? (albums.find(a => a.slug === item.slug) || { title: item.title, artist: item.artist, year: item.year, rank: -1, slug: item.slug })
    : { title: item.title, artist: item.artist, year: item.year, rank: -1, slug: null, genre: item.genre };
  return {
    ...item,
    album,
    value: typeof item.est_value === "number" ? item.est_value : 0,
    prevValue: typeof item.prev_value === "number" ? item.prev_value : null, // last weekly snapshot
    genre: item.genre || "Other",
    decade: v2decade(item.year),
  };
}

function V2Card({ r, onRemove }) {
  const scanned = r.verification_level === "scanned";
  // Per-tile week-over-week change (vs the last weekly snapshot) — sums into the
  // rolled-up movement at the top.
  const delta = (typeof r.value === "number" && typeof r.prevValue === "number")
    ? Math.round(r.value) - Math.round(r.prevValue) : null;
  return (
    <div className="mc-card">
      <div className="mc-card-actions">
        <button className="mc-iconbtn danger" title="Remove" onClick={() => onRemove(r.id)}>
          <svg width="13" height="13" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M2 3.5h10M5 3.5V2h4v1.5M3.5 3.5l.6 8.5h5.8l.6-8.5"/></svg>
        </button>
      </div>
      <div className="mc-card-cover"><Sleeve album={r.album} size={76} /></div>
      <div className="mc-card-body">
        <div className="mc-card-top">
          <div>
            <div className="mc-card-title">{r.title}</div>
            <div className="mc-card-artist">{r.artist}{r.year ? " · " + r.year : ""}</div>
          </div>
          <span className={"mc-cond " + v2condClass(r.media_condition)} title="Media / Sleeve">
            {gradeDisplay(r.media_condition)}<small style={{ opacity: .6 }}>/{gradeDisplay(r.sleeve_condition)}</small>
          </span>
        </div>
        <div className="mc-card-meta">
          {r.genre || "—"}{r.slug ? " · in the Hundred" : ""}
          <span className={"v2-badge " + (scanned ? "scanned" : "manual")} title={scanned ? "Added by barcode scan" : "Added manually"}>
            {scanned ? "✓ scanned" : "manual"}
          </span>
        </div>
        <div className="mc-card-foot">
          <div>
            <div className="mc-card-val"><span className="cur">£</span><AnimatedMoney value={r.value} /></div>
            <div className="mc-card-range">{r.value > 0 ? "est. value" : "unvalued"}</div>
          </div>
          {delta != null && delta !== 0 && (
            <span style={{ fontSize: 12.5, fontWeight: 800, whiteSpace: "nowrap",
              color: delta > 0 ? "oklch(0.5 0.13 150)" : "var(--accent)" }} title="Change this week">
              {delta > 0 ? "▲ +" : "▼ −"}£{Math.abs(delta).toLocaleString("en-GB")}
            </span>
          )}
        </div>
      </div>
    </div>
  );
}

function CollectionAppV2({ albums, user, onGate }) {
  const store = window.BBR_store;
  const [, force] = useV2State(0);
  useV2Effect(() => store.subscribe(() => force(n => n + 1)), []);

  const [tab, setTab] = useV2State("collection");
  const [search, setSearch] = useV2State("");
  const [sortKey, setSortKey] = useV2State("value");
  const [filterGenre, setFilterGenre] = useV2State("all");
  const [addOpen, setAddOpen] = useV2State(false);
  const [wishAdd, setWishAdd] = useV2State(null);
  const [covers, setCovers] = useV2State({}); // cover-art key -> url|null (non-canon records)
  const coverReq = React.useRef(new Set());
  const [mvmt, setMvmt] = useV2State(null);   // {last_total,last_sent_at} — last weekly snapshot

  // Pull real cover art for non-canon (manual, slug=null) records, the same
  // way the leaderboard does (Discogs + iTunes via /api/cover, localStorage-cached).
  useV2Effect(() => {
    if (!window.lbFetchCover) return;
    const seen = new Set();
    const todo = [];
    store.collectionV2.forEach(r => {
      if (r.slug) return;
      const k = window.lbCoverKey(r);
      if (seen.has(k) || coverReq.current.has(k)) return;
      seen.add(k); coverReq.current.add(k); todo.push(r);
    });
    if (!todo.length) return;
    let cancelled = false;
    Promise.all(todo.map(r => window.lbFetchCover(r).then(url => [window.lbCoverKey(r), url])))
      .then(pairs => {
        if (cancelled) return;
        setCovers(prev => { const next = Object.assign({}, prev); pairs.forEach(([k, u]) => { next[k] = u; }); return next; });
      });
    return () => { cancelled = true; };
  }, [store.collectionV2]);

  // Week-over-week movement: this member's last weekly snapshot (written by the
  // collection-digest job — same number the weekly email reports). RLS lets a
  // member read only their own row; absent (or no policy) -> badge just hides.
  useV2Effect(() => {
    if (!window.BBR_supabase || !user || !user.id) return;
    let cancelled = false;
    window.BBR_supabase.from("collection_digest_state")
      .select("last_total,last_sent_at").eq("user_id", user.id).maybeSingle()
      .then(({ data }) => { if (!cancelled) setMvmt(data || {}); }, () => { if (!cancelled) setMvmt({}); });
    return () => { cancelled = true; };
  }, [user && user.id]);

  const items = useV2Memo(() => store.collectionV2.map(i => {
    const r = resolveV2(i, albums);
    if (!r.album.slug && window.lbCoverKey) {
      const u = covers[window.lbCoverKey(i)];
      if (u) r.album = Object.assign({}, r.album, { coverUrl: u });
    }
    return r;
  }), [store.collectionV2, albums, covers]);
  const wishlist = store.wishlist;
  // resolveRecord is a global from MyCollection.jsx (loaded first); reuse it for wishlist cards.
  const resolvedWish = useV2Memo(() => wishlist.map(r => resolveRecord(r, albums)), [wishlist, albums]);

  const total = items.reduce((s, r) => s + r.value, 0);
  const canonOwned = new Set(items.filter(r => r.slug).map(r => r.slug)).size;
  const genres = useV2Memo(() => Array.from(new Set(items.map(r => r.genre))).sort(), [items]);
  const mostValuable = items.reduce((m, r) => (!m || r.value > m.value ? r : m), null);
  const prevTotal = (mvmt && typeof mvmt.last_total === "number") ? Math.round(mvmt.last_total) : null;
  const weekDelta = prevTotal != null ? Math.round(total) - prevTotal : null;

  const view = useV2Memo(() => {
    let list = items.filter(r => {
      if (filterGenre !== "all" && r.genre !== filterGenre) return false;
      if (search && !((r.title + " " + r.artist).toLowerCase().includes(search.toLowerCase()))) return false;
      return true;
    });
    const cmp = {
      value: (a, b) => b.value - a.value,
      artist: (a, b) => (a.artist || "").localeCompare(b.artist || ""),
      year: (a, b) => (a.year || 0) - (b.year || 0),
      added: (a, b) => String(b.date_added).localeCompare(String(a.date_added)),
    }[sortKey] || ((a, b) => b.value - a.value);
    return list.slice().sort(cmp);
  }, [items, filterGenre, search, sortKey]);

  const empty = items.length === 0 && tab === "collection";

  function openAdd() { setAddOpen(true); }

  return (
    <div className="mc">
      {/* HERO */}
      <div className="mc-hero">
        <div>
          <div className="mc-hero-label"><span className="mc-eyebrow">Estimated collection value</span></div>
          <div className="mc-hero-total"><span className="cur">£</span><AnimatedMoney value={total} /></div>
          {weekDelta != null && items.length > 0 && (
            <div style={{ marginTop: 6, fontSize: 14.5 }}>
              {weekDelta === 0
                ? <span style={{ color: "var(--ink-mute)", fontWeight: 600 }}>No change this week</span>
                : <span style={{ fontWeight: 800, letterSpacing: "-.01em",
                    color: weekDelta > 0 ? "oklch(0.5 0.13 150)" : "var(--accent)" }}>
                    {weekDelta > 0 ? "▲ +" : "▼ −"}£{Math.abs(weekDelta).toLocaleString("en-GB")}
                    <span style={{ color: "var(--ink-mute)", fontWeight: 600 }}> this week</span>
                  </span>}
            </div>
          )}
          {items.length > 0 && (
            <div style={{ marginTop: 8, fontSize: 12.5, color: "var(--ink-mute)" }}>
              &#8635; Values refresh <b style={{ color: "var(--ink-2)" }}>every day</b> from live marketplace pricing.
            </div>
          )}
          {items.length > 0 ? (
            <div className="mc-hero-sub">
              <span className="mc-chip">{items.length} records</span>
              <span className="mc-chip">{canonOwned} / 100 of the Hundred</span>
              <span className="mc-chip">{genres.length} genres</span>
            </div>
          ) : (
            <div className="mc-hero-sub"><span className="mc-chip">Scan your first record to begin</span></div>
          )}
          <p className="v2-public-note">
            Your collection, its conditions and estimated values are <b>public</b> and appear on the leaderboards under your display name.
          </p>
        </div>
        {items.length > 0 && mostValuable && (
          <div className="mc-hero-stats">
            <div className="mc-stat">
              <div className="mc-stat-k">Most valuable</div>
              <div className="mc-stat-v">{v2gbp(mostValuable.value)}<small>{mostValuable.title}</small></div>
            </div>
            <div className="mc-stat">
              <div className="mc-stat-k">The Hundred</div>
              <div className="mc-stat-v">{canonOwned}<small>of 100 owned</small></div>
            </div>
          </div>
        )}
      </div>

      {/* TABS + CONTROLS */}
      <div className="mc-bar">
        <div className="mc-tabs">
          <button className={"mc-tab " + (tab === "collection" ? "active" : "")} onClick={() => setTab("collection")}>Collection<span className="ct">{items.length}</span></button>
          <button className={"mc-tab " + (tab === "wishlist" ? "active" : "")} onClick={() => setTab("wishlist")}>Wishlist<span className="ct">{resolvedWish.length}</span></button>
        </div>
        <div className="mc-controls">
          {tab === "collection" && (
            <div className="mc-search">
              <svg width="15" height="15" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="7" cy="7" r="5" /><path d="M11 11l3.5 3.5" /></svg>
              <input placeholder="Search your collection…" value={search} onChange={e => setSearch(e.target.value)} />
            </div>
          )}
          {tab === "collection" && (
            <div className="mc-select">
              <select value={filterGenre} onChange={e => setFilterGenre(e.target.value)}>
                <option value="all">All genres</option>
                {genres.map(g => <option key={g} value={g}>{g}</option>)}
              </select>
            </div>
          )}
          {tab === "collection" && (
            <div className="mc-select">
              <select value={sortKey} onChange={e => setSortKey(e.target.value)}>
                <option value="value">Sort: Value</option>
                <option value="artist">Sort: Artist</option>
                <option value="year">Sort: Year</option>
                <option value="added">Sort: Date added</option>
              </select>
            </div>
          )}
          <button className="mc-add" onClick={() => tab === "wishlist" ? setWishAdd({ intent: "wishlist" }) : openAdd()}>
            <span className="plus">+</span> {tab === "wishlist" ? "Add to wishlist" : "Add record"}
          </button>
        </div>
      </div>

      {/* DASHBOARD — data first, above the (potentially long) grid */}
      {tab === "collection" && items.length > 1 && window.CollectionDashboard_v2 && (
        <CollectionDashboard_v2 items={items} albums={albums} total={total} canonOwned={canonOwned} />
      )}

      {/* COLLECTION GRID */}
      {tab === "collection" && (empty ? (
        <div className="mc-empty">
          <h3>Your shelf is empty</h3>
          <p>Scan a barcode to add your first record. We'll match the exact pressing, value it, and put it on the board.</p>
          <div className="mc-empty-cta">
            <button className="mc-btn solid" onClick={openAdd}><span style={{ fontSize: 14 }}>+</span> Add your first record</button>
          </div>
        </div>
      ) : (
        <div className="mc-grid">
          {view.map(r => <V2Card key={r.id} r={r} onRemove={(id) => store.removeCollectionItemV2(id)} />)}
        </div>
      ))}

      {/* WISHLIST (unchanged data + card) */}
      {tab === "wishlist" && (resolvedWish.length === 0 ? (
        <div className="mc-empty">
          <h3>Nothing on the wishlist yet</h3>
          <p>Queue up the records you're hunting for.</p>
          <div className="mc-empty-cta"><button className="mc-btn solid" onClick={() => setWishAdd({ intent: "wishlist" })}><span style={{ fontSize: 14 }}>+</span> Add to wishlist</button></div>
        </div>
      ) : (
        <div className="mc-grid">
          {resolvedWish.map(r => <WishCard key={r.id} r={r} onMove={() => {}} onRemove={(id) => store.removeFromWishlist(id)} />)}
        </div>
      ))}

      {/* ADD FLOWS */}
      {addOpen && (
        <MyCollectionAdd_v2
          albums={albums}
          acceptedTerms={store.acceptedTerms}
          onAcceptTerms={() => store.acceptTerms()}
          onAdd={async (item) => { await store.addCollectionItemV2(item); setAddOpen(false); }}
          onClose={() => setAddOpen(false)}
        />
      )}
      {wishAdd && (
        <AddRecordFlow
          albums={albums}
          intent="wishlist"
          onAdd={(rec) => { store.addToWishlist(rec); setWishAdd(null); }}
          onClose={() => setWishAdd(null)}
        />
      )}
    </div>
  );
}

Object.assign(window, { CollectionAppV2 });
