/* ============================================================
   gp-app.jsx — shell: brand, pipeline nav, routing, tweaks
   ============================================================ */
const { useState: aS, useEffect: aE, useMemo: aM } = React;

const STAGES = [
  { id: "radar", label: "Radar", num: "01", sub: "market scan" },
  { id: "selection", label: "Selection", num: "02", sub: "asset picks" },
  { id: "plan", label: "Plan", num: "03", sub: "sweep audit" },
  { id: "execution", label: "Execution", num: "04", sub: "plan vs. fills" },
];

function Clock() {
  const [t, setT] = aS("");
  aE(() => {
    const tick = () => {
      const d = new Date();
      const p = (n) => String(n).padStart(2, "0");
      setT(`${p(d.getUTCHours())}:${p(d.getUTCMinutes())}:${p(d.getUTCSeconds())} UTC`);
    };
    tick(); const id = setInterval(tick, 1000); return () => clearInterval(id);
  }, []);
  return <Mono color="var(--fg-2)" size={11} style={{ letterSpacing: "0.06em" }}>{t}</Mono>;
}

function PipelineNav({ stage, setStage, summary }) {
  return (
    <div style={{ display: "flex", alignItems: "stretch", borderBottom: "1px solid var(--line)", background: "var(--bg-1)" }}>
      {STAGES.map((s, i) => {
        const active = stage === s.id;
        return (
          <React.Fragment key={s.id}>
            <button onClick={() => setStage(s.id)} style={{
              display: "flex", alignItems: "center", gap: 11, padding: "11px 20px",
              background: active ? "var(--bg-2)" : "transparent", border: "none", cursor: "pointer",
              borderBottom: "2px solid " + (active ? "var(--cyan)" : "transparent"),
              transition: "background 120ms",
            }}>
              <Mono color={active ? "var(--cyan)" : "var(--fg-3)"} size={10} weight={600}>{s.num}</Mono>
              <div style={{ textAlign: "left" }}>
                <div style={{ fontFamily: "var(--head)", fontSize: 13, fontWeight: 600, color: active ? "var(--fg)" : "var(--fg-2)" }}>{s.label}</div>
                <Label style={{ fontSize: 8 }}>{s.sub}</Label>
              </div>
              {s.id === "plan" && summary.flagged > 0 && (
                <Pill kind="flagged" style={{ marginLeft: 2 }}>{summary.flagged}</Pill>
              )}
              {s.id === "plan" && summary.flagged === 0 && (
                <VerdictDot verdict="VERIFIED" size={6} />
              )}
            </button>
            {i < STAGES.length - 1 && (
              <div style={{ display: "flex", alignItems: "center", color: "var(--fg-3)", padding: "0 2px" }}>
                <Mono color="var(--fg-3)" size={12}>→</Mono>
              </div>
            )}
          </React.Fragment>
        );
      })}
    </div>
  );
}

