// Conductor (agent office + missions) + Swarm + Profiles
const { useState, useEffect } = React;

// live===true -> backend, live===false -> offline/fallback, null -> loading
function LiveBadge({ live, offlineLabel = "preview data" }) {
  if (live === null) return null;
  return live
    ? <span className="badge ok"><span className="d" />live</span>
    : <span className="badge warn"><span className="d" />{offlineLabel}</span>;
}

function MissionRow({ m, onStop, onOpen }) {
  const agents = m.agents || [];
  const steps = m.steps || 0;
  const done = m.done || 0;
  const running = m.status !== "done";
  return (
    <div className="card" style={{ display: "flex", flexDirection: "column", gap: 12 }}>
      <div className="flex between">
        <div className="flex" style={{ gap: 10 }}>
          <div style={{ display: "flex" }}>{agents.map((id, i) => <div key={id + i} style={{ marginLeft: i ? -8 : 0, border: "2px solid var(--surface)", borderRadius: 9 }}><AgentBot agent={id} size={28} /></div>)}</div>
          <div><div style={{ fontWeight: 650, fontSize: 14 }}>{m.title}</div><div className="faint mono" style={{ fontSize: 11.5 }}>{agents.length} agents · {m.eta || "—"}</div></div>
        </div>
        <div className="flex" style={{ gap: 8 }}>
          {m.status === "done"
            ? <span className="badge ok"><Icon name="check" size={12} />done</span>
            : <span className="badge warn"><StatusDot status="provisioning" live />running</span>}
          {running && onStop && <button className="btn btn-danger btn-sm" onClick={() => onStop(m)} title="Stop mission"><Icon name="x" size={13} />Stop</button>}
        </div>
      </div>
      <div className="flex" style={{ gap: 10 }}>
        <div style={{ flex: 1 }}><Meter value={steps ? done / steps : 0} tone={m.status === "done" ? "ok" : ""} /></div>
        <span className="mono faint" style={{ fontSize: 11.5 }}>{done}/{steps} steps</span>
      </div>
      <button className="btn btn-subtle btn-sm" style={{ alignSelf: "flex-start" }} onClick={() => onOpen(m)}><Icon name="activity" size={13} />View activity</button>
    </div>
  );
}

// Normalize a backend mission into the shape MissionRow expects (defensive — API mission shape varies).
function normMission(raw, i) {
  if (!raw || typeof raw !== "object") return null;
  const assignments = Array.isArray(raw.assignments) ? raw.assignments : [];
  const steps = raw.steps ?? raw.totalTasks ?? raw.taskCount ?? assignments.length;
  const done = raw.done ?? raw.completedTasks ?? raw.completed ?? assignments.filter((a) => ["done", "complete", "completed", "succeeded"].includes(String(a.state || a.status).toLowerCase())).length;
  const status = raw.status || raw.state || (steps && done >= steps ? "done" : "running");
  const workers = assignments.map((a) => a.workerId || a.agentId || a.worker).filter(Boolean);
  return {
    id: raw.id || raw.missionId || raw.sessionKey || ("m" + i),
    title: raw.title || raw.name || raw.goal || "Mission",
    status: status === "complete" || status === "completed" || status === "succeeded" ? "done" : status,
    agents: Array.isArray(raw.agents) ? raw.agents : (Array.isArray(raw.workers) ? raw.workers.map((w) => w.workerId || w.id || w) : workers),
    steps,
    done,
    eta: raw.eta || raw.elapsed || "",
    sessionKey: raw.sessionKey,
    missionId: raw.id || raw.missionId,
    assignments,
    lines: Array.isArray(raw.lines) ? raw.lines : [],
    updatedAt: raw.updatedAt || raw.updated_at || raw.createdAt || raw.created_at,
  };
}

