/* global React */
// Terminal LatAm — HITL approval flow modals + snapshot viewer

const TH_T = window.T_THEME;
const TH_FM = window.T_FM;
const TH_FS = window.T_FS;
const TH_B = window.TerminalBtn;

// ─── Modal shell ─────────────────────────────────────────────────────────
const ModalShell = ({ children, onClose, width = 640, kind = "default" }) => {
  const accent = kind === "danger" ? TH_T.red : kind === "warn" ? TH_T.amber : kind === "ok" ? TH_T.green : TH_T.cyan;
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);
  return (
    <div onClick={onClose} style={{
      position: "fixed", inset: 0, background: "rgba(0,0,0,0.72)", backdropFilter: "blur(4px)",
      zIndex: 9000, display: "flex", alignItems: "center", justifyContent: "center", padding: 30,
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width, maxWidth: "95vw", maxHeight: "92vh", overflowY: "auto",
        background: TH_T.bg, border: `1px solid ${TH_T.line}`, borderTop: `3px solid ${accent}`,
        boxShadow: `0 24px 80px rgba(0,0,0,0.6)`, fontFamily: TH_FS,
      }}>{children}</div>
    </div>
  );
};

const ModalHeader = ({ tag, title, sub, onClose, accent = TH_T.cyan }) => (
  <div style={{ padding: "18px 22px", borderBottom: `1px solid ${TH_T.line}`, display: "flex", alignItems: "center", gap: 14 }}>
    <span style={{ fontFamily: TH_FM, fontSize: 10, color: accent, letterSpacing: "0.2em", padding: "3px 8px", border: `1px solid ${accent}` }}>{tag}</span>
    <div style={{ flex: 1 }}>
      <div style={{ fontSize: 16, color: TH_T.ink, fontWeight: 600 }}>{title}</div>
      {sub && <div style={{ fontSize: 11, color: TH_T.mute, marginTop: 3 }}>{sub}</div>}
    </div>
    <button onClick={onClose} style={{ background: "none", border: `1px solid ${TH_T.line}`, color: TH_T.mute, fontFamily: TH_FM, fontSize: 11, padding: "4px 10px", cursor: "pointer" }}>ESC</button>
  </div>
);

const ModalActions = ({ children }) => (
  <div style={{ padding: "14px 22px", borderTop: `1px solid ${TH_T.line}`, display: "flex", justifyContent: "flex-end", gap: 8, background: TH_T.surface }}>{children}</div>
);

const KV = ({ k, v, vCol }) => (
  <div style={{ display: "grid", gridTemplateColumns: "140px 1fr", gap: 14, padding: "8px 0", borderBottom: `1px dashed ${TH_T.line}` }}>
    <span style={{ fontSize: 10, color: TH_T.muteDim, letterSpacing: "0.12em" }}>{k}</span>
    <span style={{ fontSize: 12, color: vCol || TH_T.ink, fontFamily: typeof v === "string" && v.match(/^[\d\-+.,$ S\/%]+$/) ? TH_FM : TH_FS }}>{v}</span>
  </div>
);

