/* global React, Icon, StatusChip, PageHeader, Banner, Avatar, LEVER_LIB, SEVERITY, PEOPLE, Drawer, DrawerHead, Meta, useScenarios, ScenStore, seedLevers, SNAPSHOT, useActionDrawer */
const { useState: useStateS, useEffect: useEffectS } = React;

const STRESS_HELP = {
  title: "Stress Lab",
  purpose: "A governed early-warning workspace for the Treasury & Liquidity team. Build a named scenario from typed levers, preview which workpaper rows and ratios move, save it through maker-checker, re-run it on the latest approved snapshot, compare runs over time, and hand breaches to Recovery — without ever touching the official baseline.",
  first: "The purple analysis-only banner is permanent. The library is the shelf of governed scenarios; Build & impact is the workbench; reverse stress is the breach cliff; run history is the audit trail.",
  terms: [
    ["Scenario", "A named, owned, evidence-tagged bundle of levers with a lifecycle."],
    ["Lever", "One assumption moved from base to stressed, with a unit and value."],
    ["Internal preview", "Analysis-only impact computed on the approved snapshot — not the official engine."],
    ["Approved internal", "Cleared by a checker (≠ maker). Citable in a board pack, never official."],
  ],
  next: ["Open a library scenario to Edit or Run, or build a new one.", "Adjust levers and watch the internal preview.", "Save draft, then submit for internal review.", "Hand a breach to Recovery Planning."],
  connections: ["Preview impacts open workpaper rows.", "Breaches trigger Recovery Planning.", "Runs bind to approved source snapshots; assumptions carry evidence."],
  guardrail: "Analysis-only. Re-runs use the latest approved source snapshot (no live execution). Approval is internal only; a maker cannot approve their own scenario. Official baselines never change and export stays blocked.",
};

const LU = { "%": "%", bps: " bps", pp: "pp", "notch": " notch", "\u00d7": "\u00d7", "Br M": " M" };
const levVal = (v, unit) => unit === "Br M" ? "Br " + v + "M" : v + (LU[unit] || "");
const leverMeta = {}; LEVER_LIB.forEach(g => g.levers.forEach(l => { leverMeta[g.type + "|" + l.name] = { ...l, type: g.type }; }));

/* lightweight analysis-only preview — derives stressed ratios from lever values */
function computePreview(levers) {
  const v = k => levers[k]; const has = k => v(k) != null;
  const retail = has("Liquidity / Behavioral|Retail deposit run-off") ? v("Liquidity / Behavioral|Retail deposit run-off") : 7;
  const sme = has("Liquidity / Behavioral|SME deposit run-off") ? v("Liquidity / Behavioral|SME deposit run-off") : 10;
  const inflow = has("Liquidity / Behavioral|Inflow realisation") ? v("Liquidity / Behavioral|Inflow realisation") : 100;
  const haircut = has("Market / FX|HQLA Level 2A haircut") ? v("Market / FX|HQLA Level 2A haircut") : 15;
  const dep = has("Market / FX|FX depreciation — USD") ? v("Market / FX|FX depreciation — USD") : 0;
  const wd = has("Capital|Cross-holding write-down") ? v("Capital|Cross-holding write-down") : 0;
  const mig = has("Credit|Rating migration (downgrade)") ? v("Credit|Rating migration (downgrade)") : 0;
  const npl = has("Credit|NPL surge") ? v("Credit|NPL surge") : 0;
  const undrawn = has("Liquidity / Behavioral|Undrawn commitment draw-down") ? v("Liquidity / Behavioral|Undrawn commitment draw-down") : 10;

  const lcr = Math.max(40, Math.round(265 * (1 - (retail - 7) * 0.038 - (sme - 10) * 0.012 - (100 - inflow) * 0.006 - (haircut - 15) * 0.004)));
  const nsfr = Math.max(70, Math.round(121 - (undrawn - 10) * 0.25 - (100 - inflow) * 0.15));
  const surv = Math.max(8, Math.round(94 - (retail - 7) * 3.0 - (sme - 10) * 0.9 - (100 - inflow) * 0.4));
  const rwaInfl = mig * 2 + npl * 0.12;
  const car = +(13.0 * (1 - wd / 100 * 0.012) / (1 + rwaInfl / 100)).toFixed(1);
  const nop = +(7.3 + dep * 0.48).toFixed(1);

  return [
    { metric: "LCR ratio", base: "265%", stress: lcr + "%", breach: lcr < 100, limit: "≥ 100%", route: "lcr" },
    { metric: "Survival horizon", base: "94 days", stress: surv + " days", breach: surv < 30, limit: "≥ 30 days", route: "workpaper" },
    { metric: "NSFR ratio", base: "121%", stress: nsfr + "%", breach: nsfr < 100, limit: "≥ 100%", route: "lcr" },
    { metric: "CAR (total capital)", base: "13.0%", stress: car + "%", breach: car < 11, limit: "≥ 11%", route: "capital" },
    { metric: "Net open position", base: "7.3%", stress: nop + "%", breach: nop > 18, limit: "≤ ±18%", route: "market" },
  ];
}