function normConductorAgent(raw, i, source) {
  if (!raw || typeof raw !== "object") return null;
  const id = raw.workerId || raw.id || raw.name || ("agent-" + i);
  const name = raw.displayName || raw.humanLabel || raw.name || raw.role || id;
  const visual = window.agentVisual ? window.agentVisual(id) : {};
  const backendStatus = String(raw.status || raw.gatewayState || "").toLowerCase();
  const running = raw.processAlive === true || ["active", "online", "ready", "running"].includes(backendStatus);
  const status = running ? "running" : (backendStatus && backendStatus !== "unknown" ? backendStatus : ((source === "profile" || raw.profileFound === true) ? "configured" : "idle"));
  return {
    ...raw,
    id,
    name: name === "default" ? "Workspace" : name,
    role: (raw.role && !["profile", "primary profile", "worker"].includes(String(raw.role).toLowerCase()) ? raw.role : "") || raw.specialty || visual.title || (source === "profile" ? "Configured profile" : "Worker"),
    status,
    model: raw.model || raw.shortModel || "Inherited default",
    provider: raw.provider || "",
    blurb: raw.specialty || raw.mission || raw.description || raw.desc || visual.blurb || "",
    profileFound: raw.profileFound !== false,
    source,
    color: visual.color || (window.agentById ? window.agentById(id).color : null) || "var(--primary)",
    emoji: visual.emoji || "🤖",
    zone: visual.zone || "engineering",
    order: visual.order ?? 99,
  };
}

function mergeConductorAgents(profiles, crew, roster) {
  const map = new Map();
  function add(raw, source, i) {
    const agent = normConductorAgent(raw, i, source);
    if (!agent) return;
    const rawKey = String(agent.id).toLowerCase();
    const key = rawKey === "default" || rawKey === "workspace" ? "workspace" : rawKey;
    const previous = map.get(key);
    map.set(key, previous ? { ...previous, ...agent, source: previous.source + "+" + source } : agent);
  }
  profiles.forEach((p, i) => add(p, "profile", i));
  roster.forEach((p, i) => add(p, "roster", i));
  crew.forEach((p, i) => add(p, "runtime", i));
  return Array.from(map.values()).sort((a, b) => a.name.localeCompare(b.name));
}

function AgentDetailModal({ agent, onClose }) {
  const metrics = [
    ["Sessions", agent.sessionCount], ["Messages", agent.messageCount], ["Tool calls", agent.toolCallCount],
    ["Assigned tasks", agent.assignedTaskCount], ["Cron jobs", agent.cronJobCount], ["Tokens", agent.totalTokens == null ? null : fmt(agent.totalTokens)],
  ].filter((entry) => entry[1] != null);
  const skills = Array.isArray(agent.skills) ? agent.skills : (Array.isArray(agent.capabilities) ? agent.capabilities : []);
  return (
    <div className="modal-scrim" onClick={onClose}>
      <div className="modal" onClick={(e) => e.stopPropagation()}>
        <div className="modal-head">
          <AgentBot agent={agent} size={42} ring />
          <div><h2>{agent.name}</h2><p>{agent.role}</p></div>
          <button className="btn btn-ghost btn-sm x" onClick={onClose}><Icon name="x" size={15} /></button>
        </div>
        <div className="modal-body grid" style={{ gap: 14 }}>
          <div className="flex wrap" style={{ gap: 8 }}>
            <span className={cls("badge", agent.status === "running" ? "ok" : "")}><span className="d" />{agent.status}</span>
            <span className="tag">{agent.model}</span>{agent.provider && <span className="tag">{agent.provider}</span>}
            <span className="tag">{agent.source}</span>
          </div>
          {agent.blurb && <p className="muted" style={{ margin: 0, lineHeight: 1.55 }}>{agent.blurb}</p>}
          {metrics.length > 0 && <div className="grid cols-3" style={{ gap: 8 }}>{metrics.map(([label, value]) => <div className="card" key={label} style={{ padding: 10, borderRadius: 8 }}><div className="faint" style={{ fontSize: 11 }}>{label}</div><div className="mono" style={{ marginTop: 4 }}>{value}</div></div>)}</div>}
          {agent.lastSessionTitle && <div><div className="faint" style={{ fontSize: 11, marginBottom: 4 }}>Last session</div><div>{agent.lastSessionTitle}</div>{agent.lastSessionAt && <div className="faint mono" style={{ fontSize: 11, marginTop: 3 }}>{new Date(agent.lastSessionAt).toLocaleString()}</div>}</div>}
          {skills.length > 0 && <div className="flex wrap" style={{ gap: 6 }}>{skills.map((skill) => <span className="tag" key={String(skill)}>{String(skill)}</span>)}</div>}
        </div>
      </div>
    </div>
  );
}