function Header() {
  return (
    <div style={{
      display: "flex", alignItems: "center", justifyContent: "space-between",
      padding: "10px 20px", borderBottom: "1px solid var(--line)", background: "var(--bg-1)",
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
        <div style={{ width: 16, height: 16, background: "linear-gradient(135deg, var(--cyan), var(--indigo))", display: "grid", placeItems: "center", boxShadow: "0 0 12px rgba(103,232,249,0.35)" }}>
          <span style={{ width: 4, height: 4, background: "var(--bg-0)" }} />
        </div>
        <div>
          <div style={{ fontFamily: "var(--head)", fontSize: 13, fontWeight: 600, letterSpacing: "-0.01em", background: "linear-gradient(90deg, var(--cyan), var(--indigo))", WebkitBackgroundClip: "text", backgroundClip: "text", color: "transparent" }}>
            OVERMONT V2 · COMMAND CENTER
          </div>
          <Mono color="var(--fg-3)" size={9} style={{ letterSpacing: "0.16em" }}>GAME PLAN AUDIT · MODULE 08</Mono>
        </div>
      </div>
      <div style={{ display: "flex", alignItems: "center", gap: 18 }}>
        <span style={{ fontFamily: "var(--mono)", fontSize: 10, fontWeight: 700, letterSpacing: "0.18em", padding: "4px 10px", borderRadius: 3, color: "var(--amber)", background: "color-mix(in oklab, var(--amber) 16%, transparent)", border: "1px solid color-mix(in oklab, var(--amber) 50%, transparent)" }}>● PAPER</span>
        <Mono color="var(--fg-3)" size={10} style={{ letterSpacing: "0.12em" }}>D5 GATE · v0.4.2</Mono>
        <Clock />
      </div>
    </div>
  );
}

// ---- Tweak panel (host protocol, like the AXIOM terminal) ----
function TweakPanel({ open, onClose, state, set }) {
  if (!open) return null;
  const Group = ({ label, children }) => (
    <div style={{ marginBottom: 18 }}>
      <Label style={{ display: "block", marginBottom: 8 }}>{label}</Label>
      {children}
    </div>
  );
  const Seg = ({ value, options }) => (
    <div style={{ display: "flex", gap: 4, flexWrap: "wrap" }}>
      {options.map((o) => (
        <button key={o.v} onClick={() => set(value, o.v)} style={{
          padding: "6px 11px", cursor: "pointer", borderRadius: 2,
          background: state[value] === o.v ? "var(--cyan)" : "var(--bg-2)",
          color: state[value] === o.v ? "var(--bg-0)" : "var(--fg-2)",
          border: "1px solid " + (state[value] === o.v ? "var(--cyan)" : "var(--line)"),
          fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.06em",
        }}>{o.l}</button>
      ))}
    </div>
  );
  return (
    <div style={{
      position: "fixed", top: 0, right: 0, bottom: 0, width: 300, zIndex: 200,
      background: "var(--bg-1)", borderLeft: "1px solid var(--line)", boxShadow: "-12px 0 40px rgba(0,0,0,0.4)",
      padding: "16px 18px", overflowY: "auto",
    }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 18 }}>
        <Mono color="var(--fg)" size={12} weight={600} style={{ letterSpacing: "0.14em" }}>TWEAKS</Mono>
        <button onClick={onClose} style={{ background: "transparent", border: "none", color: "var(--fg-3)", cursor: "pointer", fontFamily: "var(--mono)", fontSize: 14 }}>✕</button>
      </div>
      <Group label="Verdict presentation">
        <Seg value="flagStyle" options={[{ v: "banner", l: "Banner" }, { v: "stamp", l: "Stamp" }, { v: "minimal", l: "Minimal" }]} />
        <Mono color="var(--fg-3)" size={9} style={{ marginTop: 8, display: "block", lineHeight: 1.5 }}>
          how the VERIFIED / NEEDS-REVIEW verdict reads at the top of each audit.
        </Mono>
      </Group>
      <Group label="Accent">
        <Seg value="accent" options={[{ v: "cyan", l: "Cyan" }, { v: "indigo", l: "Indigo" }, { v: "emerald", l: "Emerald" }]} />
      </Group>
      <Group label="Density">
        <Seg value="density" options={[{ v: "regular", l: "Regular" }, { v: "compact", l: "Compact" }]} />
      </Group>
      <Group label="Scanlines">
        <Seg value="scanlines" options={[{ v: "on", l: "On" }, { v: "off", l: "Off" }]} />
      </Group>
    </div>
  );
}

// ---- no-data state: artifact present but empty / not today's briefing ----
function NoBriefing() {
  return (
    <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", padding: 24 }}>
      <div style={{ textAlign: "center", maxWidth: 460 }}>
        <Mono color="var(--fg-2)" size={13} weight={600} style={{ letterSpacing: "0.1em" }}>NO BRIEFING TO AUDIT</Mono>
        <Mono color="var(--fg-3)" size={11} style={{ marginTop: 12, display: "block", lineHeight: 1.7 }}>
          /api/strategy-rationale returned no decisions. Either the D5 gate has
          not produced today's plan yet, or the artifact is empty. Seed one with{" "}
          <code style={{ color: "var(--cyan)" }}>python scripts/seed_sample_briefing.py</code> and reload.
        </Mono>
      </div>
    </div>
  );
}

function App() {
  const decisions = window.GP.DATA.decisions;
  const summary = aM(() => window.GP.verify.summary(decisions), [decisions]);
  const [stage, setStage] = aS("plan");
  const [jumpId, setJumpId] = aS(null);
  const [tweakOpen, setTweakOpen] = aS(false);

  const TWEAKS = /*EDITMODE-BEGIN*/{
    "flagStyle": "banner",
    "accent": "cyan",
    "density": "regular",
    "scanlines": "on"
  }/*EDITMODE-END*/;
  const [state, setState] = aS(TWEAKS);
  const set = (k, v) => setState((s) => ({ ...s, [k]: v }));

  // jump from radar/selection/execution into a specific plan decision
  const jumpToDecision = (id) => { setJumpId(id); setStage("plan"); };

  aE(() => {
    window.parent.postMessage({ type: "__edit_mode_set_keys", edits: state }, "*");
    document.body.setAttribute("data-density", state.density);
    document.body.setAttribute("data-scanlines", state.scanlines);
    document.body.setAttribute("data-accent", state.accent);
  }, [state]);

  aE(() => {
    const handler = (e) => {
      if (e.data?.type === "__activate_edit_mode") setTweakOpen(true);
      if (e.data?.type === "__deactivate_edit_mode") setTweakOpen(false);
    };
    window.addEventListener("message", handler);
    window.parent.postMessage({ type: "__edit_mode_available" }, "*");
    return () => window.removeEventListener("message", handler);
  }, []);

  return (
    <div style={{ display: "flex", flexDirection: "column", height: "100vh", background: "var(--bg-0)" }}>
      <Header />
      <PipelineNav stage={stage} setStage={setStage} summary={summary} />
      <div style={{ flex: 1, minHeight: 0, position: "relative" }}>
        <div style={{ position: "absolute", inset: 0 }}>
          {decisions.length === 0 ? <NoBriefing /> : (
            <>
              {stage === "radar" && <RadarView onJump={jumpToDecision} />}
              {stage === "selection" && <SelectionView onJump={jumpToDecision} />}
              {stage === "plan" && <PlanView flagStyle={state.flagStyle} initialId={jumpId} />}
              {stage === "execution" && <ExecutionView onJump={jumpToDecision} />}
            </>
          )}
        </div>
      </div>
      <TweakPanel open={tweakOpen} onClose={() => setTweakOpen(false)} state={state} set={set} />
    </div>
  );
}

// ---- token entry (§5): React surface for the localStorage Bearer contract ----
// The backend auth contract is unchanged — the password field IS the server's
// `CENTAUR_API_TOKEN`, stored browser-only in localStorage["centaur.token"]. An
// optional base-URL override targets a non-origin server (localStorage
// ["centaur.baseUrl"]); blank = same origin. Submitting re-runs window.GP.load().
function TokenEntry({ status, onRetry }) {
  const [base, setBase] = aS(localStorage.getItem("centaur.baseUrl") || "");
  const [token, setToken] = aS(localStorage.getItem("centaur.token") || "");
  const [err, setErr] = aS(status ? `auth rejected · ${status}` : null);

  const connect = () => {
    if (!token.trim()) { setErr("a bearer token is required"); return; }
    const b = base.trim().replace(/\/$/, "");
    if (b) localStorage.setItem("centaur.baseUrl", b);
    else localStorage.removeItem("centaur.baseUrl");
    localStorage.setItem("centaur.token", token.trim());
    setErr(null);
    onRetry();
  };

  const field = {
    width: "100%", boxSizing: "border-box", padding: "9px 11px", marginTop: 6,
    background: "var(--bg-0)", border: "1px solid var(--line)", borderRadius: 3,
    color: "var(--fg)", fontFamily: "var(--mono)", fontSize: 12, letterSpacing: "0.04em",
  };
  const lbl = { fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.16em", textTransform: "uppercase", color: "var(--fg-3)" };

  return (
    <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", padding: 24, background: "var(--bg-0)" }}>
      <div style={{ width: "100%", maxWidth: 360, background: "var(--bg-1)", border: "1px solid var(--line)", borderRadius: 6, padding: "22px 22px 18px" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 4 }}>
          <div style={{ width: 14, height: 14, background: "linear-gradient(135deg, var(--cyan), var(--indigo))", display: "grid", placeItems: "center" }}>
            <span style={{ width: 4, height: 4, background: "var(--bg-0)" }} />
          </div>
          <Mono color="var(--fg)" size={12} weight={600} style={{ letterSpacing: "0.14em" }}>GAME PLAN AUDIT</Mono>
        </div>
        <Mono color="var(--fg-3)" size={10} style={{ display: "block", marginBottom: 18, lineHeight: 1.6 }}>
          locked · provide the server bearer token to unlock the audit.
        </Mono>

        <label style={{ ...lbl, display: "block" }}>
          Server <span style={{ textTransform: "none", letterSpacing: 0, color: "var(--fg-3)" }}>(blank = this origin)</span>
          <input style={field} value={base} placeholder={location.origin}
                 onChange={(e) => setBase(e.target.value)}
                 onKeyDown={(e) => { if (e.key === "Enter") connect(); }} />
        </label>
        <div style={{ height: 12 }} />
        <label style={{ ...lbl, display: "block" }}>
          Bearer token
          <input style={field} type="password" value={token} autoFocus
                 placeholder="CENTAUR_API_TOKEN"
                 onChange={(e) => setToken(e.target.value)}
                 onKeyDown={(e) => { if (e.key === "Enter") connect(); }} />
        </label>

        {err && (
          <div style={{ marginTop: 12, padding: "7px 10px", background: "color-mix(in oklab, var(--rose) 12%, transparent)", borderLeft: "2px solid var(--rose)", borderRadius: 2 }}>
            <Mono color="var(--rose)" size={10}>{err}</Mono>
          </div>
        )}

        <button onClick={connect} style={{
          width: "100%", marginTop: 16, padding: "10px", cursor: "pointer", borderRadius: 3,
          background: "var(--cyan)", color: "var(--bg-0)", border: "1px solid var(--cyan)",
          fontFamily: "var(--mono)", fontSize: 11, fontWeight: 700, letterSpacing: "0.14em", textTransform: "uppercase",
        }}>Unlock</button>

        <Mono color="var(--fg-3)" size={9} style={{ display: "block", marginTop: 12, lineHeight: 1.6 }}>
          stored browser-only in <code style={{ color: "var(--cyan)" }}>localStorage</code>. generate server-side with{" "}
          <code style={{ color: "var(--cyan)" }}>python -c "import secrets; print(secrets.token_urlsafe(32))"</code>.
        </Mono>
      </div>
    </div>
  );
}

// ---- boot lifecycle (§4.3): loading → token | error | ready ----
function BootScreen({ children }) {
  return <div className="gp-boot"><div className="gp-boot-inner">{children}</div></div>;
}

function Boot() {
  const [phase, setPhase] = aS("loading"); // loading | token | error | ready
  const [err, setErr] = aS(null);

  const run = () => {
    setPhase("loading");
    window.GP.load()
      .then(() => setPhase("ready"))
      .catch((e) => {
        setErr(e);
        setPhase(e && (e.status === 401 || e.status === 403) ? "token" : "error");
      });
  };

  aE(() => { run(); }, []);

  if (phase === "ready") return <App />;
  if (phase === "token") return <TokenEntry status={err && err.status} onRetry={run} />;
  if (phase === "error") {
    return (
      <BootScreen>
        failed to load briefing<br /><br />
        <code>{err && err.message ? err.message : "unknown error"}</code>
      </BootScreen>
    );
  }
  return <BootScreen>resolving audit…</BootScreen>;
}

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