const ST_META = {
  DRAFT: ["Draft", "chip-neutral"], REVIEW_PENDING: ["Review pending", "chip-amber"],
  APPROVED_INTERNAL: ["Approved · internal", "chip-green"], ARCHIVED: ["Archived", "chip-slate"],
};
function ScenChip({ s }) { const m = ST_META[s] || ["", "chip-neutral"]; return <span className={"chip " + m[1]}><span className="dot" />{m[0]}</span>; }

function StressLab({ go, info }) {
  const scenarios = useScenarios();
  const [tab, setTab] = useStateS("Build & impact");
  const [editId, setEditId] = useStateS(null);       // scenario being edited, or null = new
  const [draft, setDraft] = useStateS(null);          // working copy
  const [trace, setTrace] = useStateS(null);
  const [toast, setToast] = useStateS(null);
  const [actionNode, openAction] = useActionDrawer();

  // open a scenario into the builder (Edit/Run) or start new
  const openScenario = (sc) => {
    setEditId(sc ? sc.id : null);
    setDraft(sc ? JSON.parse(JSON.stringify(sc)) : { name: "", family: "Liquidity", severity: "Severe", owner: "dawit", checker: "", state: "DRAFT", version: 1, snapshot: SNAPSHOT, levers: {} });
    setTab("Build & impact");
  };
  // ensure a draft exists when on Build & impact
  useEffectS(() => { if (tab === "Build & impact" && !draft) openScenario(scenarios[0]); }, [tab]);

  const flash = (m) => { setToast(m); setTimeout(() => setToast(null), 2200); };

  return (
    <div className="main-inner">
      <PageHeader eyebrow="Governance · Treasury & Liquidity" title="Stress Lab" onInfo={() => info(STRESS_HELP)}
        right={<span className="center gap8"><StatusChip s="STRESS_ONLY" lg /><button className="btn btn-ghost btn-sm" onClick={() => go("recovery")}><Icon name="refresh" size={15} /> Recovery Planning</button></span>} />

      <div style={{ marginBottom: 16 }}><Banner kind="indigo" icon="flask"><b>Analysis-only workspace.</b> Every figure is a scenario result on the latest <b>approved source snapshot</b>. Stress values never merge into official baselines, returns, or approvals — and official export stays blocked.</Banner></div>

      <div className="seg-ctl mb20">
        {["Scenario library", "Build & impact", "Reverse stress", "Run history"].map(t => <button key={t} className={tab === t ? "on" : ""} onClick={() => setTab(t)}>{t}</button>)}
      </div>

      {tab === "Scenario library" && <ScenarioLibrary scenarios={scenarios} onEdit={openScenario} onNew={() => openScenario(null)} />}

      {tab === "Build & impact" && draft && <Builder draft={draft} setDraft={setDraft} editId={editId} go={go} onTrace={setTrace} flash={flash}
        onSaved={(id) => { setEditId(id); }} />}

      {tab === "Reverse stress" && <ReversePanel go={go} />}
      {tab === "Run history" && <RunHistory openAction={openAction} />}

      {trace && <TraceDrawer trace={trace} onClose={() => setTrace(null)} go={go} />}
      {toast && <div className="toast"><Icon name="check" /> {toast}</div>}
      {actionNode}
    </div>
  );
}

