/* QCM⁵ tradeXchange — Genesis card viewer
   Glass-screen card · tilt · holographic foil — wrapping each agent's own
   1-of-1 artwork (drag & drop, persists per token). Every text field is
   editable: prefilled from data, typeable when empty, saved per token. */
const { useState, useRef, useEffect, useCallback } = React;

const TIER_META = {
  "Quantum Seeker":    { rank: 1, label: "Seeker" },
  "Quantum Explorer":  { rank: 2, label: "Explorer" },
  "Quantum Pioneer":   { rank: 3, label: "Pioneer" },
  "Quantum Founder":   { rank: 4, label: "Founder" },
  "Quantum Singleton": { rank: 5, label: "Singleton" },
};

function pad3(n) { return String(n).padStart(3, "0"); }
function titleCase(s) { return (s || "").replace(/(^|[-\s])\w/g, m => m.toUpperCase()).replace(/-/g, " "); }

// ---------- on-chain core details (collection-wide constants) ----------
const CHAIN = {
  collection: "QCM\u2075 Genesis",
  symbol: "QCM5G",
  network: "Base",
  standard: "ERC-721",
  contract: "0x8d5b1246CA5fb193F9E76bAdB4405D5024DD431e",
  creator: "0x6b477738f7f73471A54bD390e5741FbD79730069",
};
function shortAddr(a) { return a ? a.slice(0, 6) + "\u2026" + a.slice(-4) : ""; }
function tokenIdOf(card) {
  if (card.tokenId != null) return card.tokenId;
  const n = parseInt(card.tokenLabel, 10);
  return isNaN(n) ? "" : n;
}
function baseScanUrl(tokenId) {
  return tokenId !== "" && tokenId != null
    ? "https://basescan.org/nft/" + CHAIN.contract + "/" + tokenId
    : "https://basescan.org/token/" + CHAIN.contract;
}

// ---------- editable field (per-token persistence) ----------
function Ed({ token, field, fallback, placeholder, className, locked }) {
  const key = "qcm5:" + token + ":" + field;
  const [initial] = useState(() => {
    let s = null; try { s = localStorage.getItem(key); } catch (_) {}
    return s != null ? s : (fallback == null ? "" : String(fallback));
  });
  return (
    <span
      className={"ed " + (className || "")}
      contentEditable={!locked}
      suppressContentEditableWarning
      spellCheck={false}
      data-ph={placeholder || ""}
      onInput={(e) => { try { localStorage.setItem(key, e.currentTarget.textContent); } catch (_) {} }}
      onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); e.currentTarget.blur(); } }}
    >{initial}</span>
  );
}