// ─── APPROVE ─────────────────────────────────────────────────────────────
const ApproveModal = ({ action, onConfirm, onClose }) => {
  const [holding, setHolding] = React.useState(0);
  React.useEffect(() => {
    if (holding === 0) return;
    const id = setInterval(() => setHolding(h => h >= 100 ? 100 : h + 8), 30);
    return () => clearInterval(id);
  }, [holding]);
  React.useEffect(() => { if (holding >= 100) onConfirm("approve"); }, [holding, onConfirm]);

  return (
    <ModalShell onClose={onClose} kind="ok">
      <ModalHeader tag="APPROVE" title={action.title} sub={`${action.account.toUpperCase()} · ${action.category} · ETA ${action.eta}`} onClose={onClose} accent={TH_T.green} />
      <div style={{ padding: "20px 22px" }}>
        <KV k="IMPACTO" v={action.impact.primary} vCol={TH_T.green} />
        <KV k="LEADS" v={action.impact.leads} vCol={TH_T.amber} />
        <KV k="CONFIDENCE" v={`${action.confidence.toFixed(2)} · ${action.evidence}`} />
        <KV k="ROLLBACK" v={action.rollback} vCol={TH_T.cyan} />
        <KV k="SCOPE" v={action.scope} />
        <div style={{ marginTop: 18, padding: "12px 14px", background: TH_T.surface, border: `1px solid ${TH_T.green}`, borderLeftWidth: 3, fontSize: 11, color: TH_T.ink, lineHeight: 1.6 }}>
          <span style={{ color: TH_T.green, letterSpacing: "0.14em", fontFamily: TH_FM }}>// AGENT </span>
          Aplico cambio, capturo snapshot pre · monitoreo 72h · si CPL sube &gt; 8% revierto auto. Hash auditable se genera al confirmar.
        </div>
        <div style={{ marginTop: 18 }}>
          <div style={{ fontSize: 10, color: TH_T.muteDim, letterSpacing: "0.14em", marginBottom: 8 }}>HOLD TO CONFIRM · 400ms anti-mistake</div>
          <button
            onMouseDown={() => setHolding(1)}
            onMouseUp={() => setHolding(0)}
            onMouseLeave={() => setHolding(0)}
            style={{
              width: "100%", padding: "16px 0", background: TH_T.bg, border: `1px solid ${TH_T.green}`,
              color: TH_T.green, fontFamily: TH_FM, fontSize: 12, letterSpacing: "0.18em",
              cursor: "pointer", position: "relative", overflow: "hidden",
            }}>
            <div style={{ position: "absolute", inset: 0, background: TH_T.green, opacity: 0.18, width: `${holding}%`, transition: "width 30ms linear" }} />
            <span style={{ position: "relative" }}>▶ APPROVE &amp; EXECUTE · {action.eta}</span>
          </button>
        </div>
      </div>
      <ModalActions>
        <TH_B onClick={onClose}>CANCEL</TH_B>
      </ModalActions>
    </ModalShell>
  );
};