/* ============================================================
   BUILDER — identity bar · lever editor · live preview · governance
   ============================================================ */
function Builder({ draft, setDraft, editId, go, onTrace, flash, onSaved }) {
  const preview = computePreview(draft.levers);
  const breaches = preview.filter(p => p.breach).length;
  const activeKeys = Object.keys(draft.levers);
  const setField = (k, val) => setDraft({ ...draft, [k]: val });
  const toggleLever = (key) => {
    const m = { ...draft.levers };
    if (key in m) delete m[key]; else { const lm = leverMeta[key]; m[key] = lm.bands[["Mild", "Moderate", "Severe", "Extreme"].indexOf(draft.severity)] ?? lm.bands[2]; }
    setDraft({ ...draft, levers: m });
  };
  const setLever = (key, val) => setDraft({ ...draft, levers: { ...draft.levers, [key]: +val } });
  const isOwnerChecker = draft.owner === draft.checker && draft.checker !== "";

  const save = (state) => {
    const obj = { ...draft, state: state || draft.state, id: editId || undefined, _bumpVersion: !!editId };
    const saved = ScenStore.save(obj); onSaved(saved.id);
    flash(state === "REVIEW_PENDING" ? "Submitted for internal review" : "Saved as draft");
  };
  const approve = () => { if (isOwnerChecker) { flash("A maker cannot approve their own scenario"); return; } ScenStore.setState(editId, "APPROVED_INTERNAL"); flash("Approved · internal"); };
  const run = () => { ScenStore.recordRun(editId, breaches + " breach" + (breaches === 1 ? "" : "es")); flash("Run recorded · snapshot " + SNAPSHOT); };
  const rerun = () => { ScenStore.recordRun(editId, breaches + " breach" + (breaches === 1 ? "" : "es")); flash("Re-run recorded on latest snapshot " + SNAPSHOT); };

  return (
    <div>
      {/* identity bar */}
      <div className="card card-pad mb16">
        <div className="between" style={{ alignItems: "flex-start", gap: 16, flexWrap: "wrap" }}>
          <div style={{ flex: "1 1 260px", minWidth: 0 }}>
            <div className="section-label mb4">{editId ? "Editing scenario" : "New scenario"}</div>
            <input value={draft.name} onChange={e => setField("name", e.target.value)} placeholder="Name this scenario…"
              style={{ width: "100%", fontSize: 19, fontWeight: 800, letterSpacing: "-.02em", border: "none", outline: "none", background: "transparent", color: "var(--ink)" }} />
          </div>
          <div className="center gap8 wrap" style={{ flex: "0 0 auto" }}>
            <ScenChip s={draft.state} />
            <span className="chip chip-neutral">v{draft.version || 1}</span>
            <span className="chip chip-neutral"><Icon name="database" size={11} /> {draft.snapshot}</span>
          </div>
        </div>
        <div className="grid g4 mt12" style={{ gap: 12 }}>
          <Field label="Family"><select value={draft.family} onChange={e => setField("family", e.target.value)}>{["Liquidity", "Market", "Credit", "Capital", "Combined"].map(f => <option key={f}>{f}</option>)}</select></Field>
          <Field label="Severity"><select value={draft.severity} onChange={e => setField("severity", e.target.value)}>{SEVERITY.concat(["Custom"]).map(s => <option key={s}>{s}</option>)}</select></Field>
          <Field label="Owner (maker)"><select value={draft.owner} onChange={e => setField("owner", e.target.value)}>{Object.keys(PEOPLE).map(k => <option key={k} value={k}>{PEOPLE[k].name}</option>)}</select></Field>
          <Field label="Checker"><select value={draft.checker} onChange={e => setField("checker", e.target.value)}><option value="">Unassigned</option>{Object.keys(PEOPLE).map(k => <option key={k} value={k}>{PEOPLE[k].name}</option>)}</select></Field>
        </div>
        {isOwnerChecker && <div className="mt8"><Banner kind="amber" icon="alert">Owner and checker are the same person — a maker cannot approve their own scenario. Assign a different checker.</Banner></div>}
      </div>

      <div className="grid" style={{ gridTemplateColumns: "380px 1fr", gap: 18, alignItems: "start" }}>
        {/* lever editor */}
        <div className="card card-pad">
          <div className="panel-h"><h3 style={{ fontSize: 15 }}>Lever editor</h3><span className="chip chip-indigo" style={{ marginLeft: "auto" }}>{activeKeys.length} active</span></div>
          {LEVER_LIB.map(grp => (
            <div key={grp.type} style={{ marginBottom: 14 }}>
              <div className="center gap8 mb8" style={{ color: "var(--ink-2)" }}><Icon name={grp.icon} size={14} /><span className="section-label">{grp.type}</span></div>
              {grp.levers.map(l => {
                const key = grp.type + "|" + l.name; const on = key in draft.levers; const val = draft.levers[key];
                return (
                  <div key={l.name} style={{ padding: "7px 6px", borderRadius: 8, background: on ? "var(--surface-2)" : "transparent", marginBottom: 3 }}>
                    <div className="lev-row" style={{ padding: 0 }} onClick={() => toggleLever(key)}>
                      <span className={"lev-tog" + (on ? " on" : "")}>{on && <Icon name="check" size={11} />}</span>
                      <div className="grow" style={{ minWidth: 0 }}>
                        <div style={{ fontSize: 12.5, fontWeight: 600 }}>{l.name}</div>
                        <div className="center gap6 mt4 wrap">{l.drives.map(d => <span key={d} className="chip chip-neutral" style={{ fontSize: 10, padding: "1px 6px" }}>{d}</span>)}<span className="chip" style={{ fontSize: 10, padding: "1px 6px", background: /HQLA|run-off|outflow|inflow|FX deprec|rate/i.test(l.name) ? "var(--green-soft)" : "var(--amber-soft)", color: /HQLA|run-off|outflow|inflow|FX deprec|rate/i.test(l.name) ? "var(--green-ink)" : "var(--amber)", border: "1px solid " + (/HQLA|run-off|outflow|inflow|FX deprec|rate/i.test(l.name) ? "var(--green-line)" : "var(--amber-line)") }} title={/HQLA|run-off|outflow|inflow|FX deprec|rate/i.test(l.name) ? "Concept defined by NBE directive; magnitude is bank-set" : "Bank-policy assumption — needs board-approved evidence"}>{/HQLA|run-off|outflow|inflow|FX deprec|rate/i.test(l.name) ? "NBE-defined" : "bank-policy"}</span></div>
                      </div>
                    </div>
                    {on && <div className="center gap8" style={{ marginTop: 8, paddingLeft: 28 }} onClick={e => e.stopPropagation()}>
                      <input type="range" className="lev-slider" min={l.bands[0]} max={l.bands[3]} step={l.unit === "notch" ? 1 : (l.bands[3] - l.bands[0]) > 50 ? 5 : 1} value={val} onChange={e => setLever(key, e.target.value)} style={{ flex: 1 }} />
                      <span className="chip chip-indigo tnum" style={{ fontSize: 10.5, minWidth: 48, justifyContent: "center" }}>{levVal(val, l.unit)}</span>
                    </div>}
                  </div>
                );
              })}
            </div>
          ))}
        </div>

        {/* live internal preview + governance */}
        <div className="col" style={{ gap: 16 }}>
          <div className="card">
            <div className="panel-h" style={{ padding: "16px 20px 0" }}><h3 style={{ fontSize: 15 }}>Internal impact preview</h3><span className="sub">analysis-only · on approved snapshot · click a metric to trace</span><div className="right"><StatusChip s="STRESS_ONLY" /></div></div>
            <table className="tbl" style={{ marginTop: 8 }}>
              <thead><tr><th>Metric</th><th className="num">Baseline</th><th className="num">Stressed</th><th>Limit</th><th>Result</th></tr></thead>
              <tbody>
                {preview.map((m, i) => (
                  <tr key={i} className="click" onClick={() => onTrace(m)}>
                    <td style={{ fontWeight: 600 }}>{m.metric}</td>
                    <td className="num tnum">{m.base}</td>
                    <td className="num tnum" style={{ color: "var(--indigo)", fontWeight: 700 }}>{m.stress}</td>
                    <td className="tnum muted" style={{ fontSize: 12 }}>{m.limit}</td>
                    <td>{m.breach ? <span className="chip chip-red"><span className="dot" />Breach</span> : <span className="chip chip-green"><span className="dot" />Within</span>}</td>
                  </tr>
                ))}
              </tbody>
            </table>
            <div style={{ padding: "14px 20px" }}>
              {breaches > 0
                ? <div className="banner banner-red"><Icon name="alert" size={18} className="ic" /><div className="grow"><b>{breaches} limit breach in this scenario.</b> Analysis-only — hand to Recovery to record management actions.</div><button className="btn btn-ghost btn-sm" style={{ flex: "0 0 auto" }} onClick={() => go("recovery")}>Hand to Recovery <Icon name="arrowR" size={13} /></button></div>
                : <div className="banner banner-green"><Icon name="checkCirc" size={18} className="ic" /><div>All metrics within their NBE limits under this scenario.</div></div>}
            </div>
          </div>

          {/* governance strip */}
          <div className="card card-pad">
            <div className="between" style={{ flexWrap: "wrap", gap: 10 }}>
              <div className="center gap10 wrap">
                <button className="btn btn-ghost btn-sm" onClick={() => save("DRAFT")}><Icon name="fileText" size={14} /> Save draft</button>
                <button className="btn btn-ghost btn-sm" disabled={!draft.name || isOwnerChecker} onClick={() => save("REVIEW_PENDING")}><Icon name="arrowR" size={14} /> Submit for review</button>
                <button className="btn btn-ghost btn-sm" disabled={!editId || draft.state !== "REVIEW_PENDING" || isOwnerChecker} onClick={approve}><Icon name="check" size={14} /> Approve · internal</button>
              </div>
              <div className="center gap8">
                <button className="btn btn-ghost btn-sm" disabled={!editId} onClick={rerun}><Icon name="refresh" size={14} /> Re-run on snapshot</button>
                <button className="btn btn-dark btn-sm" disabled={!editId} onClick={run}><Icon name="activity" size={14} /> Run &amp; record</button>
              </div>
            </div>
            <div className="muted" style={{ fontSize: 11.5, marginTop: 10 }}>Submit routes to the checker (≠ maker) and logs an internal review event. Approval is internal only — never official, never an export unlock. Saving never changes a baseline.</div>
          </div>
        </div>
      </div>
    </div>
  );
}
function Field({ label, children }) { return <label style={{ display: "block" }}><span style={{ fontSize: 11, fontWeight: 600, color: "var(--ink-3)", display: "block", marginBottom: 4 }}>{label}</span>{children}</label>; }

