/* eagle-stream.jsx — Eagle Eye runtime DECOUPLED from its data source.
   ============================================================
   The run functions (runModel / runApp / runAgent) OWN the SOC UI: they
   translate a stream of RAW events into the UI events the screens reduce.
   They get those raw events from ONE swappable source:

       const stream = (window.LASTROSE_BACKENDS && window.LASTROSE_BACKENDS.eagleStream)
                    || window.EAGLE_MOCK_STREAM;
       await stream(tier, alertText, onEvent, shouldStop);

   • No eagleStream injected  → window.EAGLE_MOCK_STREAM (this file) runs the
     scripted demo, byte-for-byte identical to before.
   • LASTROSE_BACKENDS.eagleStream set (by the external, never-regenerated
     wiring.js) → real SSE adapter feeds the SAME onEvent(type, data) handler.

   The live wiring and these demo internals never touch each other again.
   ============================================================
   RAW EVENT VOCABULARY  —  onEvent(type, data)   (FROZEN CONTRACT)
   An external adapter emits these; the translator renders the SOC UI from
   them. `data` fields listed are what the translator reads:

     start              { tier, request? }            run begins
     token              { text }                       one streamed response chunk
     latency            { label, value?, detail?, ck?, spinner?, ok? }   a timing/telemetry line
     usage              { tokIn, tokOut, cost, latency, tps? }           running token/cost meters
     done               { }                            response stream finished
     error              { message }                    run failed
     guardrail_check    { label, ok?, detail?, value?, ck?, phase?, stage?{id,status} }   input guardrails
     guardrail_output   { label, ok?, detail?, value?, ck?, phase?, stage?{id,status} }   output guardrails
     rag_query          { label, value?, detail?, spinner? }             retrieval progress
     rag_result         { doc:{title,score,snippet} }                    one retrieved doc
     context_built      { parts:{system,history,rag,alert}, payload? }   assembled context window
     investigation_start{ }                            agent run begins
     investigation_done { }                            agent run ends
     agent_start        { id, label?, ck? }            an agent stage activates
     agent_thinking     { label?, detail?, find?{sev,cat,id} }           agent reasoning / verdict
     agent_done         { id }                          an agent stage completes
     a2a_discover       { from, to, text }              agent-to-agent discovery
     a2a_submit         { from, to, text }              agent-to-agent task handoff
     a2a_result         { from, to, text }              agent-to-agent result
     mcp_call           { agent?, tool, args }          a tool call starts
     mcp_result         { agent?, tool, args, result }  a tool call returns
     task_complete      { report?, summary? }           final report + run summary

   The mock additionally bundles pre-built UI ops under `data` (nav, telem,
   stage, mcp, action, …) with `_mock:true` for an EXACT replay; the translator
   passes those through verbatim and only SYNTHESIZES UI from the semantic
   fields above when `_mock` is absent (i.e. for real adapters).
   ============================================================ */

