// 运行状态 —— 跨 Studio 与底部抽屉共享。
import type { ExecEvent } from "./api";

export type RunPhase = "idle" | "submitting" | "streaming" | "done" | "error";

export interface RunEvent {
  t: number; // 相对开始的毫秒
  label: string;
}

export interface RunState {
  phase: RunPhase;
  taskId?: string;
  output: string;
  events: RunEvent[];
  exec: ExecEvent[]; // 后端回流的节点级执行轨迹（运行·观测）
  error?: string;
  lifecycle?: string; // 轮询到的后端任务状态（waiting 时弹审批；done/rejected 等终态）
  detail?: string; // 状态附带说明（如审批标题）
}

export const emptyRun: RunState = { phase: "idle", output: "", events: [], exec: [] };

// ---- 执行轨迹派生：把扁平 ExecEvent 流归并为按节点聚合的轨迹 ----

export type NodeStatus = "running" | "done" | "error" | "info" | "waiting";

export interface NodeTrace {
  node: string;
  kind: string; // memory|tool|prompt|model|plan|section|render|system
  label: string;
  status: NodeStatus;
  ms?: number;
  detail?: string;
  notes: string[]; // info 子事件（如检索到的参考资料预览）
  order: number;
}

// PendingApproval 是一个待人工审批的中断（HITL）：审批节点已发 await 事件、尚未被 end/error 收口。
export interface PendingApproval {
  node: string;
  title: string;
  summary: string;
}

// pendingApproval 从执行事件流里找出当前待审批的中断：取最后一个 kind=approval & phase=await，
// 且其后该节点没有 end/error（未被批准/拒绝收口）的事件。无则 null。
export function pendingApproval(events: ExecEvent[]): PendingApproval | null {
  let pending: PendingApproval | null = null;
  for (const e of events) {
    if (e.kind !== "approval") continue;
    if (e.phase === "await") {
      pending = { node: e.node, title: e.label || "人工审批", summary: e.detail || "" };
    } else if (e.phase === "end" || e.phase === "error") {
      if (pending && pending.node === e.node) pending = null; // 已决，收口
    }
  }
  return pending;
}

// deriveNodes 把事件流按 node 归并：start→running，end→done(带耗时)，error→error，info→点事件/附注。
export function deriveNodes(events: ExecEvent[]): NodeTrace[] {
  const map = new Map<string, NodeTrace>();
  let order = 0;
  for (const e of events) {
    let n = map.get(e.node);
    if (!n) {
      n = { node: e.node, kind: e.kind, label: e.label, status: "info", notes: [], order: order++ };
      map.set(e.node, n);
    }
    if (e.label) n.label = e.label;
    n.kind = e.kind;
    switch (e.phase) {
      case "start":
        if (n.status !== "done" && n.status !== "error") n.status = "running";
        break;
      case "await": // HITL 审批节点暂停，等人工决定
        n.status = "waiting";
        break;
      case "end":
        n.status = "done";
        n.ms = e.ms;
        if (e.detail) n.detail = e.detail;
        break;
      case "error":
        n.status = "error";
        n.ms = e.ms;
        if (e.detail) n.detail = e.detail;
        break;
      case "info":
        if (e.detail) n.notes.push(e.detail);
        else if (e.label) n.notes.push(e.label);
        break;
    }
  }
  return [...map.values()].sort((a, b) => a.order - b.order);
}

// ---- 团队视图派生（多智能体专用）----
// 后端事件约定（dispatcher/internal/eino）：
//   协调者 node="coordinator:<id>" kind="model"；专家 node="agent:<名字>" kind="agent"
//   专家收尾 detail="简报 <简报> → <产出>"；MCP 工具 node="tool:<名字>" kind="tool"
// 与 deriveNodes 的区别：这里保留 start/end 时间戳，才能画并发时间轴。

export interface TeamSeat {
  node: string;
  name: string; // 去掉 agent:/tool: 前缀
  kind: "agent" | "tool"; // agent=专家(队友)，tool=工具
  status: NodeStatus;
  ms?: number;
  brief?: string; // 协调者给的定制简报（从 detail 解析）
  output?: string; // 产出预览
  startTS: number;
  endTS?: number;
}

export interface TeamLead {
  node: string;
  label: string;
  detail?: string;
  status: NodeStatus;
}

export interface TeamModel {
  lead: TeamLead | null;
  seats: TeamSeat[];
  t0: number; // 最早开始
  t1: number; // 最晚结束（未完则为 now）
}