/* ============================================================
   SCENARIO LIBRARY — the shelf, with New / Edit / Run
   ============================================================ */
function ScenarioLibrary({ scenarios, onEdit, onNew }) {
  return (
    <React.Fragment>
      <div className="between mb12"><div className="muted" style={{ fontSize: 12.5 }}>Governed scenarios · owner, severity, version, state &amp; last run</div><button className="btn btn-primary btn-sm" onClick={onNew}><Icon name="flask" size={14} /> New scenario</button></div>
      <div className="card">
        <table className="tbl">
          <thead><tr><th>Scenario</th><th>Family</th><th>Severity</th><th>Owner</th><th>Levers</th><th>Ver</th><th>State</th><th>Last run</th><th></th></tr></thead>
          <tbody>
            {scenarios.map(s => { const p = PEOPLE[s.owner]; return (
              <tr key={s.id} className="click" onClick={() => onEdit(s)}>
                <td style={{ fontWeight: 600 }}>{s.name}</td>
                <td><span className={"chip " + (s.family === "Liquidity" ? "chip-green" : s.family === "Market" ? "chip-indigo" : s.family === "Combined" ? "chip-ink" : "chip-slate")}>{s.family}</span></td>
                <td><span className={"chip " + (s.severity === "Severe" || s.severity === "Extreme" ? "chip-red" : s.severity === "Moderate" ? "chip-amber" : "chip-neutral")}>{s.severity}</span></td>
                <td><span className="center gap6" style={{ fontSize: 12.5 }}><Avatar name={p ? p.name : "?"} size={20} /> {p ? p.name.split(" ")[0] : "—"}</span></td>
                <td className="tnum">{Object.keys(s.levers || {}).length}</td>
                <td className="tnum muted">v{s.version || 1}</td>
                <td><ScenChip s={s.state} /></td>
                <td className="muted" style={{ fontSize: 12 }}>{s.lastRun}</td>
                <td className="num"><div className="center gap6" style={{ justifyContent: "flex-end" }}><button className="btn btn-quiet btn-sm" onClick={(e) => { e.stopPropagation(); onEdit(s); }}>Edit</button><button className="btn btn-ghost btn-sm" onClick={(e) => { e.stopPropagation(); onEdit(s); }}>Run</button></div></td>
              </tr>
            ); })}
          </tbody>
        </table>
      </div>
      <div className="mt16"><Banner kind="amber" icon="users">Different teams hold different views of "severe". The library gives each scenario a single <b>owner</b>, a <b>version</b>, an <b>approval state</b>, and a last-run snapshot — so a board pack cites a governed scenario, not an analyst's private copy.</Banner></div>
    </React.Fragment>
  );
}

