/* SkyOps Agent — docked, context-aware agent panel (⌘J).
   Pattern used by current agentic products: a persistent side panel that pushes the workspace,
   shows what it can see (context pills, @mentions), plans visibly (tool calls with status),
   reads data on its own, and asks for approval before any write. Background tasks run alongside. */

const { useState: uSAG, useEffect: uEAG, useRef: uRAG } = React;

const agStore = {
  v: (() => { try { return { open: false, w: 420, threads: [], active: null, ...JSON.parse(localStorage.getItem("skyops_agent_v1") || "{}") }; } catch { return { open: false, w: 420, threads: [], active: null }; } })(),
  subs: new Set(),
  set(fn) { this.v = fn(this.v); try { localStorage.setItem("skyops_agent_v1", JSON.stringify({ ...this.v, threads: this.v.threads.slice(0, 12) })); } catch {} document.body.classList.toggle("agent-open", !!this.v.open); document.documentElement.style.setProperty("--agent-w", this.v.w + "px"); this.subs.forEach(s => s(this.v)); },
};
document.body.classList.toggle("agent-open", !!agStore.v.open); document.documentElement.style.setProperty("--agent-w", agStore.v.w + "px");
function useAgent() { const [v, setV] = uSAG(agStore.v); uEAG(() => { agStore.subs.add(setV); return () => agStore.subs.delete(setV); }, []); return v; }
const agOpen = (detail) => window.dispatchEvent(new CustomEvent("skyops:agent", { detail: detail || {} }));

