/* Sentinel — app root: role switching, navigation, decisions, tweaks, toasts. */

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "consoleLayout": "split",
  "redline": "inline",
  "aiPanel": "full",
  "riskStyle": "badge",
  "density": "default"
}/*EDITMODE-END*/;

function Toasts({ toasts, onDismiss }) {
  return (
    <div style={{ position: "fixed", bottom: 20, right: 20, zIndex: 80, display: "flex", flexDirection: "column", gap: 10, width: 400 }}>
      {toasts.map((t) => (
        <div key={t.id} style={{ display: "flex", alignItems: "flex-start", gap: 12, background: "var(--bg-surface)", border: "1px solid var(--border-base)",
          borderRadius: "var(--radius-lg)", boxShadow: "var(--elev-3)", padding: "14px 16px", animation: "gjToastIn var(--motion-base) var(--ease-decelerate)" }}>
          <span style={{ width: 22, height: 22, borderRadius: "50%", flex: "none", display: "flex", alignItems: "center", justifyContent: "center", color: "#fff", marginTop: 1,
            background: t.tone === "success" ? "var(--status-success)" : t.tone === "danger" ? "var(--status-danger)" : "var(--status-info)" }}>
            <Icon name={t.tone === "success" ? "check" : t.tone === "danger" ? "x" : "info"} size={13} strokeWidth={3} /></span>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 14, fontWeight: 600, color: "var(--content-strong)" }}>{t.title}</div>
            {t.body && <div style={{ fontSize: 13, color: "var(--content-muted)", marginTop: 2, lineHeight: 1.45 }}>{t.body}</div>}
          </div>
          <button onClick={() => onDismiss(t.id)} style={{ background: "none", border: "none", color: "var(--content-faint)", cursor: "pointer", padding: 2, flex: "none" }}><Icon name="x" size={15} /></button>
        </div>
      ))}
    </div>
  );
}

/* Low/medium items the Head of SP Contracting has already decided —
   so the CEO can review and override prior decisions. */
const SEED_DECISIONS = {
  "CT-2841:C5": { action: "accept", by: "Daniel Otieno", when: "earlier today" },
  "CT-2841:C6": { action: "accept", by: "Daniel Otieno", when: "earlier today" },
  "CT-2842:C3": { action: "edit", by: "Daniel Otieno", when: "earlier today" },
  "CT-2843:C4": { action: "accept", by: "Daniel Otieno", when: "earlier today" },
};