/* ============================================================
   TRACE · REVERSE · RUN HISTORY
   ============================================================ */
const LEVER_TRACE = {
  "Net open position": { lever: "FX depreciation · USD", row: "FX matrix · USD net position", base: "Br 6,200 M", stressed: "Br 9,920 M", factor: "+25% deprec.", evidence: "RECONCILED", owner: "abel", route: "market" },
  "LCR ratio": { lever: "Retail deposit run-off", row: "LCR · Saving deposits (individuals)", base: "Br 3,258 M @ 10%", stressed: "Br 3,258 M @ 18%", factor: "run-off 10%→18%", evidence: "EVIDENCE_NEEDED", owner: "sara", route: "lcr" },
  "CAR (total capital)": { lever: "Cross-holding write-down", row: "Capital · Investment in FI capital (2.8)", base: "Br 220 M", stressed: "Br 540 M", factor: "write-down 145%", evidence: "REVIEW_PENDING", owner: "meron", route: "capital" },
  "NSFR ratio": { lever: "Wholesale funding rollover", row: "NSFR · Wholesale < 1yr ASF", base: "Br 21,400 M @ 50%", stressed: "Br 14,980 M @ 35%", factor: "rollover 100%→70%", evidence: "MAPPED", owner: "dawit", route: "lcr" },
  "Survival horizon": { lever: "Combined run-off", row: "ALM · cumulative liquidity gap", base: "94 days", stressed: "31 days", factor: "severe outflow", evidence: "REVIEW_PENDING", owner: "dawit", route: "workpaper" },
};
function TraceDrawer({ trace, onClose, go }) {
  const t = LEVER_TRACE[trace.metric];
  return (
    <Drawer open={true} onClose={onClose} wide>
      <DrawerHead title={trace.metric + " · impact trace"} sub="cause → effect · analysis-only" chip={<StatusChip s={t ? t.evidence : "STRESS_ONLY"} />} onClose={onClose} />
      <div className="drawer-body">
        {!t ? <Banner kind="indigo" icon="flask">Analysis-only impact. Composite of active levers — no single driver.</Banner> : <React.Fragment>
          <Banner kind="indigo" icon="flask">No stress result without its driver, affected row, evidence and owner. Analysis-only — the official row is unchanged.</Banner>
          <div className="mt16"><Meta rows={[
            ["Driving lever", t.lever], ["Affected workpaper row", t.row], ["Baseline", t.base],
            ["Stressed", <span style={{ color: "var(--indigo)", fontWeight: 600 }}>{t.stressed}</span>], ["Factor change", t.factor],
            ["Metric (this scenario)", trace.metric + ": " + trace.base + " → " + trace.stress],
            ["Evidence", <StatusChip s={t.evidence} />], ["Owner", <span className="center gap8"><Avatar name={PEOPLE[t.owner].name} size={20} /> {PEOPLE[t.owner].name}</span>],
          ]} /></div>
          {t.evidence === "EVIDENCE_NEEDED" && <div className="mt16"><Banner kind="amber" icon="alert"><b>Assumption evidence needed.</b> No NBE reference attached — the impact is computed but not yet defensible for an internal pack.</Banner></div>}
        </React.Fragment>}
      </div>
      <div className="drawer-foot">
        <button className="btn btn-ghost" onClick={() => go(t ? t.route : "workpaper")}><Icon name="arrowR" size={14} /> Open workpaper row</button>
        {trace.breach && <button className="btn btn-dark" style={{ marginLeft: "auto" }} onClick={() => go("recovery")}>Hand to Recovery <Icon name="arrowR" size={14} /></button>}
      </div>
    </Drawer>
  );
}