/* ── Tools ──────────────────────────────────────────────────── */
const S = () => window.SITES || [], I = () => window.INSPECTIONS || [];
const sid = (x) => { if (!x) return null; const s = S().find(s => s.id === x || s.name.toLowerCase() === String(x).toLowerCase() || s.name.toLowerCase().startsWith(String(x).toLowerCase())); return s ? s.id : null; };
const AG_TOOLS = {
  portfolio_overview: { w: false, label: () => "Read portfolio overview", run: () => S().map(s => ({ id: s.id, name: s.name, mw: s.capacity_mw, health: s.health, criticals: s.criticals, last: s.lastInspection, next: s.nextInspection })) },
  get_site: { w: false, label: (a) => `Read site ${a.site}`, run: (a) => { const s = S().find(x => x.id === sid(a.site)); if (!s) return "not found"; const p = window.siteProfile ? window.siteProfile(s) : {}; return { ...s, area_ha: p.area, zones: (p.zones || []).map(z => z.id), inspections: I().filter(i => i.siteId === s.id).map(i => ({ id: i.id, cycle: i.cycle, status: i.status, detections: i.detections, critical: i.critical })) }; } },
  list_detections: { w: false, label: (a) => `Query detections${a.severity ? " · " + a.severity : ""}${a.kind ? " · " + a.kind : ""}`, run: (a) => (window.DETECTIONS || []).filter(d => (!a.severity || d.severity === a.severity) && (!a.kind || d.kind === a.kind || d.label.toLowerCase().includes(String(a.kind).toLowerCase())) && (!a.status || d.review === a.status)).slice(0, a.limit || 12).map(d => ({ id: d.id, label: d.label, severity: d.severity, dT: d.dT, conf: d.confidence, temporal: d.temporal, module: d.module, string: d.string, review: d.review })) },
  list_work_orders: { w: false, label: (a) => `Query work orders${a.status ? " · " + a.status : ""}`, run: (a) => (window.woStore?.list || []).filter(w => (!a.site || w.siteId === sid(a.site)) && (!a.status || w.status === a.status)).map(w => ({ id: w.id, site: w.siteId, title: w.title, prio: w.prio, status: w.status, assignee: w.assignee, due: w.due, rar: w.rar, cost: w.cost })) },
  list_alerts: { w: false, label: () => "Read open alerts", run: () => (window.alBuild ? window.alBuild() : []).slice(0, 14).map(a => ({ id: a.id, sev: a.sev, cat: a.cat, site: a.siteId, title: a.title, rule: a.rule })) },
  list_crew: { w: false, label: () => "Read fleet & crew availability", run: () => ({ people: (window.resStore?.data.people || []).map(p => ({ name: p.name, role: p.role, kind: p.kind, region: p.region, skills: p.skills, status: p.status })), drones: (window.resStore?.data.drones || []).map(d => ({ id: d.id, model: d.model, status: d.status, base: d.base })) }) },
  core_situations: { w: false, label: () => "Read correlated situations", run: () => (window.coreModel ? window.coreModel() : { sit: [] }).sit.map(s => ({ id: s.id, site: s.siteId, title: s.title, cause: s.cause, impact: s.impact })) },
  open_view: { w: false, nav: true, label: (a) => `Open ${a.route}${a.site ? " · " + a.site : ""}${a.mic ? " · " + a.mic : ""}`, run: (a) => { const n = window.__inNav; if (!n) return "no nav"; if (a.site) n.setActiveSite(sid(a.site) || a.site); if (a.mic) n.setActiveMic(a.mic); if (a.stage) n.setWsStage(a.stage); n.go(a.route); if (a.severity) setTimeout(() => window.dispatchEvent(new CustomEvent("skyops:review-filter", { detail: { sev: a.severity } })), 80); return "opened"; } },
  create_work_order: { w: true, label: (a) => `Create work order · ${a.title}`, preview: (a) => [["Site", (S().find(s => s.id === sid(a.site)) || {}).name], ["Type", a.type], ["Priority", a.prio], ["Location", a.loc], ["Assignee", a.assignee || "Unassigned"]],
    run: (a) => { const id = window.woNextId(); window.woStore.set(l => [{ id, siteId: sid(a.site) || "pt-ev-03", title: a.title, loc: a.loc || "—", type: a.type || "Electrical", prio: a.prio || "P2", status: a.assignee ? "assigned" : "open", assignee: a.assignee || "Unassigned", created: "2026-04-17", due: a.due || "2026-04-24", cost: a.cost || 1200, rar: a.rar || 2000, verifyMic: "next inspection", checklist: [], mic: a.mic || "", obs: a.obs || "—", origin: "agent" }, ...l]); return `created ${id}`; } },
  assign_work_order: { w: true, label: (a) => `Assign ${a.id} → ${a.assignee}`, preview: (a) => [["Work order", a.id], ["Assignee", a.assignee]], run: (a) => { window.woStore.set(l => l.map(w => w.id === a.id ? { ...w, assignee: a.assignee, status: w.status === "open" ? "assigned" : w.status } : w)); return "assigned"; } },
  draft_warranty_claim: { w: true, label: (a) => `Draft warranty claim · ${a.site}`, preview: (a) => { const el = window.wcEligible ? window.wcEligible(sid(a.site) || "pt-ev-03").filter(x => x.covered) : []; return [["Site", (S().find(s => s.id === sid(a.site)) || {}).name], ["Eligible modules", el.length], ["Value", "€" + el.reduce((s, x) => s + x.value, 0).toLocaleString("en-US")]]; },
    run: (a) => { const s = sid(a.site) || "pt-ev-03"; const el = window.wcEligible(s).filter(x => x.covered); const id = `WC-2026-0${11 + window.wcStore.list.length}`; window.wcStore.set(l => [{ id, siteId: s, mfr: el[0]?.mfr || "—", model: el[0]?.model || "—", ref: "—", status: "draft", created: "2026-04-17", submitted: null, items: el.length, value: el.reduce((t, x) => t + x.value, 0), credited: 0, serials: el.map(x => x.serial), note: "Drafted by SkyOps Agent", origin: "agent" }, ...l]); return `drafted ${id}`; } },
  schedule_inspection: { w: true, label: (a) => `Plan new inspection · ${a.site}`, preview: (a) => [["Site", (S().find(s => s.id === sid(a.site)) || {}).name], ["Opens", "New inspection form, prefilled"]], run: (a) => { window.dispatchEvent(new CustomEvent("skyops:new-insp", { detail: { siteId: sid(a.site) } })); return "form opened"; } },
  approve_decision: { w: true, label: (a) => `Approve Argus decision ${a.id}`, preview: (a) => { const d = window.coreModel().dec.find(x => x.id === a.id); return [["Decision", d ? d.title : a.id], ["Impact", d && d.impact ? "€" + Math.round(d.impact).toLocaleString("en-US") : "—"]]; }, run: (a) => { const d = window.coreModel().dec.find(x => x.id === a.id); if (!d) return "not found"; window.coreExecuteDec(d, "Agent"); return "executed"; } },
};
const AG_SPEC = Object.entries(AG_TOOLS).map(([k, t]) => `${k}${t.w ? " (WRITE — needs user approval)" : ""}`).join(", ");
const AG_ARGS = `Args: get_site{site}, list_detections{severity?,kind?,status?,limit?}, list_work_orders{site?,status?}, open_view{route: portfolio|site|workspace|workorders|alerts|warranty|commercial|fleet|field, site?, mic?, stage?: plan|fly|capture|process|review|report, severity?}, create_work_order{site,title,type: Electrical|Module replace|Cleaning|Vegetation|Tracker,prio: P1|P2|P3,loc?,assignee?,obs?}, assign_work_order{id,assignee}, draft_warranty_claim{site}, schedule_inspection{site}, approve_decision{id}.`;
const AG_SYS = `You are Argus, the SkyOps intelligence agent, embedded in a solar-PV drone inspection & O&M platform. You can call tools: ${AG_SPEC}. ${AG_ARGS}
Respond ONLY with JSON: {"reply": string, "steps": [{"tool": string, "args": object}]}.
Use read tools to ground answers; propose WRITE tools when the user asks for an action (they will approve). Keep "reply" to 2–5 short sentences, cite objects in [brackets] like [WO-2026-0412], [MIC-2026-04-033], [Évora Plana PV]. Reply in the user's language. If tool results are provided, use them and do not repeat the same read.`;