function MissionDetailModal({ mission, detail, loading, onClose }) {
  const current = detail || mission;
  const assignments = current.assignments || mission.assignments || [];
  const lines = current.lines || [];
  return (
    <div className="modal-scrim" onClick={onClose}>
      <div className="modal" onClick={(e) => e.stopPropagation()}>
        <div className="modal-head">
          <div className="ic"><Icon name="activity" size={20} /></div>
          <div><h2>{mission.title}</h2><p>Live mission checkpoints and worker activity.</p></div>
          <button className="btn btn-ghost btn-sm x" onClick={onClose}><Icon name="x" size={15} /></button>
        </div>
        <div className="modal-body grid" style={{ gap: 14 }}>
          {loading && <div className="muted">Loading activity…</div>}
          {assignments.length > 0 && <div className="grid" style={{ gap: 8 }}>{assignments.map((a, i) => <div className="card" key={a.workerId || i} style={{ padding: 11, borderRadius: 8 }}><div className="flex between"><strong>{a.workerId || a.agentId || "Worker"}</strong><span className="badge"><span className="d" />{a.state || a.status || "assigned"}</span></div><div className="muted" style={{ fontSize: 12, marginTop: 5 }}>{a.task || a.checkpoint || a.result || "Waiting for a checkpoint."}</div></div>)}</div>}
          <div><div className="faint mono" style={{ fontSize: 11, marginBottom: 7, textTransform: "uppercase" }}>Activity log</div><pre className="card mono" style={{ padding: 12, borderRadius: 8, maxHeight: 320, overflow: "auto", whiteSpace: "pre-wrap", fontSize: 11.5, margin: 0 }}>{lines.length ? lines.join("\n") : "No activity has been recorded yet."}</pre></div>
        </div>
      </div>
    </div>
  );
}

function ConductorHelpModal({ onClose }) {
  return <div className="modal-scrim" onClick={onClose}><div className="modal sm" onClick={(e) => e.stopPropagation()}><div className="modal-head"><div className="ic"><Icon name="help" size={20} /></div><div><h2>How Mission Control works</h2><p>This view is connected to the live Hermes APIs.</p></div><button className="btn btn-ghost btn-sm x" onClick={onClose}><Icon name="x" size={15} /></button></div><div className="modal-body"><ol className="muted" style={{ lineHeight: 1.7, margin: 0, paddingLeft: 20 }}><li>Configured profiles and active workers are merged into the agent roster.</li><li>A new mission is decomposed and assigned to parallel workers.</li><li>Mission cards poll for status; View activity shows worker checkpoints and logs.</li><li>Stopping a mission sends its session and mission IDs to the Conductor backend.</li></ol></div></div></div>;
}

function AgentOfficeCard({ agent, onOpen, featured = false }) {
  return (
    <button className={cls("office-agent", featured && "featured")} onClick={() => onOpen(agent)} aria-label={`Open ${agent.name} agent details`} style={{ "--agent-color": agent.color }}>
      <AgentBot agent={agent} size={featured ? 58 : 48} ring />
      <span className="office-agent-copy">
        <strong>{agent.name}</strong>
        <small>{agent.role}</small>
      </span>
      <span className={cls("office-agent-status", agent.status === "running" && "running")}><span />{agent.status}</span>
    </button>
  );
}