function App() {
  const [tw, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [authed, setAuthed] = React.useState(false);
  const [email, setEmail] = React.useState("");
  const [role, setRole] = React.useState("head");
  const [nav, setNav] = React.useState("queue");
  const [openId, setOpenId] = React.useState(null);
  const [contracts, setContracts] = React.useState(CONTRACTS);
  const [decisions, setDecisions] = React.useState(SEED_DECISIONS);
  const [killSwitch, setKill] = React.useState(false);
  const [medHigh, setMedHigh] = React.useState("head_ceo");
  const [search, setSearch] = React.useState("");
  const [toasts, setToasts] = React.useState([]);
  const [auditLog, setAuditLog] = React.useState(AUDIT);

  const ctxVal = { consoleLayout: tw.consoleLayout, redline: tw.redline, aiPanel: tw.aiPanel, riskStyle: tw.riskStyle, density: tw.density };

  const pushToast = (t) => { const id = Date.now() + Math.random(); setToasts((ts) => [...ts, { ...t, id }]); setTimeout(() => setToasts((ts) => ts.filter((x) => x.id !== id)), 5000); };
  const dismiss = (id) => setToasts((ts) => ts.filter((x) => x.id !== id));

  const changeRole = (r) => {
    setRole(r); setOpenId(null);
    setNav(r === "admin" ? "admin" : "queue");
  };
  const onAuthed = (r, em) => { setRole(r); setEmail(em); setAuthed(true); setOpenId(null); setNav(r === "admin" ? "admin" : "queue"); };
  const signOut = () => { setAuthed(false); setOpenId(null); setNav("queue"); };
  const navigate = (n) => { setNav(n); setOpenId(null); };

  const openContract = (id) => { setOpenId(id); setNav("queue"); };

  const decide = (contract, ch, action, finalText) => {
    const key = contract.id + ":" + ch.id;
    setDecisions((d) => {
      const next = { ...d };
      if (!action) { delete next[key]; return next; }
      next[key] = { action, by: ROLES[role].name, finalText: finalText || ch.counter, at: Date.now() };
      return next;
    });
    if (action) {
      const verb = action === "accept" ? "Counter accepted" : action === "edit" ? "Accepted with edit" : "Change rejected";
      setAuditLog((l) => [{ id: "AU-" + Date.now(), actor: ROLES[role].name, role: ROLES[role].avatarRole, action: "decision", target: contract.id,
        detail: `${CLAUSES[ch.clause].title} (${ch.clauseRef}) · ${RISK[ch.risk].label} · ${verb.toLowerCase()}`, mins: 0, hash: Math.random().toString(16).slice(2, 6) + "…" + Math.random().toString(16).slice(2, 5) }, ...l]);
    }
  };

  const generate = (contract) => {
    const elapsed = Math.round((Date.now() - contract.uploadedMs) / 60000);
    setContracts((cs) => cs.map((c) => c.id === contract.id ? { ...c, status: "cleared", clearedMin: Math.min(elapsed, 235) } : c));
    setAuditLog((l) => [{ id: "AU-" + Date.now(), actor: ROLES[role].name, role: ROLES[role].avatarRole, action: "output", target: contract.id,
      detail: `Clean + redlined .docx generated and emailed to ${contract.rm} (RM) and reviewer · Slack posted`, mins: 0, hash: Math.random().toString(16).slice(2, 6) + "…" + Math.random().toString(16).slice(2, 5) }, ...l]);
    setOpenId(null);
    pushToast({ tone: "success", title: "Outputs generated · " + contract.id, body: `Clean + redlined .docx emailed to ${contract.rm} and you. Slack notification posted to #sp-contracting.` });
  };

  const queueCount = contracts.filter((c) => role !== "admin" && (role === "rm" ? c.status !== "cleared" : inQueueFor(role, c, decisions))).length;
  const openContractObj = openId ? contracts.find((c) => c.id === openId) : null;

  const titles = {
    queue: role === "rm" ? ["My contracts", "Provider edits you've uploaded · status & turnaround"] : role === "ceo" ? ["Review queue", "Contracts with a high-risk item awaiting your decision"] : ["Review queue", "Substantive changes routed to you for a decision"],
    upload: ["Upload contract", "Diff against our standard, catch omissions, classify & route"],
    tracker: ["Change tracker", "Every clause change and its variations across rounds"],
    sla: ["SLA dashboard", "Turnaround against the 4-hour SLA, end to end"],
    audit: ["Audit trail", "Immutable, tamper-evident record of every action"],
    admin: ["Rules & routing", "Clause rules, risk tiers, routing line & kill-switch"],
    access: ["Users & access", "Domains, OTP 2FA, roles & permissions"],
  };
  let title, subtitle;
  if (openContractObj) { title = "Review console"; subtitle = null; }
  else [title, subtitle] = titles[nav] || titles.queue;

  const showSearch = !openContractObj && ["queue", "tracker", "audit"].includes(nav);

  let body;
  if (openContractObj) body = <ReviewConsole contract={openContractObj} role={role} decisions={decisions} onBack={() => setOpenId(null)} onDecide={(ch, a, t) => decide(openContractObj, ch, a, t)} onGenerate={() => generate(openContractObj)} />;
  else if (nav === "queue") body = <ContractQueue role={role} contracts={contracts} decisions={decisions} search={search} onOpen={openContract} />;
  else if (nav === "upload") body = <UploadFlow onDone={() => setNav("queue")} onOpenContract={openContract} />;
  else if (nav === "tracker") body = <Tracker search={search} />;
  else if (nav === "sla") body = <SlaDashboard contracts={contracts} decisions={decisions} />;
  else if (nav === "audit") body = <AuditTrail log={auditLog} search={search} />;
  else if (nav === "admin") body = <AdminConsole killSwitch={killSwitch} onKill={setKill} medHighRoute={medHigh} onMedHigh={setMedHigh} />;
  else if (nav === "access") body = <AccessConsole />;

  if (!authed) return <LoginScreen onAuthed={onAuthed} />;

  return (
    <SentinelCtx.Provider value={ctxVal}>
      <AppShell role={role} current={nav} onNavigate={navigate} onRole={changeRole} queueCount={queueCount} killSwitch={killSwitch}
        topbar={<Topbar title={title} subtitle={subtitle} role={role} onRole={changeRole} onSignOut={signOut} killSwitch={killSwitch}
          search={showSearch ? search : null} onSearch={showSearch ? setSearch : null}
          actions={role === "rm" && !openContractObj ? <Button leadingIcon="file-up" onClick={() => setNav("upload")}>Upload edit</Button> : null} />}>
        <div style={{ height: "100%", display: "flex", flexDirection: "column", minHeight: 0 }}>{body}</div>
      </AppShell>

      <TweaksPanel>
        <TweakSection label="Review console" />
        <TweakRadio label="Layout" value={tw.consoleLayout} options={[{ value: "split", label: "Split" }, { value: "document", label: "Document" }, { value: "cards", label: "Cards" }]} onChange={(v) => setTweak("consoleLayout", v)} />
        <TweakRadio label="Redline" value={tw.redline} options={[{ value: "inline", label: "Inline" }, { value: "side", label: "Side-by-side" }]} onChange={(v) => setTweak("redline", v)} />
        <TweakSection label="AI co-pilot" />
        <TweakRadio label="Panel depth" value={tw.aiPanel} options={[{ value: "full", label: "Full" }, { value: "compact", label: "Compact" }, { value: "minimal", label: "Minimal" }]} onChange={(v) => setTweak("aiPanel", v)} />
        <TweakSection label="Risk language" />
        <TweakRadio label="Style" value={tw.riskStyle} options={[{ value: "badge", label: "Badges" }, { value: "dots", label: "Dots" }, { value: "bar", label: "Bars" }]} onChange={(v) => setTweak("riskStyle", v)} />
      </TweaksPanel>
      <Toasts toasts={toasts} onDismiss={dismiss} />
    </SentinelCtx.Provider>
  );
}

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