function ReversePanel({ go }) {
  const rows = [
    { metric: "LCR ratio", limit: "≥ 100%", buffer: "265% today", brk: "Retail run-off 41%", head: "First driver: saving deposits (individuals)", route: "lcr" },
    { metric: "Survival horizon", limit: "≥ 30 days", buffer: "94 days today", brk: "Combined run-off 22%", head: "First driver: cumulative liquidity gap", route: "workpaper" },
    { metric: "Net open position", limit: "≤ ±18%", buffer: "7.3% today", brk: "FX depreciation 19%", head: "First driver: USD net long position", route: "market" },
    { metric: "CAR (total)", limit: "≥ 11%", buffer: "13.0% today", brk: "Cross-holding write-down 410%", head: "Large headroom before breach", route: "capital" },
  ];
  return (
    <div className="card">
      <div className="panel-h" style={{ padding: "18px 22px 0" }}><h3>Reverse stress · distance to breach</h3><span className="sub">the shock that takes each metric to its NBE limit — the cliff, not just today's pass</span><div className="right"><StatusChip s="STRESS_ONLY" lg /></div></div>
      <table className="tbl" style={{ marginTop: 8 }}>
        <thead><tr><th>Metric</th><th>NBE limit</th><th>Current buffer</th><th>Breaking shock</th><th></th></tr></thead>
        <tbody>
          {rows.map((r, i) => (
            <tr key={i} className="click" onClick={() => go(r.route)}>
              <td style={{ fontWeight: 600 }}>{r.metric}</td><td className="tnum muted">{r.limit}</td>
              <td className="tnum" style={{ color: "var(--green-ink)", fontWeight: 600 }}>{r.buffer}</td>
              <td><b style={{ color: "var(--indigo)" }}>{r.brk}</b><div className="muted" style={{ fontSize: 11.5 }}>{r.head}</div></td>
              <td className="num"><span className="jump">Trace <Icon name="arrowR" size={13} /></span></td>
            </tr>
          ))}
        </tbody>
      </table>
      <div style={{ padding: "16px 22px" }}><div className="banner banner-indigo"><Icon name="flask" size={18} className="ic" /><div><b>Reverse stress is analysis-only.</b> It finds the shock at which each ratio hits its NBE limit — turning "we passed today" into a <b>distance-to-breach early-warning trigger</b> for ALCO. Not an official calculation.</div></div></div>
    </div>
  );
}