function OfficeZone({ zone, title, subtitle, agents, onOpen }) {
  return (
    <section className={cls("office-zone", "office-zone-" + zone)} aria-label={title}>
      <div className="office-zone-head">
        <div><span className="office-zone-kicker">{title}</span><p>{subtitle}</p></div>
        <span className="tag">{agents.length}</span>
      </div>
      <div className="office-zone-agents">
        {agents.map((agent) => <AgentOfficeCard key={agent.id} agent={agent} onOpen={onOpen} featured={zone === "leadership"} />)}
      </div>
    </section>
  );
}

function NewMissionModal({ onClose, onSpawn }) {
  const [goal, setGoal] = useState("");
  const [maxParallel, setMaxParallel] = useState(2);
  const [supervised, setSupervised] = useState(false);
  const [saving, setSaving] = useState(false);

  async function save() {
    if (!goal.trim()) return;
    setSaving(true);
    try {
      await onSpawn({ goal: goal.trim(), maxParallel, supervised });
      onClose();
    } finally {
      setSaving(false);
    }
  }

  return (
    <div className="modal-scrim" onClick={onClose}>
      <div className="modal sm" onClick={(e) => e.stopPropagation()}>
        <div className="modal-head">
          <div className="ic"><Icon name="rocket" size={20} /></div>
          <div>
            <h2>New mission</h2>
            <p>Decompose a goal across parallel workers.</p>
          </div>
          <button className="btn btn-ghost btn-sm x" onClick={onClose}><Icon name="x" size={15} /></button>
        </div>
        <div className="modal-body">
          <div className="field">
            <label>Goal</label>
            <textarea className="input" rows={4} placeholder="e.g. Ship statute search v2 across the API and web app" value={goal} onChange={(e) => setGoal(e.target.value)} style={{ resize: "vertical", fontFamily: "inherit" }} />
          </div>
          <div className="field">
            <label>Max parallel workers · {maxParallel}</label>
            <input type="range" min={1} max={5} step={1} value={maxParallel} onChange={(e) => setMaxParallel(Number(e.target.value))} style={{ width: "100%" }} />
          </div>
          <div className="flex between">
            <div>
              <div style={{ fontWeight: 600, fontSize: 13.5 }}>Supervised mode</div>
              <div className="faint" style={{ fontSize: 12 }}>Require approval before each task</div>
            </div>
            <Toggle on={supervised} onChange={setSupervised} />
          </div>
        </div>
        <div className="modal-foot">
          <button className="btn btn-ghost" onClick={onClose}>Cancel</button>
          <button className="btn btn-primary" disabled={saving || !goal.trim()} onClick={save}>
            <Icon name="rocket" size={15} />{saving ? "Launching…" : "Launch mission"}
          </button>
        </div>
      </div>
    </div>
  );
}