(function () {
  const _delay = (ms) => new Promise((r) => setTimeout(r, ms));
  const cost = (tin, tout) => tin * 3e-6 + tout * 15e-6;
  const fmtCost = (n) => "$" + n.toFixed(4);

  /* ---------- the translator: RAW events -> UI emit() ---------- */
  function eagleTranslate(emit) {
    return function onEvent(type, data) {
      data = data || {};
      // 1) explicit UI ops bundled by the mock — exact passthrough
      if (data.nav) emit({ t: "nav", ...data.nav });
      if (data.stage) emit({ t: "stage", ...data.stage });
      if (data.telem) emit({ t: "telem", ...data.telem });
      if (Array.isArray(data.telems)) data.telems.forEach((x) => emit({ t: "telem", ...x }));
      if (data.respStart) emit({ t: "respStart" });
      if (data.text != null) emit({ t: "token", text: data.text });
      if (data.ragdoc) emit({ t: "ragdoc", doc: data.ragdoc });
      if (data.ctx) emit({ t: "ctx", parts: data.ctx });
      if (data.mcpstart) emit({ t: "mcpstart", tool: data.mcpstart.tool });
      if (data.mcp) emit({ t: "mcp", ...data.mcp });
      if (data.agent) emit({ t: "agent", ...data.agent });
      if (data.agentfind) emit({ t: "agentfind", ...data.agentfind });
      if (data.action) emit({ t: "action", ...data.action });
      if (data.a2a) emit({ t: "a2a", ...data.a2a });
      if (data.report) emit({ t: "report", ...data.report });
      if (data.summary) emit({ t: "summary", ...data.summary });
      if (data.respDone) emit({ t: "respDone" });
      if (data.forgotten) emit({ t: "forgotten" });

      // 2) synthesis for REAL adapters that emit only semantic primitives
      if (data._mock) return;
      switch (type) {
        case "usage":
          emit({ t: "nav", tokIn: data.tokIn || 0, tokOut: data.tokOut || 0, cost: data.cost || 0, latency: data.latency || 0, tps: data.tps || 0 });
          break;
        case "latency":
          if (data.label) emit({ t: "telem", ck: data.ck || "gateway", label: data.label, value: data.value, detail: data.detail, spinner: data.spinner, ok: data.ok });
          break;
        case "done":
          emit({ t: "respDone" });
          break;
        case "guardrail_check":
        case "guardrail_output":
          if (data.stage) emit({ t: "stage", ...data.stage });
          if (data.phase) emit({ t: "telem", phase: true, ck: data.ck || "guard", label: data.label });
          else if (data.label) emit({ t: "telem", ck: data.ck || "guard", label: data.label, ok: data.ok, detail: data.detail, value: data.value });
          break;
        case "rag_query":
          if (data.label) emit({ t: "telem", ck: "rag", label: data.label, value: data.value, detail: data.detail, spinner: data.spinner });
          break;
        case "rag_result":
          if (data.doc) { emit({ t: "telem", ck: "rag", doc: data.doc }); emit({ t: "ragdoc", doc: data.doc }); }
          break;
        case "context_built":
          if (data.payload) emit({ t: "telem", ck: "app", label: "Assembling context window", payload: data.payload });
          if (data.parts) emit({ t: "ctx", parts: data.parts });
          break;
        case "agent_start":
          if (data.id) emit({ t: "agent", id: data.id, status: "active" });
          if (data.label) emit({ t: "telem", phase: true, ck: data.ck || "agent", label: data.label });
          break;
        case "agent_thinking":
          if (data.find) emit({ t: "agentfind", id: data.find.id, sev: data.find.sev, cat: data.find.cat });
          if (data.label) emit({ t: "telem", ck: data.ck || "agent", label: data.label, detail: data.detail });
          break;
        case "agent_done":
          if (data.id) emit({ t: "agent", id: data.id, status: "done" });
          break;
        case "a2a_discover":
        case "a2a_submit":
        case "a2a_result":
          emit({ t: "a2a", from: data.from, to: data.to, text: data.text });
          break;
        case "mcp_call":
          emit({ t: "mcpstart", tool: data.tool });
          break;
        case "mcp_result":
          emit({ t: "mcp", agent: data.agent, tool: data.tool, args: data.args, result: data.result, status: "done" });
          break;
        case "task_complete":
          if (data.report) emit({ t: "report", ...data.report });
          if (data.summary) emit({ t: "summary", ...data.summary });
          break;
        default:
          break;
      }
    };
  }

  /* ---------- streamed token helper (mock) ---------- */
  async function streamTok(text, E, shouldStop, base) {
    const words = text.match(/\S+\s*/g) || [];
    let out = 0; const t0 = base.t0;
    for (let i = 0; i < words.length; i++) {
      if (shouldStop()) return out;
      E("token", { _mock: true, text: words[i] });
      out += Math.max(1, Math.round(words[i].trim().length / 4));
      if (i % 3 === 0) {
        const el = (performance.now() - t0) / 1000;
        E("usage", { _mock: true, nav: { tokIn: base.tin, tokOut: out, cost: base.baseCost + cost(0, out), latency: Math.round(el * 1000), tps: Math.round(out / Math.max(0.4, el)) } });
      }
      await _delay(24 + Math.random() * 24);
    }
    return out;
  }

  /* ========================================================
     EAGLE_MOCK_STREAM — scripted producer. Emits the raw
     vocabulary above (with bundled _mock UI ops for an exact
     replay). Resolves the alert from its text so it stays a
     pure function of (tier, alertText).
     ======================================================== */
  async function mockModel(alert, E, shouldStop) {
    const t0 = performance.now();
    E("usage", { _mock: true, nav: { tokIn: 0, tokOut: 0, cost: 0, latency: 0, tps: 0 } });
    E("start", { _mock: true, telem: { ck: "cost", label: "API call constructed", detail: "POST /v1/messages",
      payload: `{\n  "model": "claude-sonnet-4",\n  "messages": [{ "role": "user",\n    "content": "${alert.code}: ${alert.rule} on ${alert.host}" }]\n}\n// no system prompt · no tools · no memory` } });
    await _delay(480); if (shouldStop()) return;
    E("latency", { _mock: true, telem: { ck: "gateway", label: "Routing via AI gateway", spinner: true } });
    await _delay(620); if (shouldStop()) return;
    E("latency", { _mock: true, telem: { ck: "gateway", label: "Connection established", detail: "TLS 1.3", ok: true } });
    await _delay(360); if (shouldStop()) return;
    E("latency", { _mock: true, telem: { ck: "model", label: "Time to first byte", value: "612 ms" } });
    E("usage", { _mock: true, nav: { tokIn: 94, tokOut: 0, cost: cost(94, 0), latency: 612, tps: 0 }, respStart: true });
    const out = await streamTok(alert.model, E, shouldStop, { tin: 94, baseCost: cost(94, 0), t0 });
    if (shouldStop()) return;
    E("done", { _mock: true, respDone: true });
    const el = Math.round(performance.now() - t0);
    E("done", { _mock: true, telem: { ck: "success", label: "Stream complete", detail: `94 input + ${out} output tokens`, ok: true } });
    E("done", { _mock: true, telem: { ck: "cost", label: "Billing", value: fmtCost(cost(94, out)), detail: "no further context retained" } });
    E("usage", { _mock: true, nav: { tokIn: 94, tokOut: out, cost: cost(94, out), latency: el, tps: 0 } });
    E("done", { _mock: true, summary: { time: (el / 1000).toFixed(1) + "s", tokens: 94 + out, cost: fmtCost(cost(94, out)), calls: 1, tools: 0 } });
    await _delay(850); if (shouldStop()) return;
    E("done", { _mock: true, forgotten: true });
  }

  async function mockApp(alert, E, shouldStop) {
    const t0 = performance.now();
    const C = alert.ctx, inTok = C.system + C.history + C.rag + C.alert;
    E("usage", { _mock: true, nav: { tokIn: 0, tokOut: 0, cost: 0, latency: 0, tps: 0 } });
    E("guardrail_check", { _mock: true, stage: { id: "guard_in", status: "active" }, telem: { phase: true, ck: "guard", label: "PHASE 1 · GUARDRAILS (INPUT)" } });
    await _delay(240); E("guardrail_check", { _mock: true, telem: { ck: "guard", label: "Input validation", ok: true } }); if (shouldStop()) return;
    await _delay(220); E("guardrail_check", { _mock: true, telem: { ck: "guard", label: "Prompt-injection scan", ok: true, detail: "0 patterns" } }); if (shouldStop()) return;
    await _delay(220); E("guardrail_check", { _mock: true, telem: { ck: "guard", label: "PII detection", ok: true, detail: "1 email tokenised" }, stage: { id: "guard_in", status: "done" } }); if (shouldStop()) return;

    E("rag_query", { _mock: true, stage: { id: "rag", status: "active" }, telem: { phase: true, ck: "rag", label: "PHASE 2 · RAG RETRIEVAL" } });
    await _delay(280); E("rag_query", { _mock: true, telem: { ck: "rag", label: "Embedding query", value: "38 ms", detail: "1536-d" } }); if (shouldStop()) return;
    await _delay(320); E("rag_query", { _mock: true, telem: { ck: "rag", label: "Vector search", spinner: true, detail: "12,480 docs" } }); if (shouldStop()) return;
    await _delay(480);
    for (const d of alert.rag) { E("rag_result", { _mock: true, telem: { ck: "rag", doc: d }, ragdoc: d }); await _delay(240); if (shouldStop()) return; }
    E("rag_query", { _mock: true, stage: { id: "rag", status: "done" } });

    E("context_built", { _mock: true, stage: { id: "ctx", status: "active" }, telem: { phase: true, ck: "app", label: "PHASE 3 · CONTEXT ASSEMBLY" } });
    await _delay(300);
    E("context_built", { _mock: true,
      telem: { ck: "app", label: "Assembling context window", payload: `system   ${C.system} tok\nhistory  ${C.history} tok\nRAG docs ${C.rag} tok\nalert    ${C.alert} tok\n──────────────\ninput  ${inTok.toLocaleString()} tok` },
      ctx: C, nav: { tokIn: inTok, tokOut: 0, cost: cost(inTok, 0), latency: Math.round(performance.now() - t0), tps: 0 }, stage: { id: "ctx", status: "done" } });
    if (shouldStop()) return;

    E("agent_start", { _mock: true, stage: { id: "model", status: "active" }, telem: { phase: true, ck: "model", label: "PHASE 4 · MODEL" } });
    await _delay(280); E("latency", { _mock: true, telem: { ck: "gateway", label: "Sending via gateway…", spinner: true } }); if (shouldStop()) return;
    await _delay(620); E("latency", { _mock: true, telem: { ck: "model", label: "Time to first byte", value: "1,240 ms" }, respStart: true });
    const out = await streamTok(alert.app, E, shouldStop, { tin: inTok, baseCost: cost(inTok, 0), t0 });
    if (shouldStop()) return;
    E("done", { _mock: true, respDone: true, telem: { ck: "model", label: "Generation", detail: `34 tok/s · ${out} output tokens`, ok: true }, stage: { id: "model", status: "done" } });

    E("guardrail_output", { _mock: true, stage: { id: "guard_out", status: "active" }, telem: { phase: true, ck: "guard", label: "PHASE 5 · GUARDRAILS (OUTPUT)" } });
    await _delay(240); E("guardrail_output", { _mock: true, telem: { ck: "guard", label: "IP-range validation", ok: true } }); if (shouldStop()) return;
    await _delay(220); E("guardrail_output", { _mock: true, telem: { ck: "guard", label: "Destructive-action check", ok: true } }); if (shouldStop()) return;
    await _delay(220); E("guardrail_output", { _mock: true, telem: { ck: "guard", label: "Confidence threshold", ok: true, value: "0.91" }, stage: { id: "guard_out", status: "done" } });

    const c = cost(inTok, out);
    E("task_complete", { _mock: true, telems: [
      { phase: true, ck: "success", label: "SUMMARY" },
      { ck: "cost", label: "Total", value: fmtCost(c), detail: `input ${inTok.toLocaleString()} + output ${out}` },
    ] });
    const el = Math.round(performance.now() - t0);
    E("usage", { _mock: true, nav: { tokIn: inTok, tokOut: out, cost: c, latency: el, tps: 0 } });
    E("task_complete", { _mock: true, summary: { time: (el / 1000).toFixed(1) + "s", tokens: inTok + out, cost: fmtCost(c), calls: 1, tools: 3 } });
  }

  async function mockAgent(alert, E, shouldStop) {
    const t0 = performance.now();
    let tok = 0;
    const bump = (n) => { tok += n; E("usage", { _mock: true, nav: { tokIn: tok, tokOut: Math.round(tok * 0.4), cost: cost(tok, Math.round(tok * 0.4)), latency: Math.round(performance.now() - t0), tps: 0 } }); };
    E("investigation_start", { _mock: true, nav: { tokIn: 0, tokOut: 0, cost: 0, latency: 0, tps: 0 } });

    E("agent_start", { _mock: true, agent: { id: "triage", status: "active" }, telems: [
      { phase: true, ck: "agent", label: "TRIAGE AGENT" },
      { ck: "identity", label: "Agent identity issued", detail: "spiffe://soc/triage", ok: true },
    ] });
    await _delay(680); bump(420); if (shouldStop()) return;
    E("agent_thinking", { _mock: true, agentfind: { id: "triage", sev: alert.sev, cat: alert.threatCat }, telem: { ck: "agent", label: "Severity assessment", detail: `${alert.sev} — ${alert.threatCat}` } });
    await _delay(560); if (shouldStop()) return;
    E("a2a_submit", { _mock: true, a2a: { from: "Triage", to: "Enrichment", text: `${alert.sev} · ${alert.threatCat}. Enrich host + indicators, return findings.` } });
    E("agent_done", { _mock: true, agent: { id: "triage", status: "done" } });
    await _delay(460); if (shouldStop()) return;

    E("agent_start", { _mock: true, agent: { id: "enrich", status: "active" }, telem: { phase: true, ck: "agent", label: "ENRICHMENT AGENT" } });
    for (const call of alert.mcp) {
      E("mcp_call", { _mock: true, telem: { ck: "tools", label: "MCP call", detail: call.tool + "()", spinner: true }, mcpstart: { tool: call.tool } });
      await _delay(760); if (shouldStop()) return;
      E("mcp_result", { _mock: true, mcp: { agent: "enrich", ...call, status: "done" }, telem: { ck: "tools", label: "Tool result", detail: call.tool.split(".")[1], ok: true } });
      bump(360);
      await _delay(360); if (shouldStop()) return;
    }
    E("a2a_submit", { _mock: true, a2a: { from: "Enrichment", to: "Response", text: "Findings complete. Execute the response plan." } });
    E("agent_done", { _mock: true, agent: { id: "enrich", status: "done" } });
    await _delay(440); if (shouldStop()) return;

    E("agent_start", { _mock: true, agent: { id: "response", status: "active" }, telem: { phase: true, ck: "agent", label: "RESPONSE AGENT" } });
    for (const a of alert.actions) {
      E("mcp_call", { _mock: true, action: { ...a, status: "pending" }, telem: { ck: "tools", label: "Action call", detail: a.tool + "()", spinner: true } });
      await _delay(950); if (shouldStop()) return;
      E("mcp_result", { _mock: true, action: { ...a, status: "done" }, telem: { ck: "success", label: "Action complete", detail: a.result, ok: true } });
      bump(240);
      await _delay(340); if (shouldStop()) return;
    }
    E("agent_done", { _mock: true, agent: { id: "response", status: "done" } });
    await _delay(360); if (shouldStop()) return;

    E("task_complete", { _mock: true, report: { id: alert.code, severity: alert.sev, ...alert.report } });
    const el = Math.round(performance.now() - t0);
    const outTok = Math.round(tok * 0.4), c = cost(tok, outTok);
    E("task_complete", { _mock: true, telems: [
      { phase: true, ck: "success", label: "RUN COMPLETE" },
      { ck: "cost", label: "Total", value: fmtCost(c), detail: `${tok + outTok} tokens · ${alert.mcp.length + alert.actions.length} tool calls` },
    ] });
    E("usage", { _mock: true, nav: { tokIn: tok, tokOut: outTok, cost: c, latency: el, tps: 0 } });
    E("investigation_done", { _mock: true, summary: { time: (el / 1000).toFixed(1) + "s", tokens: tok + outTok, cost: fmtCost(c), steps: 3, mcp: alert.mcp.length, a2a: 2, actions: alert.actions.length } });
  }

  async function EAGLE_MOCK_STREAM(tier, alertText, onEvent, shouldStop) {
    const alert = window.ALERTS.find((a) => a.text === alertText) || window.makeCustomAlert(alertText);
    if (tier === "model") return mockModel(alert, onEvent, shouldStop);
    if (tier === "app") return mockApp(alert, onEvent, shouldStop);
    return mockAgent(alert, onEvent, shouldStop);
  }

  /* ========================================================
     RUN FUNCTIONS — keep the (alert, emit, shouldStop) signature
     the screens call; route raw events through ONE swappable stream.
     ======================================================== */
  function pickStream() {
    return (window.LASTROSE_BACKENDS && window.LASTROSE_BACKENDS.eagleStream) || window.EAGLE_MOCK_STREAM;
  }
  async function runModel(alert, emit, shouldStop) {
    await pickStream()("model", alert.text, eagleTranslate(emit), shouldStop);
  }
  async function runApp(alert, emit, shouldStop) {
    await pickStream()("app", alert.text, eagleTranslate(emit), shouldStop);
  }
  async function runAgent(alert, emit, shouldStop) {
    await pickStream()("agent", alert.text, eagleTranslate(emit), shouldStop);
  }

  // Helpers exposed so an external SSE adapter (wiring.js) and the run/translate
  // functions never depend on file-private scope.
  window.EAGLE_HELPERS = { fmtCost, cost, delay: _delay };
  Object.assign(window, { EAGLE_MOCK_STREAM, eagleTranslate, runModel, runApp, runAgent });
})();
