/* global React, Icon, StatusChip, PageHeader, Banner, etb */
const { useState: useStateOP } = React;

/* ============================================================
   NBE Net Open Position (NOP) return — faithful matrix + Official·Stress lens
   FX shock hits the aggregate NOP vs the ±18% of Tier-1 limit.
   All figures internal/synthetic · ETB millions
   ============================================================ */
const TIER1 = 11913; // from the Capital return (row 7)
const LIMIT = 18;    // ±18% of Tier 1

// per-currency net open position (ETB millions equiv) — long (+) / short (−)
const FX = [
  { ccy: "USD", rate: 142.0, aOn: { "1.1.1": 120, "1.1.2": 900, "1.1.4": 700, "1.1.5": 120 }, aOff: { "1.2.2": 0 }, lOn: { "2.1.2": 800, "2.1.3": 300, "2.1.4": 120 }, lOff: {} },
  { ccy: "Euro", rate: 154.3, aOn: { "1.1.2": 500, "1.1.4": 210 }, aOff: {}, lOn: { "2.1.2": 430, "2.1.3": 100 }, lOff: {} },
  { ccy: "GBP", rate: 178.1, aOn: { "1.1.2": 230 }, aOff: {}, lOn: { "2.1.2": 205 }, lOff: {} },
  { ccy: "AED", rate: 38.7, aOn: { "1.1.2": 300 }, aOff: {}, lOn: { "2.1.2": 250 }, lOff: {} },
  { ccy: "ZAR", rate: 7.8, aOn: { "1.1.2": 120 }, aOff: {}, lOn: { "2.1.2": 135 }, lOff: {} },
  { ccy: "Gold", rate: null, gold: true, aOn: { "1.1.1": 260 }, aOff: {}, lOn: {}, lOff: {} },
];
const ssum = o => Object.values(o).reduce((a, b) => a + b, 0);
FX.forEach(f => { f.assets = ssum(f.aOn) + ssum(f.aOff); f.liab = ssum(f.lOn) + ssum(f.lOff); f.net = f.assets - f.liab; });
const GOLD_OPEN = FX.filter(f => f.gold).reduce((a, f) => a + Math.abs(f.net), 0);
const totalLong = FX.filter(f => !f.gold && f.net > 0).reduce((a, f) => a + f.net, 0);
const totalShort = -FX.filter(f => !f.gold && f.net < 0).reduce((a, f) => a + f.net, 0);
const OVERALL = Math.max(totalLong, totalShort) + GOLD_OPEN;          // greater of long / short plus gold
const BASE_RATIO = OVERALL / TIER1 * 100;                  // ≈7.3%

