/* ATLAS — network-level AI agent (Plant Network / first page).
   Read-only analyst that sees EVERY plant at once: each plant's config, final metrics
   and calculated outputs are computed from the shared formula engine and packed into one
   cross-plant context. It answers comparative / aggregate questions ("which plant has the
   lowest GEI?", "total cement across the network") — it does not edit values or the model
   (that lives in the per-plant AgentPanel, where a single plant is in scope). */
(function () {
  "use strict";
  const { useEffect, useMemo, useRef, useState } = React;
  const h = React.createElement;
  const MARKDOWN_OPTIONS = { gfm: true, breaks: true };

  const NETWORK_PROMPTS = [
    "Compare GEI across all plants and chart it",
    "Which plant has the biggest short-term decarbonisation opportunity?",
    "Build a network decarbonisation deck (PPTX) with charts and ranked levers",
  ];

  // ---- streaming POST to /api/agent (SSE): live step trace + token-by-token answer ----
  async function streamAgent(payload, handlers) {
    const hx = handlers || {};
    const response = await fetch("/api/agent", {
      method: "POST",
      headers: { "Content-Type": "application/json", "Accept": "text/event-stream" },
      body: JSON.stringify(Object.assign({ stream: true }, payload)),
    });
    if (!response.ok || !response.body) {
      const j = await response.json().catch(() => ({}));
      throw new Error(j.error || "Agent request failed.");
    }
    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let buffer = "", result = null, errored = null;
    while (true) {
      const { value, done } = await reader.read();
      if (done) break;
      buffer += decoder.decode(value, { stream: true });
      let idx;
      while ((idx = buffer.indexOf("\n\n")) !== -1) {
        const chunk = buffer.slice(0, idx); buffer = buffer.slice(idx + 2);
        let event = "message", data = "";
        chunk.split("\n").forEach((line) => {
          if (line.startsWith("event:")) event = line.slice(6).trim();
          else if (line.startsWith("data:")) data += line.slice(5).trim();
        });
        if (!data) continue;
        let parsed; try { parsed = JSON.parse(data); } catch (e) { continue; }
        if (event === "status" && hx.onStatus) hx.onStatus(parsed);
        else if (event === "step" && hx.onStep) hx.onStep(parsed);
        else if (event === "delta" && hx.onDelta) hx.onDelta(parsed.text || "");
        else if (event === "result") result = parsed;
        else if (event === "error") errored = parsed.error || "Agent error.";
      }
    }
    if (errored) throw new Error(errored);
    return result || { text: "Done.", toolUses: [] };
  }
  const statusLabel = (s) => s && s.stage === "analysing"
    ? "Analysing plants…" : s && s.stage === "thinking" ? "Thinking…" : "Working…";

  function StepTrace({ steps, streamText }) {
    if ((!steps || !steps.length) && !streamText) return null;
    const icon = (k) => k === "thinking" ? "✦" : k === "tool_result" ? "✓" : k === "round" ? "↻" : "→";
    return h("div", { className: "agent-msg assistant" },
      h("div", { className: "agent-role" }, "Agent"),
      h("div", { className: "agent-trace" },
        (steps || []).map((s, i) => h("div", { className: "trace-step " + s.kind, key: i },
          h("span", { className: "trace-ic" }, icon(s.kind)), h("span", null, s.label))),
        streamText ? h("div", { className: "agent-bubble markdown trace-answer", dangerouslySetInnerHTML: renderMarkdownToHtml(streamText) }) : null,
      ),
    );
  }

  // ---- per-plant snapshot: identity + per-asset editable inputs (the raw plant-level
  //      data) + every visible calculated output + final metrics. Carrying the inputs lets
  //      the Network Analyst drill into any plant in depth (kiln lines, fuel mix, GCVs,
  //      emission factors, electricity) — not just the headline metrics. ----
  function plantSnapshot(plant, model, finalMetrics) {
    const inputs = window.ATLAS_PLANTS.inputsFor(plant.id, model);
    const derived = window.AtlasEngine.evaluate(inputs, model);
    return {
      id: plant.id, code: plant.code, name: plant.name, fullName: plant.fullName,
      location: plant.location, fy: plant.fy, summary: plant.summary,
      config: plant.config, configLine: plant.configLine,
      notInstalled: plant.disabledGroups,
      finalMetrics: finalMetrics.map((m) => ({
        id: m.id, label: m.label, unit: m.unit,
        value: derived[m.id], formatted: m.fmt(derived[m.id]),
      })),
      totalCement: derived.totalCement,
      // raw editable inputs, grouped by asset → input group, so the agent can navigate
      // plant → asset → group → input and reason at the equipment/line level.
      assets: (model.assets || []).map((asset) => ({
        id: asset.id, name: asset.name, section: asset.section,
        inputs: (asset.inputs || []).map((row) => ({
          id: row.id, label: row.label, unit: row.unit, group: row.group, value: inputs[row.id],
        })),
        outputs: (asset.outputs || []).filter((row) => !row.hidden).map((row) => ({
          id: row.id, label: row.label, unit: row.unit,
          value: derived[row.id], formatted: window.ATLAS.fmtNum(derived[row.id]), formula: row.formulaText,
        })),
      })),
      // flat list of all visible outputs (kept for back-compat with cross-plant rollups)
      outputs: (model.assets || []).reduce((rows, asset) => {
        (asset.outputs || []).forEach((row) => {
          if (row.hidden) return;
          rows.push({
            id: row.id, label: row.label, unit: row.unit, asset: asset.name,
            value: derived[row.id], formatted: window.ATLAS.fmtNum(derived[row.id]),
          });
        });
        return rows;
      }, []),
    };
  }

  function buildNetworkContext(plants, model) {
    const finalMetrics = window.ATLAS.finalMetricsWithFmt(model);
    return {
      form: window.ATLAS.meta,
      metricDefs: finalMetrics.map((m) => ({
        id: m.id, label: m.label, unit: m.unit, formula: m.formula, note: m.note,
      })),
      plants: plants.map((p) => plantSnapshot(p, model, finalMetrics)),
    };
  }

  // ---- markdown rendering (sanitized) ----
  function escapeHtml(text) {
    return String(text || "").replace(/[&<>"']/g, (c) =>
      ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[c]));
  }
  function renderMarkdownToHtml(text) {
    const markdown = String(text || "");
    const markedParser = window.marked && window.marked.parse;
    if (!markedParser || !window.DOMPurify) return { __html: escapeHtml(markdown).replace(/\n/g, "<br>") };
    return { __html: window.DOMPurify.sanitize(markedParser(markdown, MARKDOWN_OPTIONS), { USE_PROFILES: { html: true } }) };
  }

  function AgentMessage({ message }) {
    if (message.role === "chart" && window.AtlasChartBlock) {
      return h("div", { className: "agent-msg assistant" },
        h("div", { className: "agent-role" }, "Chart"),
        h(window.AtlasChartBlock, { spec: message.spec }));
    }
    if (message.role === "report" && window.AtlasReportCard) {
      return h("div", { className: "agent-msg assistant" },
        h("div", { className: "agent-role" }, "Report"),
        h(window.AtlasReportCard, { spec: message.spec }));
    }
    const isTool = message.role === "tool";
    const bubbleClass = "agent-bubble" + (isTool ? " plain" : " markdown");
    return h("div", { className: "agent-msg " + message.role },
      h("div", { className: "agent-role" }, message.role === "user" ? "You" : message.role === "tool" ? "Note" : "Agent"),
      isTool
        ? h("div", { className: bubbleClass }, message.text)
        : h("div", { className: bubbleClass, dangerouslySetInnerHTML: renderMarkdownToHtml(message.text) }),
    );
  }

  function PromptChips({ prompts, onPick }) {
    return h("div", { className: "prompt-chips" },
      h("div", { className: "prompt-chips-label" }, "Try"),
      prompts.map((p, i) => h("button", { className: "prompt-chip", type: "button", key: i, onClick: () => onPick(p) }, p)),
    );
  }

  function NetworkAgentPanel({ plants, model, collapsed, onCollapsedChange, wide, onWideChange }) {
    const [messages, setMessages] = useState([{
      role: "assistant",
      text: "I can see all " + plants.length + " plants. Ask me to compare metrics, rank them, or total figures across the network.",
    }]);
    const [draft, setDraft] = useState("");
    const [busy, setBusy] = useState(false);
    const [status, setStatus] = useState(null);
    const [steps, setSteps] = useState([]);
    const [streamText, setStreamText] = useState("");
    const scrollRef = useRef(null);
    const hasConversation = messages.some((m) => m.role === "user");

    const context = useMemo(() => buildNetworkContext(plants, model), [plants, model]);

    useEffect(() => { const el = scrollRef.current; if (el) el.scrollTop = el.scrollHeight; }, [messages, busy, steps, streamText]);

    async function send(override) {
      const text = (typeof override === "string" ? override : draft).trim();
      if (!text || busy) return;
      const nextMessages = messages.concat({ role: "user", text });
      setMessages(nextMessages);
      setDraft("");
      setBusy(true);
      setStatus({ stage: "thinking" });
      setSteps([]);
      setStreamText("");
      try {
        const payload = await streamAgent(
          {
            mode: "network",
            messages: nextMessages.filter((m) => m.role === "user" || m.role === "assistant"),
            context,
          },
          {
            onStatus: (s) => setStatus(s),
            onStep: (s) => setSteps((prev) => prev.concat(s)),
            onDelta: (t) => setStreamText((prev) => prev + t),
          },
        );
        const toolUses = payload.toolUses || [];
        const chartTools = toolUses.filter((t) => t.name === "render_chart");
        const reportTools = toolUses.filter((t) => t.name === "generate_document");
        setMessages((prev) => {
          const additions = [];
          if (payload.text) additions.push({ role: "assistant", text: payload.text });
          chartTools.forEach((t) => additions.push({ role: "chart", spec: t.input }));
          reportTools.forEach((t) => additions.push({ role: "report", spec: t.input }));
          if (!additions.length) additions.push({ role: "assistant", text: "Done." });
          return prev.concat(additions);
        });
      } catch (error) {
        setMessages((prev) => prev.concat({ role: "assistant", text: error.message || "Agent request failed." }));
      } finally {
        setBusy(false);
        setStatus(null);
        setSteps([]);
        setStreamText("");
      }
    }

    if (collapsed) {
      return h("aside", { className: "agent-panel collapsed", "aria-label": "AI Assistant" },
        h("div", { className: "agent-rail" },
          h("button", {
            className: "agent-toggle expand", type: "button",
            title: "Expand AI assistant", "aria-label": "Expand AI assistant",
            "aria-expanded": false, onClick: () => onCollapsedChange(false),
          }, h("span", { className: "agent-chevron", "aria-hidden": true })),
          h("div", { className: "agent-rail-title", "aria-hidden": true }, "AI"),
          h("div", { className: "agent-rail-status" + (busy ? " busy" : ""), title: busy ? "Thinking" : "Ready", "aria-hidden": true }),
        ),
      );
    }

    return h("aside", { className: "agent-panel", "aria-label": "AI Assistant" },
      h("div", { className: "agent-head" },
        h("div", { className: "agent-title-row" },
          h("div", { className: "agent-title" }, "AI Assistant"),
          h("div", { className: "agent-toggle-group" },
            onWideChange ? h("button", {
              className: "agent-toggle resize" + (wide ? " is-wide" : ""), type: "button",
              title: wide ? "Shrink chat pane" : "Widen chat pane to half screen",
              "aria-label": wide ? "Shrink chat pane" : "Widen chat pane",
              "aria-pressed": !!wide, onClick: () => onWideChange(!wide),
            }, h("span", { className: "agent-resize-ic", "aria-hidden": true })) : null,
            h("button", {
              className: "agent-toggle collapse", type: "button",
              title: "Collapse AI assistant", "aria-label": "Collapse AI assistant",
              "aria-expanded": true, onClick: () => onCollapsedChange(true),
            }, h("span", { className: "agent-chevron", "aria-hidden": true })),
          ),
        ),
        h("div", { className: "agent-context" },
          h("span", { className: "agent-chip" }, plants.length + " plants"),
          h("span", { className: "agent-chip" }, context.metricDefs.length + " headline metrics"),
        ),
      ),
      h("div", { className: "agent-messages", ref: scrollRef },
        messages.map((message, idx) => h(AgentMessage, { key: idx, message })),
        busy
          ? (steps.length || streamText
              ? h(StepTrace, { steps, streamText })
              : h("div", { className: "agent-msg assistant" },
                  h("div", { className: "agent-role" }, "Agent"),
                  h("div", { className: "agent-bubble" }, statusLabel(status))))
          : null,
      ),
      h("form", { className: "agent-form", onSubmit: (e) => { e.preventDefault(); send(); } },
        !hasConversation && !busy ? h(PromptChips, { prompts: NETWORK_PROMPTS, onPick: (p) => send(p) }) : null,
        h("div", { className: "agent-form-row" },
          h("textarea", {
            className: "agent-input", value: draft, placeholder: "Ask across all plants", rows: 2,
            onChange: (e) => setDraft(e.target.value),
            onKeyDown: (e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } },
          }),
        ),
        h("button", { className: "agent-send", type: "submit", disabled: busy || !draft.trim() }, "SEND"),
      ),
    );
  }

  window.NetworkAgentPanel = NetworkAgentPanel;
})();