// ---------- founder seal (24-karat foil medallion, pure SVG) ----------
function FounderSeal({ ghost }) {
  const beads = [];
  for (let i = 0; i < 52; i++) {
    const a = (i / 52) * Math.PI * 2;
    const cx = (50 + Math.cos(a) * 45.6).toFixed(2);
    const cy = (50 + Math.sin(a) * 45.6).toFixed(2);
    beads.push(<circle key={i} cx={cx} cy={cy} r={ghost ? "0.9" : "1.05"} fill={ghost ? "currentColor" : "#6f5417"} opacity={ghost ? ".7" : ".55"} />);
  }
  if (ghost) {
    return (
      <svg className="seal-watermark" viewBox="0 0 100 100" aria-hidden="true">
        <defs>
          <path id="sealTopG" d="M 21,50 A 29,29 0 0 1 79,50" fill="none" />
          <path id="sealBotG" d="M 21,50 A 29,29 0 0 0 79,50" fill="none" />
        </defs>
        <g fill="none" stroke="currentColor" strokeWidth="1">
          <circle cx="50" cy="50" r="48" />
          <circle cx="50" cy="50" r="42" opacity=".55" />
          <circle cx="50" cy="50" r="38" />
          <circle cx="50" cy="50" r="26" />
        </g>
        <g fill="none" stroke="currentColor" strokeWidth="1.4">
          <ellipse cx="50" cy="50" rx="17" ry="6.4" />
          <ellipse cx="50" cy="50" rx="17" ry="6.4" transform="rotate(60 50 50)" />
          <ellipse cx="50" cy="50" rx="17" ry="6.4" transform="rotate(120 50 50)" />
        </g>
        <circle cx="50" cy="50" r="3.1" fill="currentColor" />
      </svg>
    );
  }
  return (
    <svg className="seal-medallion" viewBox="0 0 100 100" aria-hidden="true">
      <defs>
        <linearGradient id="sealGold" x1="0" y1="0" x2="1" y2="1">
          <stop offset="0" stopColor="#fff7d4" />
          <stop offset=".3" stopColor="#f8dc82" />
          <stop offset=".55" stopColor="#c89a34" />
          <stop offset=".78" stopColor="#f3d585" />
          <stop offset="1" stopColor="#8c6b1d" />
        </linearGradient>
        <radialGradient id="sealCore" cx=".5" cy=".4" r=".62">
          <stop offset="0" stopColor="#1b1812" />
          <stop offset="1" stopColor="#0b0906" />
        </radialGradient>
        <linearGradient id="sealSheen" x1="0" y1="0" x2="1" y2="1">
          <stop offset="0" stopColor="#fff" stopOpacity=".55" />
          <stop offset=".5" stopColor="#fff" stopOpacity="0" />
          <stop offset="1" stopColor="#fff" stopOpacity=".12" />
        </linearGradient>
        <path id="sealTop" d="M 21,50 A 29,29 0 0 1 79,50" fill="none" />
        <path id="sealBot" d="M 21,50 A 29,29 0 0 0 79,50" fill="none" />
      </defs>
      <circle cx="50" cy="50" r="48" fill="url(#sealGold)" />
      {beads}
      <circle cx="50" cy="50" r="42" fill="none" stroke="#6f5417" strokeWidth="1" opacity=".5" />
      <circle cx="50" cy="50" r="38" fill="#0e0c08" />
      <circle cx="50" cy="50" r="38" fill="none" stroke="url(#sealGold)" strokeWidth=".9" />
      <g fill="#f3da8b" style={{ font: '700 7.4px "DM Mono", monospace', letterSpacing: "1.6px" }}>
        <text><textPath href="#sealTop" startOffset="50%" textAnchor="middle">QUANTUM SEAL</textPath></text>
        <text><textPath href="#sealBot" startOffset="50%" textAnchor="middle">FOUNDER SEAL</textPath></text>
      </g>
      <circle cx="50" cy="50" r="26" fill="url(#sealCore)" stroke="url(#sealGold)" strokeWidth="1.2" />
      <g fill="none" stroke="url(#sealGold)" strokeWidth="1.5">
        <ellipse cx="50" cy="50" rx="17" ry="6.4" />
        <ellipse cx="50" cy="50" rx="17" ry="6.4" transform="rotate(60 50 50)" />
        <ellipse cx="50" cy="50" rx="17" ry="6.4" transform="rotate(120 50 50)" />
      </g>
      <circle cx="50" cy="50" r="3.1" fill="url(#sealGold)" />
      <circle className="seal-sheen" cx="50" cy="50" r="48" fill="url(#sealSheen)" />
    </svg>
  );
}