// faithful NBE line-item structure (rows) × currencies (columns)
const OP_ROWS = [
  { no: "1", label: "Foreign Currency Assets", kind: "sec" },
  { no: "1.1", label: "On-balance sheet items", kind: "sub", f: c => ssum(c.aOn) },
  { no: "1.1.1", label: "Currency on hand", leaf: "aOn" },
  { no: "1.1.2", label: "Due from banks", leaf: "aOn" },
  { no: "1.1.3", label: "Cheques and items in transit", leaf: "aOn" },
  { no: "1.1.4", label: "Loans & advances", leaf: "aOn" },
  { no: "1.1.5", label: "Accrued interest receivable", leaf: "aOn" },
  { no: "1.1.6", label: "Other assets", leaf: "aOn" },
  { no: "1.2", label: "Off-balance sheet items", kind: "sub", f: c => ssum(c.aOff) },
  { no: "1.2.1", label: "Undelivered spot purchase", leaf: "aOff" },
  { no: "1.2.2", label: "Forward purchase", leaf: "aOff" },
  { no: "1.2.3", label: "Options, swaps, derivatives", leaf: "aOff" },
  { no: "1.2.4", label: "Other assets", leaf: "aOff" },
  { no: "", label: "Total Foreign Assets (1.1 + 1.2)", kind: "tot", f: c => c.assets },
  { no: "2", label: "Foreign Currency Liabilities", kind: "sec" },
  { no: "2.1", label: "On-balance sheet items", kind: "sub", f: c => ssum(c.lOn) },
  { no: "2.1.1", label: "Due to banks abroad", leaf: "lOn" },
  { no: "2.1.2", label: "Foreign currency deposits", leaf: "lOn" },
  { no: "2.1.3", label: "Borrowings", leaf: "lOn" },
  { no: "2.1.4", label: "Accrued interest payable", leaf: "lOn" },
  { no: "2.1.5", label: "Other liabilities", leaf: "lOn" },
  { no: "2.2", label: "Off-balance sheet items", kind: "sub", f: c => ssum(c.lOff) },
  { no: "2.2.1", label: "Undelivered spot sales", leaf: "lOff" },
  { no: "2.2.2", label: "Forward sales", leaf: "lOff" },
  { no: "2.2.3", label: "Options, swaps, derivatives", leaf: "lOff" },
  { no: "2.2.4", label: "Letter of credit", leaf: "lOff" },
  { no: "2.2.5", label: "Guarantees", leaf: "lOff" },
  { no: "2.2.6", label: "Other liabilities", leaf: "lOff" },
  { no: "", label: "Total Foreign Liabilities (2.1 + 2.2)", kind: "tot", f: c => c.liab },
  { no: "3", label: "Net position (assets − liabilities)", kind: "sub", f: c => c.net },
  { no: "4", label: "Mid-exchange rate (ETB)", kind: "rate", f: c => c.rate },
  { no: "7", label: "Net open position (greater of long / short)", kind: "tot", f: c => Math.abs(c.net) },
];
const opCell = (row, c) => row.leaf ? (c[row.leaf][row.no] ?? null) : (row.f ? row.f(c) : null);

// stress: FX depreciation grows exposure, exposure-growth compounds, Tier-1 erodes
const A = 1;
const ovr = (row) => row.kind === "rate" ? null : row.no === "7" ? OVERALL : row.no === "3" ? FX.reduce((a, c) => a + c.net, 0) : FX.reduce((a, c) => { const v = opCell(row, c); return a + (v || 0); }, 0);
function nop(dep, growth, erosion) {
  const overall = OVERALL * (1 + dep / 100 * A) * (1 + growth / 100);
  const tier1 = TIER1 * (1 - erosion / 100);
  return { overall, tier1, ratio: overall / tier1 * 100 };
}
const FXSEV = [
  { name: "Mild", dep: 8, growth: 8, erosion: 3 },
  { name: "Moderate", dep: 15, growth: 15, erosion: 6 },
  { name: "Severe", dep: 25, growth: 25, erosion: 12 },
  { name: "Extreme", dep: 40, growth: 40, erosion: 25 },
];
// per-currency FX revaluation factor (long grows, short worsens)
const fac = (dep, growth) => (1 + dep / 100 * A) * (1 + growth / 100);

const OP_HELP = {
  title: "Net Open Position (NBE/NOP)",
  purpose: "The FX open-position return. It nets each currency's assets and liabilities, takes the greater of aggregate long/short, and measures it against the NBE ±18% of Tier-1 capital limit. Stress applies an FX shock to that aggregate.",
  first: "Read the NOP ratio against ±18%. In Stress, scan the severity columns for the band that breaches, then tune your own shock in the Custom column.",
  terms: [
    ["Net open position", "Greater of aggregate long or short FX exposure."],
    ["±18% limit", "NBE cap: overall NOP must stay within 18% of Tier-1 capital."],
    ["FX shock", "Depreciation + exposure growth + Tier-1 erosion under stress."],
  ],
  next: ["Open Stress to see the NOP under each severity.", "Tune the FX levers — the Custom column recomputes live.", "A breach hands off to Recovery."],
  connections: ["Currency positions reconcile to the Tier-1 FX blotter.", "Tier-1 capital comes from the Capital return.", "Breaches trigger Recovery Planning."],
  guardrail: "Internal NOP workpaper. Stress is analysis-only and never overwrites the official position; official NBE export stays blocked.",
};