function PageConductor({ go, toast }) {
  const [agents, setAgents] = useState(null);
  const [missions, setMissions] = useState(null);
  const [live, setLive] = useState(null);
  const [modal, setModal] = useState(false);
  const [selectedAgent, setSelectedAgent] = useState(null);
  const [selectedMission, setSelectedMission] = useState(null);
  const [missionDetail, setMissionDetail] = useState(null);
  const [detailLoading, setDetailLoading] = useState(false);
  const [help, setHelp] = useState(false);
  const [lastRefresh, setLastRefresh] = useState(null);
  const [loadError, setLoadError] = useState("");

  async function load(silent) {
    const results = await Promise.allSettled([
      HERMES.conductorMissions(), HERMES.swarmMissions(), HERMES.listProfiles(), HERMES.crewStatus(), HERMES.swarmRoster(),
    ]);
    const [conductorResult, swarmResult, profileResult, crewResult, rosterResult] = results;
    const conductorRaw = conductorResult.status === "fulfilled" ? (Array.isArray(conductorResult.value) ? conductorResult.value : (conductorResult.value?.missions || [])) : [];
    const swarmRaw = swarmResult.status === "fulfilled" ? (Array.isArray(swarmResult.value) ? swarmResult.value : (swarmResult.value?.missions || swarmResult.value?.data?.missions || [])) : [];
    const missionMap = new Map();
    conductorRaw.concat(swarmRaw).map(normMission).filter(Boolean).forEach((m) => missionMap.set(String(m.id), { ...(missionMap.get(String(m.id)) || {}), ...m }));
    const profiles = profileResult.status === "fulfilled" ? (profileResult.value?.profiles || (Array.isArray(profileResult.value) ? profileResult.value : [])) : [];
    const crew = crewResult.status === "fulfilled" ? (crewResult.value?.crew || crewResult.value?.agents || (Array.isArray(crewResult.value) ? crewResult.value : [])) : [];
    const rosterValue = rosterResult.status === "fulfilled" ? rosterResult.value : null;
    const roster = rosterValue?.roster?.workers || rosterValue?.workers || (Array.isArray(rosterValue) ? rosterValue : []);
    const mergedAgents = mergeConductorAgents(profiles, crew, roster);
    setMissions(Array.from(missionMap.values()));
    setAgents(mergedAgents);
    const missionOnline = conductorResult.status === "fulfilled" || swarmResult.status === "fulfilled";
    const rosterOnline = profileResult.status === "fulfilled" || crewResult.status === "fulfilled" || rosterResult.status === "fulfilled";
    setLive(missionOnline && rosterOnline);
    setLoadError(missionOnline && rosterOnline ? "" : "One or more Mission Control services are unavailable. Showing the live data that could be reached.");
    setLastRefresh(new Date());
    if (!silent && !missionOnline && !rosterOnline) toast && toast("Mission Control APIs are offline");
  }
  useEffect(() => {
    let alive = true;
    const refresh = async (silent) => { if (alive) await load(silent); };
    refresh(false);
    const timer = setInterval(() => refresh(true), 15000);
    return () => { alive = false; clearInterval(timer); };
  }, []);

  useEffect(() => {
    if (!selectedMission) return undefined;
    let alive = true;
    async function refreshDetail() {
      setDetailLoading(true);
      try {
        const res = await HERMES.conductorMission(selectedMission.missionId || selectedMission.id, 400);
        if (!alive) return;
        setMissionDetail(normMission(res?.mission || res, 0));
      } catch (err) {
        if (alive) setMissionDetail({ ...selectedMission, lines: [err.message || "Mission activity could not be loaded."] });
      } finally {
        if (alive) setDetailLoading(false);
      }
    }
    refreshDetail();
    const timer = selectedMission.status === "done" ? null : setInterval(refreshDetail, 5000);
    return () => { alive = false; if (timer) clearInterval(timer); };
  }, [selectedMission?.id]);

  async function spawn(body) {
    try {
      await HERMES.conductorSpawn(body);
      toast && toast("Mission launched");
      load(true);
    } catch (err) {
      toast && toast(err.message || "Could not launch mission");
      throw err;
    }
  }

  async function stop(m) {
    try {
      await HERMES.conductorStop({ sessionKeys: m.sessionKey ? [m.sessionKey] : [], missionIds: m.missionId ? [m.missionId] : [] });
      toast && toast("Mission stopped");
      load(true);
    } catch (err) {
      toast && toast(err.message || "Could not stop mission");
    }
  }

  const agentList = agents || [];
  const list = missions || [];
  const runningAgents = agentList.filter((a) => a.status === "running").length;
  const assignedTasks = agentList.reduce((sum, a) => sum + Number(a.assignedTaskCount || a.tasks || 0), 0);
  const orchestrator = agentList.find((a) => a.zone === "orchestrator");
  const agentsIn = (zone) => agentList.filter((a) => a.zone === zone).sort((a, b) => (a.order ?? 99) - (b.order ?? 99));
  const leadership = agentsIn("leadership");
  const product = agentsIn("product");
  const engineering = agentsIn("engineering");
  const quality = agentsIn("quality");

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <div className="eyebrow">Conductor</div>
          <h1 className="page-title">Mission Control</h1>
          <p className="page-sub">Launch a mission and watch your agent team build it live.</p>
        </div>
        <div className="flex" style={{ gap: 8 }}>
          <LiveBadge live={live} offlineLabel="partial outage" />
          <button className="btn btn-ghost btn-sm" onClick={() => setHelp(true)}><Icon name="help" size={15} />How it works</button>
          <button className="btn btn-primary" onClick={() => setModal(true)}><Icon name="rocket" size={16} />New mission</button>
        </div>
      </div>

      <div className="flex wrap" style={{ gap: 8, marginBottom: 12 }}>
        <span className="tag">{agentList.length} configured/runtime agents</span>
        <span className="tag">{runningAgents} running</span>
        <span className="tag">{assignedTasks} assigned tasks</span>
        {lastRefresh && <span className="tag">updated {lastRefresh.toLocaleTimeString()}</span>}
      </div>
      <div className="mission-team-note">
        <div><strong>One team, two workspaces.</strong><span>Operations manages persistent agent conversations and configuration. Mission Control coordinates those same profiles for time-bound missions.</span></div>
        {go && <button className="btn btn-subtle btn-sm" onClick={() => go("operations")}><Icon name="agents" size={13} />Open Operations</button>}
      </div>
      {loadError && <div className="card" style={{ marginBottom: 12, borderColor: "var(--warn)", color: "var(--warn)" }}>{loadError}</div>}

      <div className="office org-office" style={{ marginBottom: "var(--gap)" }}>
        <div className="office-floor-label"><Icon name="conductor" size={14} />Sylar's delivery organization</div>
        {agents === null ? <div className="empty muted">Loading the organization…</div> : <>
          {orchestrator && <div className="mission-orchestrator"><span className="office-zone-kicker">Mission orchestration</span><AgentOfficeCard agent={orchestrator} onOpen={setSelectedAgent} /></div>}
          <div className="office-zones">
            <OfficeZone zone="leadership" title="Executive suite" subtitle="Architecture, strategy, and risk" agents={leadership} onOpen={setSelectedAgent} />
            <OfficeZone zone="product" title="Product studio" subtitle="Discovery, definition, and experience" agents={product} onOpen={setSelectedAgent} />
            <OfficeZone zone="engineering" title="Engineering bay" subtitle="AI, platform, web, mobile, and connected devices" agents={engineering} onOpen={setSelectedAgent} />
            <OfficeZone zone="quality" title="Quality & release" subtitle="Verification, review, delivery, and final gates" agents={quality} onOpen={setSelectedAgent} />
          </div>
        </>}
      </div>

      <div className="card-head"><h2 className="mono" style={{ fontSize: 13, letterSpacing: ".06em", textTransform: "uppercase" }}>Missions</h2></div>
      {missions === null
        ? <div className="empty muted">Loading missions…</div>
        : list.length === 0
          ? <div className="empty muted">No missions yet. Launch one to get started.</div>
          : <div className="grid" style={{ gap: 12 }}>{list.map((m) => <MissionRow key={m.id} m={m} onStop={stop} onOpen={(mission) => { setMissionDetail(null); setSelectedMission(mission); }} />)}</div>}

      {modal && <NewMissionModal onClose={() => setModal(false)} onSpawn={spawn} />}
      {selectedAgent && <AgentDetailModal agent={selectedAgent} onClose={() => setSelectedAgent(null)} />}
      {selectedMission && <MissionDetailModal mission={selectedMission} detail={missionDetail} loading={detailLoading} onClose={() => { setSelectedMission(null); setMissionDetail(null); }} />}
      {help && <ConductorHelpModal onClose={() => setHelp(false)} />}
    </div>
  );
}
window.PageConductor = PageConductor;