// ---------- the card ----------
function GenesisCard({ card, t, locked }) {
  const sceneRef = useRef(null);
  const cardRef = useRef(null);
  const raf = useRef(0);

  const onMove = useCallback((e) => {
    const el = cardRef.current; if (!el) return;
    const r = el.getBoundingClientRect();
    const px = (e.clientX - r.left) / r.width;
    const py = (e.clientY - r.top) / r.height;
    cancelAnimationFrame(raf.current);
    raf.current = requestAnimationFrame(() => {
      const max = 9;
      el.style.setProperty("--rx", (-(py - 0.5) * 2 * max).toFixed(2) + "deg");
      el.style.setProperty("--ry", ((px - 0.5) * 2 * max).toFixed(2) + "deg");
      el.style.setProperty("--mx", (px * 100).toFixed(1) + "%");
      el.style.setProperty("--my", (py * 100).toFixed(1) + "%");
      el.style.setProperty("--active", "1");
    });
  }, []);

  const onLeave = useCallback(() => {
    const el = cardRef.current; if (!el) return;
    cancelAnimationFrame(raf.current);
    el.style.setProperty("--rx", "0deg");
    el.style.setProperty("--ry", "0deg");
    el.style.setProperty("--mx", "50%");
    el.style.setProperty("--my", "30%");
    el.style.setProperty("--active", "0");
  }, []);

  const tier = TIER_META[card.tier] || { rank: 1, label: card.tier };
  const isRare = tier.rank >= 3;
  const blendOn = t.textBlend && t.textBlend !== "off";
  const scope = t.blendScope || "all";
  const titleOver = blendOn && scope === "title";
  const display = card.name || card.agent || "";
  const tokenId = tokenIdOf(card);
  const scanUrl = baseScanUrl(tokenId);
  const fullArt = t.panel === "art";
  const artFit = fullArt ? "cover" : (t.artFit || "cover");

  return (
    <div className="scene" ref={sceneRef} onMouseMove={onMove} onMouseLeave={onLeave}>
      <div
        className={"card panel-" + (t.panel || "full") + (isRare ? " card--rare" : "") + (locked ? " locked" : "") + (blendOn ? " blend-on blend-" + scope + " blend-" + t.textBlend : "")}
        ref={cardRef}
        style={{
          "--accent": t.accent,
          "--radius": t.radius + "px",
          "--shine": t.shine / 100,
          "--scrim": t.scrim / 100,
          "--type": t.typeScale,
        }}
      >
        <div className={"card__photo fit-" + artFit}>
          <image-slot
            id={"art-" + card.id}
            shape="rect"
            fit={artFit}
            src={card.art}
            placeholder="Drop 1-of-1 artwork"
          ></image-slot>
        </div>
        <div className="card__scrim" />
        <div className="card__holo" />
        <div className="card__glare" />
        <div className="card__edge" />

        {t.seal !== false && (fullArt ? <FounderSeal /> : <FounderSeal ghost />)}

        <div className="card__content">
          <header className="card__top">
            <div className="brandmark">
              <span className="brandmark__q">QCM⁵</span>
              <span className="brandmark__sub">GENESIS</span>
            </div>
            <div className="topright">
              <span className="token">{card.tokenLabel ? "#" : ""}<Ed token={card.id} field="token" fallback={card.tokenLabel || ""} placeholder={card.tokenLabel ? "—" : "# token"} locked={locked} /></span>
              <span className="edition">1 OF 1</span>
            </div>
          </header>

          <div className="card__mid">
            {titleOver && (
              <div className="hero-title">
                <h1><Ed token={card.id} field="name" fallback={display} placeholder="Agent Name" locked={locked} /></h1>
              </div>
            )}
          </div>

          <section className="glass">
            <div className="glass__head">
              <div className="glass__title">
                {!titleOver && <h1><Ed token={card.id} field="name" fallback={display} placeholder="Agent Name" locked={locked} /></h1>}
                <p className="agentid"><Ed token={card.id} field="agentId" fallback={card.agentId} placeholder="AGENT-ID" locked={locked} /></p>
              </div>
              <div className="tokenid">
                <span className="tokenid__row">
                  <span className="tokenid__hash">#</span>
                  <span className="tokenid__num"><Ed token={card.id} field="tokenId" fallback={tokenId} placeholder="—" locked={locked} /></span>
                </span>
                <span className="tokenid__lbl">TOKEN ID</span>
              </div>
            </div>

            <div className="glass__detail">
              <div className="rule"><span /></div>

              <div className="details">
                <div className="detail">
                  <span className="detail__k">Collection</span>
                  <span className="detail__v detail__v--accent">{CHAIN.collection}<span className="detail__sym">{CHAIN.symbol}</span></span>
                </div>
                <div className="detail">
                  <span className="detail__k">Chain · Standard</span>
                  <span className="detail__v">{CHAIN.network} · {CHAIN.standard}</span>
                </div>
                <div className="detail">
                  <span className="detail__k">Contract</span>
                  <span className="detail__v" title={CHAIN.contract}>{shortAddr(CHAIN.contract)}</span>
                </div>
                <div className="detail">
                  <span className="detail__k">Creator</span>
                  <span className="detail__v" title={CHAIN.creator}>{shortAddr(CHAIN.creator)}</span>
                </div>
              </div>

              <a className="basescan" href={scanUrl} target="_blank" rel="noopener noreferrer">
                <span>View on BaseScan</span>
                <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M7 17L17 7M17 7H9M17 7v8" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>
              </a>
            </div>
            <span className="glass__hint">Hover for details</span>
          </section>

          <div className="wordmark">tradeXchange</div>
        </div>
      </div>
    </div>
  );
}

// ---------- viewer ----------
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "shine": 55,
  "scrim": 46,
  "accent": "#42f977",
  "radius": 26,
  "typeScale": 1,
  "textBlend": "off",
  "blendScope": "all",
  "artFit": "contain",
  "panel": "art",
  "seal": true
}/*EDITMODE-END*/;