// ─── MODIFY ──────────────────────────────────────────────────────────────
const ModifyModal = ({ action, onConfirm, onClose }) => {
  // Parse action's main numeric param to expose a slider
  const baseAmt = parseFloat((action.impact.primary.match(/[\d,.]+/) || ["1000"])[0].replace(/,/g, "")) || 1000;
  const [intensity, setIntensity] = React.useState(100); // % of original
  const [hourCap, setHourCap] = React.useState(2);
  const [scopeNote, setScopeNote] = React.useState("");

  const projected = (baseAmt * intensity / 100);
  const projectedLeads = Math.round((parseInt((action.impact.leads.match(/\d+/) || ["50"])[0]) * intensity / 100));
  const confidenceShift = intensity > 100 ? -0.05 : intensity < 80 ? -0.08 : 0;

  return (
    <ModalShell onClose={onClose} kind="warn" width={720}>
      <ModalHeader tag="MODIFY" title={action.title} sub="Ajusta intensidad, cap por hora y nota · impacto recalcula en vivo" onClose={onClose} accent={TH_T.amber} />
      <div style={{ padding: "20px 22px", display: "grid", gridTemplateColumns: "1fr 280px", gap: 22 }}>
        <div>
          <div style={{ fontSize: 10, color: TH_T.muteDim, letterSpacing: "0.14em", marginBottom: 10 }}>INTENSIDAD · {intensity}% del cambio propuesto</div>
          <input type="range" min="40" max="140" step="5" value={intensity} onChange={(e) => setIntensity(+e.target.value)} style={{ width: "100%", accentColor: TH_T.amber }} />
          <div style={{ display: "flex", justifyContent: "space-between", fontSize: 9, color: TH_T.muteDim, fontFamily: TH_FM, marginTop: 4 }}>
            <span>40% conservador</span><span>100% propuesto</span><span>140% agresivo</span>
          </div>

          <div style={{ marginTop: 22, fontSize: 10, color: TH_T.muteDim, letterSpacing: "0.14em", marginBottom: 10 }}>CAP POR HORA · {hourCap}% del budget diario / hora</div>
          <input type="range" min="1" max="8" step="0.5" value={hourCap} onChange={(e) => setHourCap(+e.target.value)} style={{ width: "100%", accentColor: TH_T.amber }} />
          <div style={{ display: "flex", justifyContent: "space-between", fontSize: 9, color: TH_T.muteDim, fontFamily: TH_FM, marginTop: 4 }}>
            <span>1% suave</span><span>8% bursty</span>
          </div>

          <div style={{ marginTop: 22, fontSize: 10, color: TH_T.muteDim, letterSpacing: "0.14em", marginBottom: 8 }}>NOTA AL AGENTE · opcional</div>
          <textarea value={scopeNote} onChange={(e) => setScopeNote(e.target.value)} placeholder="Ej. mantener LAL 1% activo · no tocar audiencias hot..." style={{
            width: "100%", height: 70, padding: "10px 12px", background: TH_T.surface, border: `1px solid ${TH_T.line}`,
            color: TH_T.ink, fontFamily: TH_FM, fontSize: 11, resize: "none", outline: "none",
          }} />
        </div>

        <div style={{ background: TH_T.surface, border: `1px solid ${TH_T.line}`, padding: "14px 16px" }}>
          <div style={{ fontSize: 10, color: TH_T.amber, letterSpacing: "0.16em", marginBottom: 12 }}>// LIVE RECOMPUTE</div>
          <div style={{ display: "flex", justifyContent: "space-between", padding: "6px 0", borderBottom: `1px dashed ${TH_T.line}` }}>
            <span style={{ fontSize: 10, color: TH_T.muteDim }}>impacto $</span>
            <span style={{ fontFamily: TH_FM, fontSize: 12, color: TH_T.green }}>S/ {projected.toLocaleString(undefined,{maximumFractionDigits:0})}</span>
          </div>
          <div style={{ display: "flex", justifyContent: "space-between", padding: "6px 0", borderBottom: `1px dashed ${TH_T.line}` }}>
            <span style={{ fontSize: 10, color: TH_T.muteDim }}>leads esperados</span>
            <span style={{ fontFamily: TH_FM, fontSize: 12, color: TH_T.amber }}>{projectedLeads}/d</span>
          </div>
          <div style={{ display: "flex", justifyContent: "space-between", padding: "6px 0", borderBottom: `1px dashed ${TH_T.line}` }}>
            <span style={{ fontSize: 10, color: TH_T.muteDim }}>confidence</span>
            <span style={{ fontFamily: TH_FM, fontSize: 12, color: confidenceShift < 0 ? TH_T.red : TH_T.green }}>
              {(action.confidence + confidenceShift).toFixed(2)} {confidenceShift < 0 ? `(${confidenceShift.toFixed(2)})` : ""}
            </span>
          </div>
          <div style={{ display: "flex", justifyContent: "space-between", padding: "6px 0", borderBottom: `1px dashed ${TH_T.line}` }}>
            <span style={{ fontSize: 10, color: TH_T.muteDim }}>riesgo CPL</span>
            <span style={{ fontFamily: TH_FM, fontSize: 12, color: intensity > 110 ? TH_T.amber : TH_T.green }}>
              {intensity > 120 ? "alto" : intensity > 100 ? "medio" : "bajo"}
            </span>
          </div>
          <div style={{ display: "flex", justifyContent: "space-between", padding: "6px 0" }}>
            <span style={{ fontSize: 10, color: TH_T.muteDim }}>rollback</span>
            <span style={{ fontFamily: TH_FM, fontSize: 11, color: TH_T.cyan }}>{action.rollback}</span>
          </div>
          <div style={{ marginTop: 14, padding: "8px 10px", background: TH_T.bg, fontSize: 10, color: TH_T.cyan, lineHeight: 1.5 }}>
            agente regenerará monitoring window basado en intensidad ajustada
          </div>
        </div>
      </div>
      <ModalActions>
        <TH_B onClick={onClose}>CANCEL</TH_B>
        <TH_B kind="amber" onClick={() => onConfirm("modify", { intensity, hourCap, scopeNote })}>EXECUTE · {intensity}%</TH_B>
      </ModalActions>
    </ModalShell>
  );
};