function agParse(t) { try { const m = t.match(/\{[\s\S]*\}/); const j = JSON.parse(m ? m[0] : t); return { reply: j.reply || "", steps: Array.isArray(j.steps) ? j.steps.filter(s => AG_TOOLS[s.tool]) : [] }; } catch { return { reply: t, steps: [] }; } }

async function agRun(q, pills, history, onUpdate) {
  const ctx = window.buildAskContext ? window.buildAskContext(window.__inCtx || {}) : {};
  const base = [...history.slice(-6).map(m => ({ role: m.role === "user" ? "user" : "assistant", content: m.role === "user" ? m.text : (m.text || "") }))];
  const msg = { role: "agent", text: "", steps: [], thinking: true };
  onUpdate(msg);
  if (!window.claude?.complete) {
    const it = window.inIntent ? window.inIntent(q, window.__inCtx || {}, window.__inNav) : null;
    if (it) { it.run(); onUpdate({ ...msg, thinking: false, text: `Done — ${it.label.toLowerCase()}.` }); } else onUpdate({ ...msg, thinking: false, text: "The language model is unavailable right now. Direct commands still work.", err: true });
    return;
  }
  let results = [];
  for (let round = 0; round < 3; round++) {
    const prompt = AG_SYS + "\n\nCONTEXT:\n" + JSON.stringify({ ...ctx, attached: pills.map(p => p.label + (p.data ? ": " + p.data : "")), screen: (document.querySelector(".main")?.innerText || "").slice(0, 2000) }) +
      (results.length ? "\n\nTOOL RESULTS:\n" + JSON.stringify(results).slice(0, 6000) : "") + "\n\nUser: " + q;
    let out;
    try { out = agParse(await window.claude.complete({ messages: [...base, { role: "user", content: prompt }] })); }
    catch { onUpdate({ ...msg, thinking: false, text: "Could not reach the model.", err: true }); return; }
    const reads = out.steps.filter(s => !AG_TOOLS[s.tool].w && !results.some(r => r.tool === s.tool && JSON.stringify(r.args) === JSON.stringify(s.args || {})));
    const writes = out.steps.filter(s => AG_TOOLS[s.tool].w);
    for (const s of reads) {
      const st = { tool: s.tool, args: s.args || {}, label: AG_TOOLS[s.tool].label(s.args || {}), status: "running" };
      msg.steps = [...msg.steps, st]; onUpdate({ ...msg });
      let r; try { r = AG_TOOLS[s.tool].run(s.args || {}); } catch (e) { r = "error"; }
      st.status = "done"; st.result = Array.isArray(r) ? `${r.length} rows` : typeof r === "string" ? r : "ok"; st.data = Array.isArray(r) ? r.slice(0, 8) : null;
      results.push({ tool: s.tool, args: s.args || {}, result: r }); onUpdate({ ...msg, steps: [...msg.steps] });
    }
    if (writes.length) msg.steps = [...msg.steps, ...writes.map(s => ({ tool: s.tool, args: s.args || {}, label: AG_TOOLS[s.tool].label(s.args || {}), status: "approval" }))];
    if (!reads.length || round === 2) { onUpdate({ ...msg, thinking: false, text: out.reply, steps: [...msg.steps] }); return; }
  }
}

