// ===========================================================
// MyCollectionAdd_v2 — verified-collection add flow.
//
//   consent? -> entry -> (scan | search the Hundred | manual)
//            -> confirm the exact Discogs release
//            -> grade media + sleeve (Goldmine, no default)
//            -> server-side estimate -> onAdd(item)
//
// Emits a collection_items-shaped object. DB writes + the consent
// timestamp are handled by the parent (props), so this stays pure UI.
//
// Props:
//   albums         BBR_ALBUMS (the Hundred)
//   acceptedTerms  bool — has the user accepted the public-collection terms?
//   onAcceptTerms  async () => persist profiles.accepted_public_terms_at
//   onAdd          (item) => persist a collection_items row
//   onClose        () => dismiss
// ===========================================================
const { useState: useAddV2State, useMemo: useAddV2Memo, useEffect: useAddV2Effect } = React;

// Goldmine display code -> DB grade enum spelling.
function toGradeEnum(code) {
  if (code === "VG+") return "VG_PLUS";
  if (code === "G+") return "G_PLUS";
  return code;
}
function normTitle(s) { return (s || "").toString().toLowerCase().replace(/[^a-z0-9]/g, ""); }

// Light canon auto-link: if a confirmed release's artist+title closely matches
// an album in the Hundred, attach that slug so it counts toward the canon board.
function matchCanonSlug(albums, artist, title) {
  const t = normTitle(title), a = normTitle(artist);
  if (!t) return null;
  const hit = albums.find(al => {
    const at = normTitle(al.title);
    return at && (at === t || at.includes(t) || t.includes(at)) &&
      (!a || normTitle(al.artist).includes(a) || a.includes(normTitle(al.artist)));
  });
  return hit ? hit.slug : null;
}

function gbp(n) { return "£" + Math.round(n).toLocaleString("en-GB"); }

// ---- consent gate ----------------------------------------------------------
function ConsentGate({ onAccept, onClose }) {
  const [checked, setChecked] = useAddV2State(false);
  const [busy, setBusy] = useAddV2State(false);
  async function accept() {
    if (!checked || busy) return;
    setBusy(true);
    try { await onAccept(); } finally { setBusy(false); }
  }
  return (
    <div className="av2-pane av2-consent">
      <h3 className="av2-title">Before you add your first record</h3>
      <p className="av2-consent-lede">
        The Lead-In collection is a <b>public</b> collector community. When you add a record:
      </p>
      <ul className="av2-consent-list">
        <li>Your collection — <b>including each item's condition and estimated value</b> — is visible to anyone, signed in or not.</li>
        <li>It appears on the <b>public leaderboards</b> under your display name.</li>
        <li>Showing the value maths openly is how the community keeps each other honest.</li>
      </ul>
      <label className="av2-consent-ack">
        <input type="checkbox" checked={checked} onChange={(e) => setChecked(e.target.checked)} />
        <span>I understand my collection and its values will be public.</span>
      </label>
      <div className="av2-foot">
        <button className="av2-back" onClick={onClose}>Cancel</button>
        <button className="av2-next" disabled={!checked || busy} onClick={accept}>
          {busy ? "Saving…" : "Agree & continue"}
        </button>
      </div>
    </div>
  );
}