// Map a roster worker (workerId, role, status, model, specialty) into the existing agent-card shape.
function normWorker(w, i) {
  if (!w || typeof w !== "object") return null;
  const id = w.workerId || w.id || w.name || ("w" + i);
  return {
    id,
    name: w.displayName || w.name || w.role || id,
    role: w.role || "Worker",
    status: w.status || "idle",
    model: w.model || w.shortModel || "—",
    blurb: w.specialty || w.mission || "",
    color: (window.agentById ? window.agentById(id).color : null) || "var(--primary)",
    tasks: w.tasks ?? 0,
    tokens: w.tokens ?? 0,
  };
}

function PageSwarm({ go, toast }) {
  const [agents, setAgents] = useState(null);
  const [live, setLive] = useState(null);
  const tone = { active: "ok", online: "ok", ready: "ok", running: "warn", idle: "", break: "warn", blocked: "bad", offline: "bad" };

  useEffect(() => {
    let alive = true;
    HERMES.swarmRoster()
      .then((res) => {
        if (!alive) return;
        const workers = res?.roster?.workers || res?.workers || (Array.isArray(res) ? res : []);
        if (!workers.length) throw new Error("empty roster");
        setAgents(workers.map(normWorker).filter(Boolean));
        setLive(true);
      })
      .catch(() => {
        // Fallback 1: crewStatus, then fallback 2: window.DATA.agents
        HERMES.crewStatus()
          .then((res) => {
            if (!alive) return;
            const crew = res?.crew || (Array.isArray(res) ? res : []);
            if (!crew.length) throw new Error("empty crew");
            setAgents(crew.map((c, i) => normWorker({
              workerId: c.workerId || c.displayName || ("crew" + i),
              displayName: c.displayName,
              role: c.role,
              status: !c.profileFound || c.gatewayState === "unknown" ? "idle" : (c.gatewayState === "running" && c.processAlive ? "online" : "offline"),
              model: c.model,
              specialty: c.specialty,
            }, i)).filter(Boolean));
            setLive(true);
          })
          .catch(() => {
            if (!alive) return;
            setAgents(window.DATA.agents || []);
            setLive(false);
          });
      });
    return () => { alive = false; };
  }, []);

  const list = agents || [];
  return (
    <div className="page">
      <PageHead eyebrow="Agents" title="Swarm" sub="Your full roster of agents, their models, and current load.">
        <LiveBadge live={live} />
        <button className="btn btn-primary" onClick={() => toast && toast("Spawned a new agent")}><Icon name="plus" size={16} />Add agent</button>
      </PageHead>
      {agents === null
        ? <div className="empty muted">Loading roster…</div>
        : list.length === 0
          ? <div className="empty muted">No agents in the roster yet.</div>
          : (
            <div className="swarm-grid">
              {list.map((a) => (
                <div className="card agent-card" key={a.id}>
                  <div className="agent-top">
                    <AgentBot agent={a} size={44} ring />
                    <div style={{ minWidth: 0, flex: 1 }}>
                      <div style={{ fontWeight: 650, fontSize: 15 }}>{a.name}</div>
                      <div className="faint" style={{ fontSize: 12 }}>{a.role}</div>
                    </div>
                    <span className={cls("badge", tone[a.status])}><span className="d" />{a.status}</span>
                  </div>
                  {a.blurb && <p className="muted" style={{ fontSize: 12.5, lineHeight: 1.5, margin: 0 }}>{a.blurb}</p>}
                  <div className="flex wrap" style={{ gap: 8 }}>
                    <span className="tag"><Icon name="bolt" size={11} style={{ display: "inline", verticalAlign: "-1px", marginRight: 4 }} />{a.model}</span>
                    {a.tasks ? <span className="tag">{a.tasks} tasks</span> : null}
                    {a.tokens ? <span className="tag">{fmt(a.tokens)} tok</span> : null}
                  </div>
                  <div className="flex" style={{ gap: 8, marginTop: "auto" }}>
                    <button className="btn btn-ghost btn-sm" onClick={() => go("chat")}><Icon name="chat" size={13} />Message</button>
                    <button className="btn btn-subtle btn-sm" onClick={() => toast && toast("Config opened")}><Icon name="sliders" size={13} />Configure</button>
                  </div>
                </div>
              ))}
            </div>
          )}
    </div>
  );
}
window.PageSwarm = PageSwarm;