function App() {
  const cards = window.GENESIS_CARDS || [];
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [locked, setLocked] = useState(false);
  const qs = (typeof URLSearchParams !== "undefined" ? new URLSearchParams(window.location.search) : null);
  const qCard = qs && qs.get("card");
  const qToken = qs && qs.get("token");
  const renderMode = qs && (qs.get("render") === "1" || qs.get("renderMode") === "1");
  const [i, setI] = useState(() => {
    if (qCard) { const k = cards.findIndex((c) => c.id === qCard); if (k >= 0) return k; }
    if (qToken) { const tn = parseInt(qToken, 10);
      const k = cards.findIndex((c) => tokenIdOf(c) === tn); if (k >= 0) return k; }
    const n = parseInt(localStorage.getItem("qcm5_idx") || "0", 10);
    return isNaN(n) ? 0 : Math.min(Math.max(n, 0), cards.length - 1);
  });
  useEffect(() => {
    if (!renderMode) return;
    document.body.classList.add("render-mode");
    const css = document.createElement("style");
    css.textContent = ".render-mode .nav,.render-mode .caption,.render-mode .dots,.render-mode .twk-fab,.render-mode .twk-panel{display:none!important}.render-mode .stage{justify-content:center}";
    document.head.appendChild(css);
  }, [renderMode]);

  useEffect(() => { localStorage.setItem("qcm5_idx", String(i)); }, [i]);

  const go = useCallback((d) => {
    setI((p) => (p + d + cards.length) % cards.length);
  }, [cards.length]);

  useEffect(() => {
    const onKey = (e) => {
      if (e.target && e.target.isContentEditable) return;
      if (e.key === "ArrowRight") go(1);
      else if (e.key === "ArrowLeft") go(-1);
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [go]);

  const card = cards[i];
  if (!card) return null;

  return (
    <div className="stage" style={{ "--gold": t.accent }}>
      <div className="stage__bg" />
      <div className="stage__holo" />
      <div className="viewer">
        <button className="nav nav--prev" onClick={() => go(-1)} aria-label="Previous">
          <svg viewBox="0 0 24 24"><path d="M15 5l-7 7 7 7" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>
        </button>

        <div className="viewer__card">
          <GenesisCard key={card.id} card={card} t={t} locked={locked} />
        </div>

        <button className="nav nav--next" onClick={() => go(1)} aria-label="Next">
          <svg viewBox="0 0 24 24"><path d="M9 5l7 7-7 7" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>
        </button>
      </div>

      <div className="caption">
        <span className="caption__idx">{pad3(i + 1)}</span>
        <span className="caption__div">/</span>
        <span className="caption__total">{pad3(cards.length)}</span>
        <span className="caption__agent">{card.name}{card.tokenLabel ? " · #" + card.tokenLabel : ""}</span>
        <button className={"lockbtn" + (locked ? " on" : "")} onClick={() => setLocked((v) => !v)}>
          {locked ? "Unlock" : "Lock"}
        </button>
      </div>

      <div className="dots">
        {cards.map((c, k) => (
          <button
            key={c.id}
            className={"dot" + (k === i ? " dot--on" : "")}
            onClick={() => setI(k)}
            aria-label={"Card " + (k + 1)}
          />
        ))}
      </div>

      <TweaksPanel>
        <TweakSection label="Card" />
        <TweakRadio label="Data panel" value={t.panel}
          options={[{ value: "full", label: "Full" }, { value: "compact", label: "Compact" }, { value: "reveal", label: "Reveal" }, { value: "art", label: "Full Art" }]}
          onChange={(v) => setTweak("panel", v)} />
        <TweakRadio label="Art fit" value={t.artFit}
          options={[{ value: "cover", label: "Cover" }, { value: "contain", label: "Contain" }]}
          onChange={(v) => setTweak("artFit", v)} />
        <TweakToggle label="Founder seal" value={t.seal !== false}
          onChange={(v) => setTweak("seal", v)} />
        <TweakSlider label="Holographic shine" value={t.shine} min={0} max={100} unit="%"
          onChange={(v) => setTweak("shine", v)} />
        <TweakSlider label="Art scrim" value={t.scrim} min={0} max={100} unit="%"
          onChange={(v) => setTweak("scrim", v)} />
        <TweakRadio label="Text blend" value={t.textBlend}
          options={[{ value: "off", label: "Off" }, { value: "adaptive", label: "Adaptive" }, { value: "chameleon", label: "Chameleon" }]}
          onChange={(v) => setTweak("textBlend", v)} />
        {t.textBlend !== "off" && (
          <TweakRadio label="Blend scope" value={t.blendScope}
            options={[{ value: "all", label: "All text" }, { value: "title", label: "Title only" }]}
            onChange={(v) => setTweak("blendScope", v)} />
        )}
        <TweakColor label="Accent" value={t.accent}
          options={["#42f977", "#23c8e9", "#a334f3", "#c6207a"]}
          onChange={(v) => setTweak("accent", v)} />
        <TweakSlider label="Corner radius" value={t.radius} min={6} max={42} unit="px"
          onChange={(v) => setTweak("radius", v)} />
        <TweakSlider label="Type scale" value={t.typeScale} min={0.85} max={1.2} step={0.01}
          onChange={(v) => setTweak("typeScale", v)} />
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