function MyCollectionAdd_v2({ albums, acceptedTerms, onAcceptTerms, onAdd, onClose }) {
  // consent | entry | scan | candidates | search | manual | grade
  const [step, setStep] = useAddV2State(acceptedTerms ? "entry" : "consent");
  const [entryMode, setEntryMode] = useAddV2State("scan"); // scan -> 'scanned', else 'manual'

  const [candidates, setCandidates] = useAddV2State([]);
  const [candNote, setCandNote] = useAddV2State("");
  const [candBusy, setCandBusy] = useAddV2State(false);

  const [pendingSlug, setPendingSlug] = useAddV2State(null); // canon slug when picked from the Hundred
  const [chosen, setChosen] = useAddV2State(null);           // confirmed release

  const [query, setQuery] = useAddV2State("");
  const [manual, setManual] = useAddV2State({ artist: "", title: "", year: "" });

  const [media, setMedia] = useAddV2State(null);   // Goldmine display code, no default
  const [sleeve, setSleeve] = useAddV2State(null);
  const [valuing, setValuing] = useAddV2State(false);
  const [estValue, setEstValue] = useAddV2State(null);
  const [estBasis, setEstBasis] = useAddV2State("");
  const [saving, setSaving] = useAddV2State(false);

  // ---- candidate lookup (barcode OR text q) --------------------------------
  async function lookup(params, mode) {
    setEntryMode(mode);
    setCandBusy(true); setCandidates([]); setCandNote(""); setStep("candidates");
    try {
      const j = await fetch("/api/barcode?" + params).then(r => r.json());
      setCandidates(j.candidates || []);
      setCandNote(j.note || "");
    } catch (e) {
      setCandNote("Couldn't reach the lookup just now. Try again, or add manually.");
    } finally {
      setCandBusy(false);
    }
  }
  function lookupBarcode(code) { lookup("barcode=" + encodeURIComponent(code), "scan"); }
  function lookupQuery(artist, title) { lookup("q=" + encodeURIComponent((artist + " " + title).trim()), "manual"); }

  // ---- choose / confirm a release ------------------------------------------
  function chooseCandidate(c) {
    setChosen({
      discogs_release_id: c.id,
      artist: c.artist || "",
      title: c.title || "",
      year: c.year || null,
      genre: c.genre || null,
      barcode: c.barcode || null,
      cover: c.cover || c.thumb || null,
      slug: pendingSlug || matchCanonSlug(albums, c.artist, c.title),
    });
    setMedia(null); setSleeve(null); setEstValue(null); setEstBasis("");
    setStep("grade");
  }
  // Manual add with no Discogs match: unvalued, genre unknown, manual trust.
  function addUnmatched() {
    const m = manual;
    setChosen({
      discogs_release_id: null,
      artist: m.artist.trim(), title: m.title.trim(),
      year: parseInt(m.year, 10) || null,
      genre: null, barcode: null, cover: null,
      slug: matchCanonSlug(albums, m.artist, m.title),
    });
    setEntryMode("manual");
    setMedia(null); setSleeve(null); setEstValue(null); setEstBasis("");
    setStep("grade");
  }

  // ---- value when a media grade is chosen (server-side) --------------------
  useAddV2Effect(() => {
    if (step !== "grade" || !media || !chosen || !chosen.discogs_release_id) { return; }
    let cancelled = false;
    setValuing(true);
    fetch("/api/discogs-price?release_id=" + chosen.discogs_release_id + "&grade=" + toGradeEnum(media))
      .then(r => r.json())
      .then(j => {
        if (cancelled) return;
        setEstValue(typeof j.est_value === "number" ? j.est_value : null);
        setEstBasis(j.basis || "");
        if (j.genre && !chosen.genre) setChosen(prev => ({ ...prev, genre: j.genre }));
      })
      .catch(() => { if (!cancelled) { setEstValue(null); setEstBasis(""); } })
      .finally(() => { if (!cancelled) setValuing(false); });
    return () => { cancelled = true; };
    // Depend on the release id, not the whole `chosen` object: patching the
    // genre below replaces `chosen`, which would otherwise re-trigger this and
    // fire a second, redundant price fetch for the same pressing.
  }, [media, step, chosen && chosen.discogs_release_id]);

  async function confirmAdd() {
    if (!chosen || !media || !sleeve || saving) return;
    setSaving(true);
    const item = {
      slug: chosen.slug || null,
      discogs_release_id: chosen.discogs_release_id || null,
      barcode: chosen.barcode || null,
      artist: chosen.artist,
      title: chosen.title,
      year: chosen.year || null,
      genre: chosen.genre || null,
      media_condition: toGradeEnum(media),
      sleeve_condition: toGradeEnum(sleeve),
      est_value: typeof estValue === "number" ? estValue : null,
      value_currency: "GBP",
      verification_level: entryMode === "scan" ? "scanned" : "manual",
    };
    try { await onAdd(item); } finally { setSaving(false); }
  }

  const canonResults = useAddV2Memo(() => {
    const q = query.trim().toLowerCase();
    const list = albums.slice().sort((a, b) => a.rank - b.rank);
    if (!q) return list.slice(0, 40);
    return list.filter(a => (a.title + " " + a.artist).toLowerCase().includes(q)).slice(0, 40);
  }, [query, albums]);

  // ====== render ======
  return (
    <div className="av2-backdrop" onClick={onClose}>
      <div className="av2-modal" onClick={(e) => e.stopPropagation()}>
        <div className="av2-head">
          <span className="av2-head-title">Add a record</span>
          <button className="av2-x" onClick={onClose}>×</button>
        </div>

        <div className="av2-body">
          {step === "consent" && (
            <ConsentGate
              onAccept={async () => { await onAcceptTerms(); setStep("entry"); }}
              onClose={onClose}
            />
          )}

          {step === "entry" && (
            <div className="av2-pane av2-entry">
              <h3 className="av2-title">How do you want to add it?</h3>
              <button className="av2-entry-primary" onClick={() => setStep("scan")}>
                <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M3 7V5a2 2 0 0 1 2-2h2M17 3h2a2 2 0 0 1 2 2v2M21 17v2a2 2 0 0 1-2 2h-2M7 21H5a2 2 0 0 1-2-2v-2"/><path d="M6 12h12"/></svg>
                <span>Scan the barcode</span>
                <small>Point your camera at the sleeve</small>
              </button>
              <button className="av2-entry-secondary" onClick={() => setStep("search")}>
                Search the Hundred
              </button>
              <button className="av2-entry-link" onClick={() => { setManual({ artist: "", title: "", year: "" }); setStep("manual"); }}>
                Can't find it? Add manually
              </button>
              <p className="av2-public-note">
                Your collection, conditions and estimated values are public and appear on the leaderboards under your display name.
              </p>
            </div>
          )}

          {step === "scan" && (
            <BarcodeScanner
              onDetect={lookupBarcode}
              onManual={lookupBarcode}
              onClose={() => setStep("entry")}
            />
          )}

          {step === "candidates" && (
            <div className="av2-pane">
              <button className="av2-back-link" onClick={() => setStep(entryMode === "scan" ? "scan" : "search")}>← Back</button>
              <h3 className="av2-title">Confirm your pressing</h3>
              <p className="av2-hint">{candBusy ? "Looking up…" : candNote}</p>
              <div className="av2-cand-list">
                {candidates.map(c => (
                  <button className="av2-cand" key={c.id} onClick={() => chooseCandidate(c)}>
                    {c.thumb ? <img className="av2-cand-thumb" src={c.thumb} alt="" loading="lazy" />
                             : <span className="av2-cand-thumb av2-cand-thumb-blank" />}
                    <span className="av2-cand-main">
                      <span className="av2-cand-t"><b>{c.artist}</b> — {c.title}</span>
                      <span className="av2-cand-meta">{[c.year, c.country, c.label, c.catno].filter(Boolean).join(" · ")}</span>
                      <span className="av2-cand-fmt">{c.format}{c.genre ? " · " + c.genre : ""}</span>
                    </span>
                  </button>
                ))}
              </div>
              {!candBusy && candidates.length === 0 && (
                <button className="av2-entry-secondary" onClick={() => { setManual({ artist: "", title: "", year: "" }); setStep("manual"); }}>
                  None of these — add manually
                </button>
              )}
            </div>
          )}

          {step === "search" && (
            <div className="av2-pane">
              <button className="av2-back-link" onClick={() => setStep("entry")}>← Back</button>
              <h3 className="av2-title">Search the Hundred</h3>
              <div className="av2-search">
                <input autoFocus placeholder="Title or artist…" value={query} onChange={(e) => setQuery(e.target.value)} />
              </div>
              <div className="av2-results">
                {canonResults.map(a => (
                  <button className="av2-res" key={a.slug} onClick={() => { setPendingSlug(a.slug); lookupQuery(a.artist, a.title); }}>
                    <span className="av2-res-t">{a.title}</span>
                    <span className="av2-res-a">{a.artist} · {a.year}</span>
                    <span className="av2-res-rank">№ {String(a.rank).padStart(3, "0")}</span>
                  </button>
                ))}
              </div>
              <button className="av2-entry-link" onClick={() => { setManual({ artist: "", title: "", year: "" }); setStep("manual"); }}>
                Not in the Hundred? Add manually
              </button>
            </div>
          )}

          {step === "manual" && (
            <div className="av2-pane">
              <button className="av2-back-link" onClick={() => setStep("entry")}>← Back</button>
              <h3 className="av2-title">Add manually</h3>
              <div className="av2-manual-grid">
                <label>Artist<input value={manual.artist} onChange={(e) => setManual({ ...manual, artist: e.target.value })} placeholder="Artist" autoFocus /></label>
                <label>Title<input value={manual.title} onChange={(e) => setManual({ ...manual, title: e.target.value })} placeholder="Album title" /></label>
                <label>Year<input value={manual.year} onChange={(e) => setManual({ ...manual, year: e.target.value })} placeholder="1975" inputMode="numeric" /></label>
              </div>
              <div className="av2-foot">
                <button className="av2-back" onClick={addUnmatched} disabled={!manual.artist.trim() || !manual.title.trim()}>
                  Add without Discogs match
                </button>
                <button className="av2-next" disabled={!manual.artist.trim() || !manual.title.trim()}
                  onClick={() => lookupQuery(manual.artist, manual.title)}>
                  Find on Discogs
                </button>
              </div>
            </div>
          )}

          {step === "grade" && chosen && (
            <div className="av2-pane">
              <button className="av2-back-link" onClick={() => setStep(chosen.discogs_release_id ? "candidates" : "manual")}>← Back</button>
              <div className="av2-chosen">
                {chosen.cover && <img className="av2-chosen-cover" src={chosen.cover} alt="" />}
                <div>
                  <div className="av2-chosen-t"><b>{chosen.artist}</b> — {chosen.title}</div>
                  <div className="av2-chosen-meta">{[chosen.year, chosen.genre].filter(Boolean).join(" · ")}{chosen.discogs_release_id ? "" : " · no Discogs match"}</div>
                </div>
              </div>
              <h3 className="av2-title">Grade your copy</h3>
              <p className="av2-hint">Goldmine standard. Grade the record and the sleeve separately — no default, pick honestly.</p>

              <div className="av2-grade-group">
                <div className="av2-grade-label">Media (the record)</div>
                <div className="av2-grade-row">
                  {window.BBR_CONDITIONS.map(c => (
                    <button key={c.code} title={c.name} className={"av2-grade " + (media === c.code ? "sel" : "")} onClick={() => setMedia(c.code)}>{c.code}</button>
                  ))}
                </div>
              </div>
              <div className="av2-grade-group">
                <div className="av2-grade-label">Sleeve</div>
                <div className="av2-grade-row">
                  {window.BBR_CONDITIONS.map(c => (
                    <button key={c.code} title={c.name} className={"av2-grade " + (sleeve === c.code ? "sel" : "")} onClick={() => setSleeve(c.code)}>{c.code}</button>
                  ))}
                </div>
              </div>

              <div className="av2-est">
                {!chosen.discogs_release_id ? <span className="av2-est-none">No Discogs match — added unvalued.</span>
                  : valuing ? <span className="av2-est-busy">Valuing…</span>
                  : estValue != null ? <span className="av2-est-val">{estBasis || ("est. " + gbp(estValue))}</span>
                  : media ? <span className="av2-est-none">No marketplace price for this pressing.</span>
                  : <span className="av2-est-none">Pick a media grade to see the estimate.</span>}
              </div>

              <div className="av2-foot">
                <button className="av2-back" onClick={() => setStep(chosen.discogs_release_id ? "candidates" : "manual")}>← Back</button>
                <button className="av2-next" disabled={!media || !sleeve || saving} onClick={confirmAdd}>
                  {saving ? "Adding…" : "Add to collection" + (estValue != null ? " · " + gbp(estValue) : "")}
                </button>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { MyCollectionAdd_v2 });