function RunHistory({ openAction }) {
  const [runs, setRuns] = useStateS(() => { try { return JSON.parse(localStorage.getItem("tos_runs") || "[]"); } catch (e) { return []; } });
  useEffectS(() => { const h = () => { try { setRuns(JSON.parse(localStorage.getItem("tos_runs") || "[]")); } catch (e) {} }; window.addEventListener("tos-runs", h); return () => window.removeEventListener("tos-runs", h); }, []);
  const seed = [
    { id: "R-0241", scenario: "NBE adverse · deposit run", snapshot: "Q2-2026 · Jun 30", by: "dawit", when: "14:20", result: "1 breach", state: "APPROVED_INTERNAL", version: 3 },
    { id: "R-0240", scenario: "FX depreciation 25%", snapshot: "Q2-2026 · Jun 30", by: "abel", when: "09:10", result: "1 breach", state: "APPROVED_INTERNAL", version: 2 },
    { id: "R-0238", scenario: "Combined macro downturn", snapshot: "Q2-2026 · Jun 28", by: "hana", when: "Jun 28", result: "2 breaches", state: "REVIEW_PENDING", version: 1 },
    { id: "R-0235", scenario: "NBE adverse · deposit run", snapshot: "Q1-2026 · Mar 31", by: "dawit", when: "Mar 31", result: "0 breaches", state: "APPROVED_INTERNAL", version: 2 },
  ];
  const all = runs.concat(seed);
  return (
    <React.Fragment>
      <div className="between mb12"><div className="muted" style={{ fontSize: 12.5 }}>Every run binds to a source snapshot &amp; scenario version — comparable across cycles</div><button className="btn btn-ghost btn-sm" onClick={() => openAction({
        title: "Compare runs",
        sub: "Scenario-version comparison",
        chip: <StatusChip s="STRESS_ONLY" />,
        banner: <span><b>Run comparison opened.</b> The comparison binds scenario versions to their source snapshots so trend changes are explainable.</span>,
        rows: [["Runs selected", "Latest 4"], ["Snapshot basis", "Q1 vs Q2 fixture snapshots"], ["Output", "Analysis-only comparison"], ["Next action", "Use Recovery for management actions if a breach appears"]],
        guardrail: "Comparing runs does not update official baselines and cannot unlock export readiness.",
      })}><Icon name="gitBranch" size={14} /> Compare runs</button></div>
      <div className="card">
        <table className="tbl">
          <thead><tr><th>Run</th><th>Scenario</th><th>Ver</th><th>Source snapshot</th><th>By</th><th>When</th><th>Result</th><th>State</th></tr></thead>
          <tbody>
            {all.map((r, i) => { const p = PEOPLE[r.by]; return (
              <tr key={i} className="click">
                <td className="mono" style={{ fontSize: 12.5 }}>{r.id}</td>
                <td style={{ fontWeight: 600 }}>{r.scenario}</td>
                <td className="tnum muted">v{r.version || 1}</td>
                <td className="muted" style={{ fontSize: 12.5 }}>{r.snapshot}</td>
                <td><span className="center gap6" style={{ fontSize: 12.5 }}><Avatar name={p ? p.name : "?"} size={20} /> {p ? p.name.split(" ")[0] : r.by}</span></td>
                <td className="muted">{r.when}</td>
                <td><span className={"chip " + (/0 breach/.test(r.result) ? "chip-green" : "chip-red")}>{r.result}</span></td>
                <td><ScenChip s={r.state} /></td>
              </tr>
            ); })}
          </tbody>
        </table>
      </div>
      <div className="mt16"><Banner kind="green" icon="info">Because each run records its <b>scenario version</b> and <b>source snapshot</b>, March and June results are comparable — assumption and data changes are explicit, so trends are meaningful for ALCO.</Banner></div>
    </React.Fragment>
  );
}

window.StressLab = StressLab;