// ─── SCHEDULE ────────────────────────────────────────────────────────────
const ScheduleModal = ({ action, onConfirm, onClose }) => {
  const presets = [
    { id: "now", label: "Ahora", sub: "ejecutar inmediato", val: "11:42" },
    { id: "1h", label: "+1 hora", sub: "12:42 hoy", val: "12:42" },
    { id: "4h", label: "+4 horas", sub: "15:42 hoy · pico tarde", val: "15:42" },
    { id: "morning", label: "Mañana 06:00", sub: "antes del primer pico", val: "Vie 06:00" },
    { id: "window", label: "Próx ventana", sub: "agente eligió: Vie 09:30 (Q-traffic peak +18%)", val: "Vie 09:30", recommended: true },
    { id: "custom", label: "Custom", sub: "elige fecha y hora", val: null },
  ];
  const [pick, setPick] = React.useState("window");
  const [customWhen, setCustomWhen] = React.useState("2026-05-09T09:00");

  return (
    <ModalShell onClose={onClose} kind="warn" width={620}>
      <ModalHeader tag="SCHEDULE" title={action.title} sub="¿Cuándo ejecuto este cambio?" onClose={onClose} accent={TH_T.amber} />
      <div style={{ padding: "20px 22px" }}>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
          {presets.map(p => (
            <button key={p.id} onClick={() => setPick(p.id)} style={{
              padding: "12px 14px", background: pick === p.id ? TH_T.surface : TH_T.bg, textAlign: "left",
              border: `1px solid ${pick === p.id ? TH_T.amber : TH_T.line}`, cursor: "pointer", position: "relative",
            }}>
              {p.recommended && <span style={{ position: "absolute", top: 6, right: 8, fontSize: 8, color: TH_T.green, letterSpacing: "0.16em", fontFamily: TH_FM }}>★ RECOMENDADO</span>}
              <div style={{ fontSize: 13, color: pick === p.id ? TH_T.amber : TH_T.ink, fontFamily: TH_FS, fontWeight: 500 }}>{p.label}</div>
              <div style={{ fontSize: 10, color: TH_T.muteDim, marginTop: 4 }}>{p.sub}</div>
              {p.val && <div style={{ fontSize: 10, color: TH_T.cyan, fontFamily: TH_FM, marginTop: 6 }}>{p.val}</div>}
            </button>
          ))}
        </div>
        {pick === "custom" && (
          <div style={{ marginTop: 14 }}>
            <div style={{ fontSize: 10, color: TH_T.muteDim, letterSpacing: "0.14em", marginBottom: 6 }}>FECHA &amp; HORA</div>
            <input type="datetime-local" value={customWhen} onChange={(e) => setCustomWhen(e.target.value)} style={{
              padding: "8px 10px", background: TH_T.surface, border: `1px solid ${TH_T.line}`, color: TH_T.ink,
              fontFamily: TH_FM, fontSize: 12, outline: "none",
            }} />
          </div>
        )}
        <div style={{ marginTop: 18, padding: "12px 14px", background: TH_T.surface, border: `1px solid ${TH_T.cyan}`, borderLeftWidth: 3, fontSize: 11, color: TH_T.ink, lineHeight: 1.6 }}>
          <span style={{ color: TH_T.cyan, letterSpacing: "0.14em", fontFamily: TH_FM }}>// AGENT </span>
          La acción quedará en cola. Recibirás ping 5 min antes con preview pre-execution. Puedes cancelar hasta T-30s.
        </div>
      </div>
      <ModalActions>
        <TH_B onClick={onClose}>CANCEL</TH_B>
        <TH_B kind="amber" onClick={() => onConfirm("schedule", { pick, when: pick === "custom" ? customWhen : presets.find(x => x.id === pick).val })}>SCHEDULE</TH_B>
      </ModalActions>
    </ModalShell>
  );
};