/* ── UI ─────────────────────────────────────────────────────── */
function AgentPanel() {
  const v = useAgent();
  const [q, setQ] = uSAG("");
  const [pills, setPills] = uSAG([]);
  const [tab, setTab] = uSAG("chat");
  const [mention, setMention] = uSAG(null);
  const body = uRAG(null), ta = uRAG(null);
  const th = v.threads.find(t => t.id === v.active) || null;
  const msgs = th ? th.msgs : [];
  const ctx = window.__inCtx || {};
  const ctxPill = { k: "ctx", label: ctx.route === "workspace" ? `${ctx.activeMic} · ${ctx.stage}` : ctx.route === "site" ? (S().find(s => s.id === ctx.activeSite) || {}).name : ({ portfolio: "Portfolio" }[ctx.route] || ctx.route), fixed: true };
  const save = (fn) => agStore.set(x => { const id = x.active || ("T" + Date.now()); const ex = x.threads.find(t => t.id === id); const t = ex || { id, title: "", msgs: [] }; const nt = fn(t); return { ...x, active: id, threads: [nt, ...x.threads.filter(y => y.id !== id)] }; });
  const send = async (text) => {
    const t = (text ?? q).trim(); if (!t) return;
    setQ(""); const att = pills; setPills([]);
    const history = msgs;
    save(tt => ({ ...tt, title: tt.title || t.slice(0, 48), msgs: [...tt.msgs, { role: "user", text: t, pills: att.map(p => p.label) }] }));
    let idx = null;
    await agRun(t, att, history, (m) => save(tt => { const ms = [...tt.msgs]; if (idx == null) { idx = ms.length; ms.push(m); } else ms[idx] = { ...m }; return { ...tt, msgs: ms }; }));
  };
  const decide = (mi, si, ok) => save(tt => { const ms = tt.msgs.map((m, i) => { if (i !== mi) return m; const steps = m.steps.map((s, j) => { if (j !== si) return s; if (!ok) return { ...s, status: "rejected" }; let r; try { r = AG_TOOLS[s.tool].run(s.args); } catch { r = "error"; } return { ...s, status: "done", result: r }; }); return { ...m, steps }; }); return { ...tt, msgs: ms }; });
  uEAG(() => {
    const h = (e) => { agStore.set(x => ({ ...x, open: true })); setTab("chat"); if (e.detail.pill) setPills(p => [...p, e.detail.pill]); if (e.detail.q) { if (e.detail.send) setTimeout(() => send(e.detail.q), 0); else setQ(e.detail.q); } setTimeout(() => ta.current && ta.current.focus(), 50); };
    const k = (e) => { if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "j") { e.preventDefault(); agStore.set(x => ({ ...x, open: !x.open })); } };
    window.addEventListener("skyops:agent", h); window.addEventListener("keydown", k);
    return () => { window.removeEventListener("skyops:agent", h); window.removeEventListener("keydown", k); };
  });
  uEAG(() => { body.current && (body.current.scrollTop = body.current.scrollHeight); }, [msgs.length, JSON.stringify(msgs.slice(-1))]);
  if (!v.open) return null;
  const core = window.coreModel ? window.coreModel() : { dec: [] };
  const coreOpen = core.dec.filter(d => !window.coreStore.v.dec[d.id]);
  const mentionList = mention != null ? [...S().map(s => ({ t: s.name, k: "site" })), ...I().slice(0, 12).map(i => ({ t: i.id, k: "inspection" })), ...(window.woStore?.list || []).slice(0, 8).map(w => ({ t: w.id, k: "work order" }))].filter(x => x.t.toLowerCase().includes(mention.toLowerCase())).slice(0, 7) : [];
  const sugg = window.inSuggestionsAgent ? window.inSuggestionsAgent(ctx) : ["What needs my attention today?", "Plan the Q3 re-check for Évora", "Create work orders for the critical hot cells", "Which claims should I file this month?"];

  return (
    <aside className="agent" aria-label="Argus">
      <div className="ag-resize" onPointerDown={(e) => { const x0 = e.clientX, w0 = v.w; const mv = (ev) => agStore.set(x => ({ ...x, w: Math.max(340, Math.min(720, w0 + (x0 - ev.clientX))) })); const up = () => { window.removeEventListener("pointermove", mv); window.removeEventListener("pointerup", up); }; window.addEventListener("pointermove", mv); window.addEventListener("pointerup", up); }}/>
      <div className="ag-hd">
        <span className="in-spark"><Icon name="sparkles" size={13}/></span>
        <div className="ag-hd-t"><b>Argus</b><span>{th ? th.title : "New conversation"}</span></div>
        <button className="ask-hd-btn" title="New conversation" onClick={() => agStore.set(x => ({ ...x, active: null }))}><Icon name="plus" size={13}/></button>
        <button className="ask-hd-btn" title="Close (⌘J)" onClick={() => agStore.set(x => ({ ...x, open: false }))}><Icon name="x" size={13}/></button>
      </div>
      <div className="ag-tabs">{[["chat", "Chat"], ["tasks", `Tasks · ${coreOpen.length}`], ["history", "History"]].map(([k, l]) => <button key={k} className={tab === k ? "on" : ""} onClick={() => setTab(k)}>{l}</button>)}</div>

      {tab === "chat" && <>
        <div className="ag-body" ref={body}>
          {msgs.length === 0 && <div className="ag-welcome">
            <b>What should we work on?</b>
            <p>I see the screen you're on, can read sites, detections, work orders, alerts, crew and warranty data, and can act — I'll ask before changing anything.</p>
            <div className="ag-sugs">{sugg.map(s => <button key={s} onClick={() => send(s)}>{s}</button>)}</div>
          </div>}
          {msgs.map((m, i) => m.role === "user" ? (
            <div key={i} className="ag-u">{m.pills && m.pills.length > 0 && <div className="ag-u-p">{m.pills.map(p => <span key={p}>{p}</span>)}</div>}{m.text}</div>
          ) : (
            <div key={i} className="ag-a">
              {m.steps && m.steps.length > 0 && <div className="ag-steps">
                {m.steps.map((s, j) => s.status === "approval" ? (
                  <div key={j} className="ag-appr">
                    <div className="ag-appr-hd"><Icon name="alert" size={12}/><b>{s.label}</b></div>
                    {AG_TOOLS[s.tool].preview && <div className="ag-appr-b">{AG_TOOLS[s.tool].preview(s.args).map(([k, val]) => <div key={k} className="ps-kv"><span>{k}</span><b>{val}</b></div>)}</div>}
                    <div className="ag-appr-ft"><button className="btn btn-ghost btn-xs" onClick={() => decide(i, j, false)}>Reject</button><button className="btn btn-primary btn-xs" onClick={() => decide(i, j, true)}><Icon name="check" size={11}/>Approve</button></div>
                  </div>
                ) : (
                  <details key={j} className={`ag-step ag-step-${s.status}`}>
                    <summary><i>{s.status === "running" ? <span className="ag-spin"/> : s.status === "rejected" ? <Icon name="x" size={10}/> : <Icon name="check" size={10}/>}</i><span>{s.label}</span><em>{s.status === "rejected" ? "rejected" : s.result || ""}</em></summary>
                    {s.data && s.data.length > 0 && <div className="ag-art"><table><thead><tr>{Object.keys(s.data[0]).slice(0, 5).map(c => <th key={c}>{c}</th>)}</tr></thead><tbody>{s.data.map((r, k) => <tr key={k}>{Object.values(r).slice(0, 5).map((c, z) => <td key={z}>{typeof c === "object" ? JSON.stringify(c).slice(0, 20) : String(c)}</td>)}</tr>)}</tbody></table></div>}
                  </details>))}
              </div>}
              {m.thinking && !m.text ? <span className="in-typing"><i/><i/><i/></span> : <div className={`ag-txt ${m.err ? "err" : ""}`}>{window.InAnswer ? <window.InAnswer text={m.text || ""} nav={window.__inNav}/> : m.text}</div>}
            </div>))}
        </div>
        <div className="ag-comp">
          <div className="ag-pills"><span className="ag-pill fixed"><Icon name="eye" size={10}/>{ctxPill.label}</span>{pills.map((p, i) => <span key={i} className="ag-pill">{p.label}<button onClick={() => setPills(ps => ps.filter((_, j) => j !== i))}><Icon name="x" size={9}/></button></span>)}</div>
          {mentionList.length > 0 && <div className="ag-mention">{mentionList.map(x => <button key={x.t} onClick={() => { setQ(qq => qq.replace(/@[^@\s]*$/, "") + x.t + " "); setPills(p => [...p, { label: x.t }]); setMention(null); ta.current.focus(); }}><span>{x.t}</span><em>{x.k}</em></button>)}</div>}
          <div className="ag-input">
            <textarea ref={ta} rows={2} value={q} placeholder="Ask, or give the agent a task…  @ to mention a site, inspection or work order"
              onChange={e => { setQ(e.target.value); const m = e.target.value.match(/@([^@\s]*)$/); setMention(m ? m[1] : null); }}
              onKeyDown={e => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } }}/>
            <button className="ag-send" disabled={!q.trim()} onClick={() => send()}><Icon name="send" size={13}/></button>
          </div>
          <div className="ag-hint">Enter to send · reads freely · asks before writing · ⌘J to toggle</div>
        </div>
      </>}

      {tab === "tasks" && <div className="ag-body">
        <div className="ag-sec">Running in the background</div>
        {[["Watching SCADA & weather for all 9 sites", "continuous"], ["Correlating alerts into situations", `${core.sit ? core.sit.length : 0} active`], ["Monitoring report SLA", "every 15 min"]].map(([a, b]) => <div key={a} className="ag-task"><span className="ag-spin"/><span>{a}</span><em>{b}</em></div>)}
        <div className="ag-sec">Waiting for your approval · {coreOpen.length}</div>
        {coreOpen.map(d => <div key={d.id} className="ag-appr"><div className="ag-appr-hd"><span className="core-dom">{d.dom}</span><b>{d.title}</b></div><div className="ag-appr-why">{d.why}</div>
          <div className="ag-appr-ft">{d.impact > 0 && <span className="mono core-imp">€{Math.round(d.impact).toLocaleString("en-US")}</span>}<span className="grow"/><button className="btn btn-ghost btn-xs" onClick={() => agOpen({ q: `Explain decision ${d.id}: ${d.title}`, send: true })}>Discuss</button><button className="btn btn-primary btn-xs" onClick={() => window.coreExecuteDec(d)}>Approve</button></div></div>)}
      </div>}

      {tab === "history" && <div className="ag-body">
        {v.threads.length === 0 && <div className="sec" style={{ fontSize: 12 }}>No conversations yet.</div>}
        {v.threads.map(t => <button key={t.id} className={`ag-hist ${t.id === v.active ? "on" : ""}`} onClick={() => { agStore.set(x => ({ ...x, active: t.id })); setTab("chat"); }}><b>{t.title || "Untitled"}</b><em>{t.msgs.length} messages</em></button>)}
      </div>}
    </aside>
  );
}

function AgentToggle() {
  const v = useAgent();
  return <button className={`ag-toggle ${v.open ? "on" : ""}`} onClick={() => agStore.set(x => ({ ...x, open: !x.open }))} title="Argus (⌘J)"><Icon name="sparkles" size={13}/>Argus<kbd>⌘J</kbd></button>;
}

Object.assign(window, { AgentPanel, AgentToggle, agOpen, agStore });