function OpenPosition({ go, info }) {
  const [stress, setStress] = useStateOP(false);
  const [mode, setMode] = useStateOP("Custom");
  const [shocks, setShocks] = useStateOP({ dep: 25, growth: 25, erosion: 12 });
  const set = (k, v) => setShocks(s => ({ ...s, [k]: +v }));
  const applyBand = (i) => setShocks({ dep: FXSEV[i].dep, growth: FXSEV[i].growth, erosion: FXSEV[i].erosion });
  const bandActive = (s) => shocks.dep === s.dep && shocks.growth === s.growth && shocks.erosion === s.erosion;
  const live = nop(shocks.dep, shocks.growth, shocks.erosion);
  const breach = live.ratio > LIMIT;
  // reverse: depreciation that hits ±18%, holding growth/erosion
  const wDep = Math.round(((LIMIT / 100 * TIER1 * (1 - shocks.erosion / 100)) / (OVERALL * (1 + shocks.growth / 100)) - 1) / A * 100);

  return (
    <div className="main-inner">
      <PageHeader eyebrow="Prudential Risk · Market" title="Net Open Position" onInfo={() => info(OP_HELP)}
        right={<button className={"btn btn-sm " + (stress ? "btn-dark" : "btn-ghost")} onClick={() => setStress(!stress)}><Icon name="flask" size={15} /> {stress ? "Stress on" : "Stress test"}</button>} />

      {/* headline */}
      {/* official currency matrix — Excel-faithful (Official lens only) */}
      {!stress && <div className="xl-wrap mb16">
        <table className="xl">
          <colgroup><col style={{ width: 44 }} /><col style={{ minWidth: 230 }} />{FX.map(f => <col key={f.ccy} style={{ width: 92 }} />)}<col style={{ width: 104 }} /></colgroup>
          <thead><tr>
            <th className="xl-no"></th>
            <th style={{ textAlign: "left" }}>Net Open Position · ETB millions · as at Jun 30, 2026</th>
            {FX.map(f => <th key={f.ccy} className="xl-amt">{f.ccy}</th>)}
            <th className="xl-amt">Overall</th>
          </tr></thead>
          <tbody>
            {OP_ROWS.map((row, ri) => {
              if (row.kind === "sec") return <tr key={ri} className="xl-green"><td className="xl-no">{row.no}</td><td colSpan={2 + FX.length}>{row.label}</td></tr>;
              const cls = row.kind === "tot" ? "xl-orange" : row.kind === "sub" ? "xl-sub" : "xl-item";
              const isRate = row.kind === "rate";
              const overall = isRate ? null : row.no === "7" ? Math.round(OVERALL) : row.no === "3" ? FX.reduce((a, c) => a + c.net, 0) : FX.reduce((a, c) => { const v = opCell(row, c); return a + (v || 0); }, 0);
              const f = v => v == null ? "" : v < 0 ? "(" + etb(Math.abs(v)) + ")" : etb(v);
              return (
                <tr key={ri} className={cls}>
                  <td className="xl-no">{row.no}</td>
                  <td className="xl-label">{row.label}</td>
                  {FX.map(c => {
                    const v = opCell(row, c);
                    return <td key={c.ccy} className="xl-amt tnum" style={{ color: (!isRate && v < 0) ? "var(--red)" : undefined }}>{v == null ? (row.kind === "item" ? <span style={{ opacity: .35 }}>–</span> : "") : (isRate ? v.toFixed(1) : f(v))}</td>;
                  })}
                  <td className="xl-amt tnum">{f(overall)}</td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>}

      {!stress && <div className="grid g4 mb16">
        <Stat label="Row 8.1 · Total long" v={etb(Math.round(totalLong))} foot="sum of long currency positions" />
        <Stat label="Row 8.2 · Total short" v={etb(Math.round(totalShort))} foot="sum of short currency positions" />
        <Stat label="Row 8.3 · Gold position" v={etb(Math.round(GOLD_OPEN))} foot="added to overall open position" />
        <Stat label="Row 8.4 · Overall open" v={etb(Math.round(OVERALL))} foot="greater of long/short plus gold" tone="green" />
      </div>}

      {!stress && <div className="grid g2 mb16">
        <Stat label="Row 8.5 · ±18% limit" v={etb(Math.round(TIER1 * LIMIT / 100))} foot="18% × Tier 1 capital" />
        <div className="card card-pad" style={{ padding: "15px 18px" }}><div className="section-label">Source / description</div><div className="mono" style={{ marginTop: 7, fontSize: 12.5 }}>Credit risk2.xlsx · Mkt / Description</div><div className="muted" style={{ fontSize: 11.5, marginTop: 4 }}>Workbook currency grid: USD, Euro, GBP, AED, ZAR, Gold.</div></div>
      </div>}

      {!stress && <Banner kind="green" icon="checkCirc">Reconciled to the Tier-1 FX blotter (Murex). Overall NOP {BASE_RATIO.toFixed(1)}% is within the NBE ±{LIMIT}% limit. The NOP control is monitored daily; breach evidence stays internal and routes to corrective action next day. Switch to <b>Stress</b> to shock the position.</Banner>}

      {/* stress: severity ladder + dock */}
      {stress && <div style={{ display: "flex", gap: 16, alignItems: "flex-start" }}>
        <div className="grow" style={{ minWidth: 0 }}>
          <div className="banner banner-indigo mb12" style={{ alignItems: "center", padding: "11px 15px" }}>
            <Icon name="flask" size={18} className="ic" />
            <div className="grow"><b>FX shock overlay</b> · analysis-only · never merged into the official position</div>
            <span className="chip chip-indigo">±{LIMIT}% limit</span>
          </div>
          <div className="xl-wrap">
            <table className="xl xl-scroll">
              <colgroup><col style={{ width: 42 }} /><col style={{ width: 320 }} /><col style={{ width: 96 }} />{FXSEV.map(s => <col key={s.name} style={{ width: 84 }} />)}<col style={{ width: 100 }} /></colgroup>
              <thead><tr>
                <th className="xl-no"></th>
                <th style={{ textAlign: "left" }}>Net Open Position under FX shock · ETB millions · analysis-only</th>
                <th className="xl-amt">Base</th>
                {FXSEV.map((s, i) => <th key={s.name} className={"xl-amt xl-stress" + (bandActive(s) ? " on" : "")} style={{ cursor: "pointer" }} title={"Use " + s.name} onClick={() => applyBand(i)}>{s.name}</th>)}
                <th className="xl-amt" style={{ background: "var(--indigo)", color: "#fff", borderLeft: "2px solid var(--indigo)" }}>Custom</th>
              </tr></thead>
              <tbody>
                {OP_ROWS.map((row, ri) => {
                  if (row.kind === "sec") return <tr key={ri} className="xl-green"><td className="xl-no">{row.no}</td><td colSpan={6}>{row.label}</td></tr>;
                  if (row.kind === "rate") return null;
                  const off = ovr(row);
                  const cls = row.kind === "tot" ? "xl-orange" : row.kind === "sub" ? "xl-sub" : "xl-item";
                  const f = v => v == null ? "" : v < 0 ? "(" + etb(Math.abs(Math.round(v))) + ")" : etb(Math.round(v));
                  return (
                    <tr key={ri} className={cls}>
                      <td className="xl-no">{row.no}</td>
                      <td className="xl-label">{row.label}</td>
                      <td className="xl-amt tnum">{f(off)}</td>
                      {FXSEV.map((s, i) => <td key={i} className={"xl-amt xl-stress tnum" + (bandActive(s) ? " on" : "")} style={{ color: off ? "var(--indigo)" : undefined }}>{off ? f(off * fac(s.dep, s.growth)) : ""}</td>)}
                      <td className="xl-amt tnum" style={{ borderLeft: "2px solid var(--indigo)", background: "rgba(79,77,208,.05)", color: off ? "var(--indigo)" : undefined, fontWeight: 600 }}>{off ? f(off * fac(shocks.dep, shocks.growth)) : ""}</td>
                    </tr>
                  );
                })}
                <tr className="xl-item"><td className="xl-no">8.4</td><td className="xl-label">Tier-1 capital (stressed)</td><td className="xl-amt tnum">{etb(TIER1)}</td>{FXSEV.map((s, i) => <td key={i} className={"xl-amt xl-stress tnum" + (bandActive(s) ? " on" : "")} style={{ color: "var(--indigo)" }}>{etb(Math.round(nop(s.dep, s.growth, s.erosion).tier1))}</td>)}<td className="xl-amt tnum" style={{ borderLeft: "2px solid var(--indigo)", background: "rgba(79,77,208,.05)", color: "var(--indigo)" }}>{etb(Math.round(live.tier1))}</td></tr>
                <tr className="xl-orange">
                  <td className="xl-no">8.6</td><td className="xl-label">NOP ratio · NBE limit ±{LIMIT}%</td>
                  <td className="xl-amt tnum">{BASE_RATIO.toFixed(1)}%</td>
                  {FXSEV.map((s, i) => { const r = nop(s.dep, s.growth, s.erosion).ratio, br = r > LIMIT; return <td key={i} className={"xl-amt tnum xl-cell hit" + (bandActive(s) ? " on" : "")} onClick={() => applyBand(i)} style={{ background: br ? "var(--red-soft)" : undefined, color: br ? "var(--red)" : "var(--indigo)", fontWeight: 800 }}>{r.toFixed(1)}%</td>; })}
                  <td className="xl-amt tnum" style={{ borderLeft: "2px solid var(--indigo)", background: breach ? "var(--red-soft)" : "rgba(79,77,208,.05)", color: breach ? "var(--red)" : "var(--indigo)", fontWeight: 800 }}>{live.ratio.toFixed(1)}%</td>
                </tr>
              </tbody>
            </table>
          </div>
          <div className="mt12"><Banner kind={breach ? "red" : "indigo"} icon={breach ? "alert" : "flask"}>
            {breach
              ? <span><b>Breach — NOP {live.ratio.toFixed(1)}% exceeds the ±{LIMIT}% limit.</b> This is a scenario result; hand it to Recovery to record management actions. The official position is unchanged.</span>
              : <span>Severity columns show NBE's named FX-shock bands. The <b style={{ color: "var(--indigo)" }}>Custom</b> column is your own shock — tune it in the panel. Red shading marks a ±{LIMIT}% breach.</span>}
          </Banner></div>
        </div>

        {/* FX stress dock */}
        <aside className="stress-dock">
          <div className="between mb12"><span className="center gap8"><Icon name="sliders" size={15} style={{ color: "var(--indigo)" }} /><b style={{ fontSize: 14, whiteSpace: "nowrap" }}>FX shock controls</b></span><span className="chip chip-indigo">Analysis-only</span></div>
          <div className="seg-ctl" style={{ width: "100%", marginBottom: 14 }}>{[["Presets", "Presets"], ["Custom", "Custom"], ["Reverse", "Reverse"]].map(([l, k]) => <button key={k} className={mode === k ? "on" : ""} style={{ flex: 1 }} onClick={() => setMode(k)}>{l}</button>)}</div>

          {mode === "Presets" && <div>
            <div className="dock-label">Scenario severity</div>
            <div className="seg-ctl" style={{ width: "100%" }}>{FXSEV.map((s, i) => <button key={s.name} className={bandActive(s) ? "on" : ""} style={{ flex: 1, padding: "6px 2px" }} onClick={() => applyBand(i)}>{s.name === "Moderate" ? "Mod" : s.name}</button>)}</div>
            <div className="muted" style={{ fontSize: 11.5, marginTop: 8 }}>Sets all FX levers to NBE's named bands at once.</div>
          </div>}

          {mode === "Custom" && <div className="col" style={{ gap: 15 }}>
            <Slider label="FX depreciation (ETB)" v={shocks.dep} min={0} max={60} step={1} onChange={v => set("dep", v)} />
            <Slider label="FX exposure growth" v={shocks.growth} min={0} max={60} step={1} onChange={v => set("growth", v)} />
            <Slider label="Tier-1 capital erosion" v={shocks.erosion} min={0} max={40} step={1} onChange={v => set("erosion", v)} />
          </div>}

          {mode === "Reverse" && <div>
            <div className="dock-label">Find the breaking point</div>
            <div className="muted" style={{ fontSize: 11.5, marginBottom: 10 }}>Reverse stress — solves the FX depreciation that hits ±{LIMIT}%, holding the other levers fixed.</div>
            <div className="banner banner-indigo" style={{ padding: "11px 13px" }}><Icon name="flask" size={16} className="ic" /><div>An FX depreciation of <b>≈{wDep}%</b> takes NOP to the ±{LIMIT}% limit.</div></div>
            <button className="btn btn-dark btn-sm" style={{ width: "100%", marginTop: 12 }} onClick={() => set("dep", Math.max(0, wDep))}>Set to breaking point</button>
          </div>}

          <hr className="hr" style={{ margin: "16px 0" }} />
          <div className="dock-label">Live NOP ratio</div>
          <div className="big-num tnum" style={{ fontSize: 30, color: breach ? "var(--red)" : "var(--green-ink)", marginTop: 2 }}>{live.ratio.toFixed(1)}%</div>
          <div className="gauge"><span className="gauge-fill" style={{ width: Math.max(2, Math.min(98, live.ratio / 30 * 100)) + "%", background: breach ? "var(--red)" : "var(--green)" }} /><span className="gauge-floor" style={{ left: (LIMIT / 30 * 100) + "%" }} /></div>
          <div className="between" style={{ fontSize: 10.5, color: "var(--ink-3)", marginTop: 3 }}><span>0%</span><span>±{LIMIT}% limit</span><span>30%</span></div>
          {breach
            ? <button className="btn btn-dark btn-sm" style={{ width: "100%", marginTop: 12 }} onClick={() => go("recovery")}>Breach — Send to Recovery <Icon name="arrowR" size={13} /></button>
            : <div className="chip chip-green chip-lg" style={{ marginTop: 12 }}><span className="dot" />Within ±{LIMIT}% limit</div>}
          <button className="btn btn-ghost btn-sm" style={{ width: "100%", marginTop: 8 }} onClick={() => setShocks({ dep: 0, growth: 0, erosion: 0 })}><Icon name="refresh" size={13} /> Clear stress · restore baseline</button>
        </aside>
      </div>}
    </div>
  );
}

function LadderRow({ label, official, bands, custom }) {
  return (
    <tr className="xl-item">
      <td className="xl-label">{label}</td>
      <td className="xl-amt tnum">{etb(official)}</td>
      {bands.map((b, i) => <td key={i} className="xl-amt xl-stress tnum" style={{ color: b !== official ? "var(--indigo)" : undefined, fontWeight: b !== official ? 700 : undefined }}>{etb(b)}</td>)}
      <td className="xl-amt tnum" style={{ borderLeft: "2px solid var(--indigo)", background: "rgba(79,77,208,.05)", color: custom !== official ? "var(--indigo)" : undefined, fontWeight: 700 }}>{etb(custom)}</td>
    </tr>
  );
}
function Slider({ label, v, min, max, step, onChange }) {
  return (
    <div>
      <div className="between" style={{ marginBottom: 5 }}><span style={{ fontSize: 12, fontWeight: 600 }}>{label}</span><span className="chip chip-indigo" style={{ fontSize: 10.5 }}>{v}%</span></div>
      <input type="range" className="lev-slider" min={min} max={max} step={step} value={v} onChange={e => onChange(e.target.value)} />
    </div>
  );
}
function Stat({ label, v, foot, tone, big }) {
  const c = tone === "green" ? "var(--green-ink)" : "var(--ink)";
  return <div className="card card-pad" style={{ padding: "15px 18px" }}><div className="section-label">{label}</div><div className="big-num tnum" style={{ marginTop: 6, color: c, fontSize: big ? 32 : 26 }}>{v}</div><div className="muted" style={{ fontSize: 11.5, marginTop: 4 }}>{foot}</div></div>;
}

window.OpenPosition = OpenPosition;