// ─── REJECT ──────────────────────────────────────────────────────────────
const RejectModal = ({ action, onConfirm, onClose }) => {
  const reasons = [
    "Disagree con la hipótesis",
    "Riesgo demasiado alto",
    "Cliente no autorizó",
    "Window incorrecto · espera fin de mes",
    "Necesita más evidencia",
    "Conflicto con otra acción",
    "Otro motivo",
  ];
  const [picked, setPicked] = React.useState("");
  const [note, setNote] = React.useState("");

  return (
    <ModalShell onClose={onClose} kind="danger" width={580}>
      <ModalHeader tag="REJECT" title={action.title} sub="El agente aprenderá de tu razón · no se vuelve a sugerir 14d" onClose={onClose} accent={TH_T.red} />
      <div style={{ padding: "20px 22px" }}>
        <div style={{ fontSize: 10, color: TH_T.muteDim, letterSpacing: "0.14em", marginBottom: 10 }}>MOTIVO</div>
        <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
          {reasons.map(r => (
            <button key={r} onClick={() => setPicked(r)} style={{
              padding: "8px 12px", background: picked === r ? TH_T.surface : "transparent", textAlign: "left",
              border: `1px solid ${picked === r ? TH_T.red : TH_T.line}`, cursor: "pointer",
              color: picked === r ? TH_T.red : TH_T.ink, fontSize: 11, fontFamily: TH_FS,
            }}>
              {picked === r ? "● " : "○ "}{r}
            </button>
          ))}
        </div>
        <div style={{ marginTop: 14, fontSize: 10, color: TH_T.muteDim, letterSpacing: "0.14em", marginBottom: 8 }}>NOTA · opcional</div>
        <textarea value={note} onChange={(e) => setNote(e.target.value)} placeholder="Más contexto para el agente..." style={{
          width: "100%", height: 60, padding: "10px 12px", background: TH_T.surface, border: `1px solid ${TH_T.line}`,
          color: TH_T.ink, fontFamily: TH_FM, fontSize: 11, resize: "none", outline: "none",
        }} />
      </div>
      <ModalActions>
        <TH_B onClick={onClose}>CANCEL</TH_B>
        <TH_B kind="red" onClick={() => onConfirm("reject", { reason: picked || "no especificado", note })}>REJECT</TH_B>
      </ModalActions>
    </ModalShell>
  );
};

// ─── BULK APPROVE ────────────────────────────────────────────────────────
const BulkApproveModal = ({ actions, onConfirm, onClose }) => {
  const [skipped, setSkipped] = React.useState({});
  const will = actions.filter(a => !skipped[a.id]);
  const totalImpact = will.reduce((s, a) => {
    const v = parseFloat((a.impact.primary.match(/[\d,.]+/) || ["0"])[0].replace(/,/g, ""));
    return s + (isFinite(v) ? v : 0);
  }, 0);

  return (
    <ModalShell onClose={onClose} kind="ok" width={760}>
      <ModalHeader tag={`BULK · ${actions.length}`} title={`Aprobar ${will.length} acciones P0 simultáneamente`} sub="Cada acción mantiene su propio rollback hash · podrás revertir individualmente" onClose={onClose} accent={TH_T.green} />
      <div style={{ padding: "20px 22px" }}>
        <div style={{ display: "flex", justifyContent: "space-between", padding: "10px 14px", background: TH_T.surface, marginBottom: 12 }}>
          <span style={{ fontSize: 10, color: TH_T.muteDim, letterSpacing: "0.14em" }}>IMPACTO COMBINADO</span>
          <span style={{ fontFamily: TH_FM, fontSize: 14, color: TH_T.green }}>~ S/ {totalImpact.toLocaleString(undefined, { maximumFractionDigits: 0 })} en juego</span>
        </div>
        <div style={{ border: `1px solid ${TH_T.line}` }}>
          {actions.map((a, i) => {
            const off = skipped[a.id];
            return (
              <div key={a.id} style={{
                display: "grid", gridTemplateColumns: "30px 60px 1fr 180px 90px", gap: 12, alignItems: "center",
                padding: "10px 14px", borderBottom: i < actions.length - 1 ? `1px solid ${TH_T.line}` : "none",
                opacity: off ? 0.4 : 1, background: TH_T.bg,
              }}>
                <input type="checkbox" checked={!off} onChange={() => setSkipped(s => ({ ...s, [a.id]: !s[a.id] }))} />
                <span style={{ fontSize: 10, color: TH_T.red, fontFamily: TH_FM, padding: "2px 6px", border: `1px solid ${TH_T.red}`, textAlign: "center" }}>{a.priority}</span>
                <div>
                  <div style={{ fontSize: 12, color: TH_T.ink, textDecoration: off ? "line-through" : "none" }}>{a.title}</div>
                  <div style={{ fontSize: 10, color: TH_T.muteDim, marginTop: 3 }}>{a.account.toUpperCase()} · {a.category}</div>
                </div>
                <span style={{ fontSize: 11, color: TH_T.green, fontFamily: TH_FM }}>{a.impact.primary}</span>
                <span style={{ fontSize: 10, color: TH_T.muteDim, fontFamily: TH_FM, textAlign: "right" }}>conf {a.confidence.toFixed(2)}</span>
              </div>
            );
          })}
        </div>
        <div style={{ marginTop: 14, padding: "12px 14px", background: TH_T.surface, border: `1px solid ${TH_T.amber}`, borderLeftWidth: 3, fontSize: 11, color: TH_T.ink, lineHeight: 1.6 }}>
          <span style={{ color: TH_T.amber, letterSpacing: "0.14em", fontFamily: TH_FM }}>// GUARDRAIL </span>
          Si 2+ acciones afectan misma audience/ad set, las ejecuto secuenciales con 30 min entre · evita interferencia de aprendizaje.
        </div>
      </div>
      <ModalActions>
        <TH_B onClick={onClose}>CANCEL</TH_B>
        <TH_B kind="green" onClick={() => onConfirm("bulk", { ids: will.map(a => a.id) })}>APPROVE {will.length} ACTIONS</TH_B>
      </ModalActions>
    </ModalShell>
  );
};