// isMultiAgent 判定这次运行值不值得看「团队」：
//   1) 走了协调者(coordinator:)——orchestrator 自主派发；或
//   2) 图里挂了 ≥2 个专家(agent:)——用户自己在编排里并排/接力多个 agent，那也是个团队。
// 只有 1 个 agent 的普通任务不显示（一个人不叫团队，看轨迹就够）。
export function isMultiAgent(events: ExecEvent[]): boolean {
  if (events.some((e) => e.node.startsWith("coordinator:"))) return true;
  const agents = new Set(events.filter((e) => e.node.startsWith("agent:")).map((e) => e.node));
  return agents.size >= 2;
}

// splitBrief 解析专家收尾 detail："简报 X → Y" → {brief:X, output:Y}；不匹配则整段当产出。
function splitBrief(detail?: string): { brief?: string; output?: string } {
  if (!detail) return {};
  const m = detail.match(/^简报\s*([\s\S]*?)\s*→\s*([\s\S]*)$/);
  if (m) return { brief: m[1], output: m[2] };
  return { output: detail };
}

// isReportRun 判定一次运行是不是「报告生成」。
// 靠 task_id 前缀：后端 newReportID() 出 "report_<hex>"、普通任务是 "task_<hex>"，
// 而报告导出接口 /reports/:id/export 本来就按同一个 id 寻址——这个前缀是既有契约，不是我临时约的。
export function isReportRun(taskId: string | undefined): boolean {
  return !!taskId && taskId.startsWith("report_");
}

// teamNow 决定并发时间轴的右端。直播时是此刻——未收口的工位应持续生长；
// 复盘时必须是最后一个事件的时间戳：回放一条卡在 running 的历史任务（崩溃/超时留下的），
// 未收口工位若量到 Date.now()，时间轴会一路拉到今天，跨度直接爆表。
export function teamNow(events: ExecEvent[], live: boolean): number {
  if (live) return Date.now();
  let last = 0;
  for (const e of events) if (e.ts > last) last = e.ts;
  return last || Date.now();
}

// deriveTeam 把事件流派生成「协调者 + 工位」模型（保留时间戳供并发时间轴）。
export function deriveTeam(events: ExecEvent[], now = Date.now()): TeamModel {
  let lead: TeamLead | null = null;
  const map = new Map<string, TeamSeat>();
  for (const e of events) {
    if (e.node.startsWith("coordinator:")) {
      if (!lead) lead = { node: e.node, label: e.label || "多智能体协调", status: "running" };
      if (e.label) lead.label = e.label;
      if (e.detail) lead.detail = e.detail;
      if (e.phase === "end") lead.status = "done";
      else if (e.phase === "error") lead.status = "error";
      continue;
    }
    // 按节点名前缀挑工位，不按 kind：两条产生 agent 的路径 kind 并不一致
    //   —— 协调者派发的专家是 kind=agent，图里的 agent 节点是 kind=model。
    // 前缀才是后端一直遵守的约定（agent:<名字> / tool:<名字>），
    // 而且它天然把 retriever:/map:/render: 这些同为 kind=tool 的节点挡在工位之外。
    const isAgent = e.node.startsWith("agent:");
    const isTool = e.node.startsWith("tool:");
    if (!isAgent && !isTool) continue;
    let s = map.get(e.node);
    if (!s) {
      s = {
        node: e.node,
        name: e.node.replace(/^(agent|tool):/, ""),
        kind: isAgent ? "agent" : "tool",
        status: "running",
        startTS: e.ts,
      };
      map.set(e.node, s);
    }
    switch (e.phase) {
      case "start":
        s.startTS = e.ts;
        if (s.status !== "done" && s.status !== "error") s.status = "running";
        break;
      case "end": {
        s.status = "done";
        s.ms = e.ms;
        s.endTS = e.ts;
        const p = splitBrief(e.detail);
        s.brief = p.brief;
        s.output = p.output;
        break;
      }
      case "error":
        s.status = "error";
        s.ms = e.ms;
        s.endTS = e.ts;
        s.output = e.detail;
        break;
    }
  }
  const seats = [...map.values()];
  const starts = seats.map((s) => s.startTS);
  const ends = seats.map((s) => s.endTS ?? now);
  return {
    lead,
    seats,
    t0: starts.length ? Math.min(...starts) : now,
    t1: ends.length ? Math.max(...ends) : now,
  };
}