// Map a backend profile {name, model, path} into the existing profile-card shape.
function normProfile(p, i) {
  if (!p || typeof p !== "object") return null;
  const name = p.name || ("profile" + i);
  return {
    id: name,
    name: name === "default" ? "Workspace" : name,
    base: p.base || "—",
    model: p.model || "Custom",
    skills: p.skills ?? 0,
    temp: p.temp ?? p.temperature ?? "—",
    desc: p.description || p.desc || p.path || "",
  };
}

function PageProfiles({ toast }) {
  const [profiles, setProfiles] = useState(null);
  const [live, setLive] = useState(null);

  useEffect(() => {
    let alive = true;
    HERMES.listProfiles()
      .then((res) => {
        if (!alive) return;
        const list = res?.profiles || (Array.isArray(res) ? res : []);
        setProfiles(list.map(normProfile).filter(Boolean));
        setLive(true);
      })
      .catch(() => {
        if (!alive) return;
        setProfiles((window.DATA.profiles || []).map(normProfile).filter(Boolean));
        setLive(false);
      });
    return () => { alive = false; };
  }, []);

  const list = profiles || [];
  return (
    <div className="page">
      <PageHead eyebrow="Knowledge" title="Profiles" sub="Reusable agent presets — base persona, model, skills, and temperature.">
        <LiveBadge live={live} />
        <button className="btn btn-primary" onClick={() => toast && toast("New profile")}><Icon name="plus" size={16} />New profile</button>
      </PageHead>
      {profiles === null
        ? <div className="empty muted">Loading profiles…</div>
        : list.length === 0
          ? <div className="empty muted">No profiles yet.</div>
          : (
            <div className="grid cols-2">
              {list.map((p) => (
                <div className="card" key={p.id} style={{ display: "flex", flexDirection: "column", gap: 12 }}>
                  <div className="flex between">
                    <div className="flex" style={{ gap: 11 }}>
                      <div className="agent-badge" style={{ background: "var(--primary-soft)", color: "var(--primary)" }}><Icon name="profiles" size={20} /></div>
                      <div><div style={{ fontWeight: 650, fontSize: 15 }}>{p.name}</div><div className="faint mono" style={{ fontSize: 11.5 }}>base: {p.base} · temp {p.temp}</div></div>
                    </div>
                    <button className="icon-btn" style={{ width: 32, height: 32 }} onClick={() => toast && toast("Edit " + p.name)}><Icon name="edit" size={15} /></button>
                  </div>
                  {p.desc && <p className="muted" style={{ fontSize: 12.5, margin: 0, lineHeight: 1.5 }}>{p.desc}</p>}
                  <div className="flex wrap" style={{ gap: 8 }}>
                    <span className="tag"><Icon name="bolt" size={11} style={{ display: "inline", verticalAlign: "-1px", marginRight: 4 }} />{p.model}</span>
                    <span className="tag"><Icon name="skills" size={11} style={{ display: "inline", verticalAlign: "-1px", marginRight: 4 }} />{p.skills} skills</span>
                  </div>
                </div>
              ))}
            </div>
          )}
    </div>
  );
}
window.PageProfiles = PageProfiles;