// ─── SNAPSHOT VIEWER ─────────────────────────────────────────────────────
const SnapshotViewer = ({ executed, onClose }) => {
  // Generate deterministic snapshot frames from executed action
  const e = executed;
  const baseImpact = parseFloat((e.impact.match(/[\d,.]+/) || ["100"])[0].replace(/,/g, ""));
  const frames = [
    { t: "PRE",  label: "antes de ejecutar", cpl: e.cplPre || 73.5, leads: 168, mer: 2.84 },
    { t: "T+24h", label: "24 horas", cpl: 70.1, leads: 184, mer: 2.97, delta: "primer ajuste auto" },
    { t: "T+72h", label: "72 horas", cpl: 67.9, leads: 198, mer: 3.04, delta: "señal estabilizada" },
    { t: "T+7d",  label: "7 días", cpl: 66.2, leads: 211, mer: 3.12, delta: "consolidado · agente liberó constraint" },
  ];
  const [pick, setPick] = React.useState(2);
  const f = frames[pick];

  return (
    <ModalShell onClose={onClose} kind="ok" width={780}>
      <ModalHeader tag="SNAPSHOT" title={e.action} sub={`hash ${e.hash} · ejecutado ${e.t} · revertible 14d`} onClose={onClose} accent={TH_T.cyan} />
      <div style={{ padding: "20px 22px" }}>
        <div style={{ display: "flex", gap: 0, border: `1px solid ${TH_T.line}`, marginBottom: 18 }}>
          {frames.map((fr, i) => (
            <button key={fr.t} onClick={() => setPick(i)} style={{
              flex: 1, padding: "12px 8px", border: "none", borderRight: i < frames.length - 1 ? `1px solid ${TH_T.line}` : "none",
              background: pick === i ? TH_T.surface : TH_T.bg, color: pick === i ? TH_T.cyan : TH_T.mute,
              fontFamily: TH_FM, fontSize: 11, letterSpacing: "0.14em", cursor: "pointer",
            }}>
              <div style={{ fontWeight: 600 }}>{fr.t}</div>
              <div style={{ fontSize: 9, color: TH_T.muteDim, marginTop: 4, letterSpacing: "0.06em" }}>{fr.label}</div>
            </button>
          ))}
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12, marginBottom: 18 }}>
          {[
            { k: "CPL", v: `S/ ${f.cpl.toFixed(2)}`, sub: pick > 0 ? `${(((f.cpl - frames[0].cpl) / frames[0].cpl) * 100).toFixed(1)}%` : "baseline", down: pick > 0 && f.cpl < frames[0].cpl },
            { k: "LEADS / día", v: f.leads, sub: pick > 0 ? `+${f.leads - frames[0].leads}` : "baseline", up: pick > 0 && f.leads > frames[0].leads },
            { k: "MER", v: `${f.mer.toFixed(2)}x`, sub: pick > 0 ? `+${(f.mer - frames[0].mer).toFixed(2)}x` : "baseline", up: pick > 0 && f.mer > frames[0].mer },
          ].map(s => (
            <div key={s.k} style={{ padding: "14px 16px", background: TH_T.surface, border: `1px solid ${TH_T.line}` }}>
              <div style={{ fontSize: 10, color: TH_T.muteDim, letterSpacing: "0.14em", marginBottom: 8 }}>{s.k}</div>
              <div style={{ fontFamily: TH_FS, fontSize: 22, color: TH_T.ink, fontWeight: 600 }}>{s.v}</div>
              <div style={{ fontSize: 10, marginTop: 6, color: s.up || s.down ? TH_T.green : TH_T.muteDim, fontFamily: TH_FM }}>{s.up ? "▲" : s.down ? "▼" : ""} {s.sub}</div>
            </div>
          ))}
        </div>

        {/* simple chart */}
        <div style={{ background: TH_T.surface, border: `1px solid ${TH_T.line}`, padding: "14px 16px" }}>
          <div style={{ fontSize: 10, color: TH_T.muteDim, letterSpacing: "0.14em", marginBottom: 10 }}>EVOLUCIÓN CPL</div>
          <svg width="100%" height="100" viewBox="0 0 600 100" preserveAspectRatio="none">
            <polyline
              fill="none" stroke={TH_T.green} strokeWidth="2"
              points={frames.map((fr, i) => `${(i / (frames.length - 1)) * 580 + 10},${100 - (frames[0].cpl - fr.cpl + 5) * 8}`).join(" ")}
            />
            {frames.map((fr, i) => (
              <g key={i}>
                <circle cx={(i / (frames.length - 1)) * 580 + 10} cy={100 - (frames[0].cpl - fr.cpl + 5) * 8} r={pick === i ? 5 : 3} fill={pick === i ? TH_T.amber : TH_T.green} />
                <text x={(i / (frames.length - 1)) * 580 + 10} y={95} textAnchor="middle" fontSize="9" fill={TH_T.muteDim}>{fr.t}</text>
              </g>
            ))}
          </svg>
        </div>

        {f.delta && (
          <div style={{ marginTop: 14, padding: "12px 14px", background: TH_T.bg, border: `1px solid ${TH_T.cyan}`, borderLeftWidth: 3, fontSize: 11, color: TH_T.ink, lineHeight: 1.6 }}>
            <span style={{ color: TH_T.cyan, letterSpacing: "0.14em", fontFamily: TH_FM }}>// AGENT </span>{f.delta}
          </div>
        )}
      </div>
      <ModalActions>
        <TH_B onClick={onClose}>CERRAR</TH_B>
        {e.rev && <TH_B kind="red">REVERTIR ACCIÓN</TH_B>}
        <TH_B kind="cyan">EXPORTAR PARA CFO</TH_B>
      </ModalActions>
    </ModalShell>
  );
};

// ─── HITL controller (state machine) ─────────────────────────────────────
const useHITL = () => {
  const [modal, setModal] = React.useState(null); // {kind, action, actions?}
  const open = (kind, payload) => setModal({ kind, ...payload });
  const close = () => setModal(null);
  return { modal, open, close };
};

const HITLPortal = ({ modal, onClose, onResolved }) => {
  if (!modal) return null;
  const handle = (kind, payload) => { onResolved(modal.action || modal.actions, kind, payload); onClose(); };
  if (modal.kind === "approve") return <ApproveModal action={modal.action} onConfirm={handle} onClose={onClose} />;
  if (modal.kind === "modify") return <ModifyModal action={modal.action} onConfirm={handle} onClose={onClose} />;
  if (modal.kind === "schedule") return <ScheduleModal action={modal.action} onConfirm={handle} onClose={onClose} />;
  if (modal.kind === "reject") return <RejectModal action={modal.action} onConfirm={handle} onClose={onClose} />;
  if (modal.kind === "bulk") return <BulkApproveModal actions={modal.actions} onConfirm={handle} onClose={onClose} />;
  if (modal.kind === "snapshot") return <SnapshotViewer executed={modal.executed} onClose={onClose} />;
  return null;
};

window.useHITL = useHITL;
window.HITLPortal = HITLPortal;
