import assert from "node:assert/strict";
import { test } from "bun:test";

import { fetchTraceSnapshot, mergeTraceResults, replayFullTrace, subscribeToTrace, type TraceSnapshot } from "./runner-trace";
import type { AgentChatResultResponse, TraceEvent } from "../types/domain";

test("mergeTraceResults derives final response from trace assistant event when trace terminal arrives first", () => {
  const terminal: AgentChatResultResponse = {
    status: "completed",
    traceId: "trc_fixture",
    message: "AgentRun result is ready for HWLAB short-connection polling."
  };
  const events: TraceEvent[] = [
    { seq: 1, label: "agentrun:request:accepted", status: "accepted" },
    { seq: 22, label: "agentrun:assistant:message", type: "assistant", status: "running", message: "你好，真实回复" },
    { seq: 31, label: "agentrun:result:completed", type: "result", status: "completed", message: "AgentRun result is ready for HWLAB short-connection polling." }
  ];
  const trace: TraceSnapshot = { traceId: "trc_fixture", status: "completed", events, eventCount: events.length, lastEventLabel: "agentrun:result:completed" };

  const merged = mergeTraceResults(terminal, trace);

  assert.equal(merged.assistantText, "你好，真实回复");
  assert.equal(merged.eventCount, 3);
  assert.equal(merged.runnerTrace?.traceId, "trc_fixture");
  assert.equal(merged.runnerTrace?.events?.length, 3);
  assert.equal(merged.traceEvents?.length, 3);
});

test("mergeTraceResults keeps trace assistant markdown ahead of flattened terminal text", () => {
  const terminal: AgentChatResultResponse = {
    status: "completed",
    traceId: "trc_markdown",
    assistantText: "当前 HWPOD node-ops 链路可用： | hwpodId | node | |---|---| | `hwpod-f103` | pc-host-1 | - API: `http://hwlab-cloud-api.hwlab-v02.svc.cluster.local:6667`"
  };
  const markdown = "当前 HWPOD node-ops 链路可用：\n\n| hwpodId | node |\n|---|---|\n| `hwpod-f103` | pc-host-1 |\n\n- API: `http://hwlab-cloud-api.hwlab-v02.svc.cluster.local:6667`";
  const trace: TraceSnapshot = {
    traceId: "trc_markdown",
    status: "completed",
    events: [
      { seq: 10, label: "agentrun:assistant:message", type: "assistant", status: "completed", final: true, replyAuthority: true, message: markdown }
    ],
    eventCount: 1
  };

  const merged = mergeTraceResults(terminal, trace);

  assert.equal(merged.assistantText, markdown);
  assert.match(merged.assistantText ?? "", /\n\n\| hwpodId \| node \|/u);
});

test("mergeTraceResults uses final assistant authority instead of later progress snapshots", () => {
  const finalMarkdown = "以下是 hwpod-spec 的机制说明：\n\n## 调用链\n\n1. `hwpod-cli` 读取 `.hwlab/hwpod-spec.yaml`。\n2. `hwpod-compiler-cli` 编译 intent。\n\n```bash\nhwpod inspect --dry-run\n```";
  const flattenedProgress = "让我先重新加载当前工作区的上下文。再快速看看源码的实现：以下是对你问题的回答： --- ## hwpod-spec 机制说明 ### 整体架构 hwpod-cli / hwpod-ctl -> hwpod-compiler-cli";
  const terminal: AgentChatResultResponse = {
    status: "completed",
    traceId: "trc_issue959_natural",
    assistantText: flattenedProgress
  };
  const events: TraceEvent[] = [
    { seq: 10, label: "agentrun:assistant:message", type: "assistant", status: "running", message: "让我先重新加载当前工作区的上下文。" },
    { seq: 20, label: "agentrun:assistant:message", type: "assistant", status: "running", message: "再快速看看源码的实现：" },
    { seq: 30, label: "agentrun:assistant:message", type: "assistant", status: "completed", final: true, replyAuthority: true, terminal: true, message: finalMarkdown },
    { seq: 31, label: "agentrun:assistant:message", type: "assistant", status: "running", message: flattenedProgress },
    { seq: 32, label: "agentrun:result:completed", type: "result", status: "completed", message: "AgentRun result is ready for HWLAB short-connection polling." }
  ];
  const trace: TraceSnapshot = { traceId: "trc_issue959_natural", status: "completed", events, eventCount: events.length, lastEventLabel: "agentrun:result:completed" };

  const merged = mergeTraceResults(terminal, trace);

  assert.equal(merged.assistantText, finalMarkdown);
  assert.match(merged.assistantText ?? "", /\n\n## 调用链\n\n1\. `hwpod-cli`/u);
  assert.doesNotMatch(merged.assistantText ?? "", / --- ## hwpod-spec 机制说明 ###/u);
});

test("mergeTraceResults prefers terminal result markdown over non-final flattened trace snapshots", () => {
  const resultMarkdown = "ISSUE959_MARKDOWN_OK\n\n## hwpod-spec 机制说明\n\n- 第一条\n- 第二条\n\n```bash\nhwpod inspect --dry-run\n```";
  const flattenedTrace = "I'll read context first.ISSUE959_MARKDOWN_OK ## hwpod-spec 机制说明 - 第一条 - 第二条 ```bash hwpod inspect --dry-run ```";
  const terminal: AgentChatResultResponse = {
    status: "completed",
    traceId: "trc_issue959_real_payload",
    reply: { role: "assistant", content: resultMarkdown }
  };
  const events: TraceEvent[] = [
    { seq: 31, label: "agentrun:assistant:message", type: "assistant", status: "running", message: flattenedTrace },
    { seq: 36, label: "agentrun:assistant:message", type: "assistant", status: "running", message: flattenedTrace },
    { seq: 42, label: "agentrun:result:completed", type: "result", status: "completed", terminal: true, message: "AgentRun result is ready for HWLAB short-connection polling." }
  ];
  const trace: TraceSnapshot = { traceId: "trc_issue959_real_payload", status: "completed", events, eventCount: events.length, lastEventLabel: "agentrun:result:completed" };

  const merged = mergeTraceResults(terminal, trace);

  assert.equal(merged.assistantText, resultMarkdown);
  assert.match(merged.assistantText ?? "", /ISSUE959_MARKDOWN_OK\n\n## hwpod-spec/u);
  assert.doesNotMatch(merged.assistantText ?? "", /^I'll read context first/u);
});

test("mergeTraceResults prefers structured result markdown over flattened trace finalResponse", () => {
  const resultMarkdown = "ISSUE959_MARKDOWN_OK\n\n## hwpod-spec 机制说明\n\n- 第一条\n- 第二条\n\n```bash\nhwpod-cli run --spec .hwlab/hwpod-spec.yaml\n```";
  const flattenedFinalResponse = "Let me read context first.ISSUE959_MARKDOWN_OK ## hwpod-spec 机制说明 - 第一条 - 第二条 ```bash hwpod-cli run --spec .hwlab/hwpod-spec.yaml ```";
  const terminal: AgentChatResultResponse = {
    status: "completed",
    traceId: "trc_issue959_finalresponse_flattened",
    assistantText: flattenedFinalResponse,
    reply: { role: "assistant", content: resultMarkdown }
  };
  const events: TraceEvent[] = [
    { seq: 31, label: "agentrun:assistant:message", type: "assistant", status: "running", message: flattenedFinalResponse },
    { seq: 36, label: "agentrun:assistant:message", type: "assistant", status: "running", message: flattenedFinalResponse },
    { seq: 42, label: "agentrun:result:completed", type: "result", status: "completed", terminal: true, message: "AgentRun result is ready for HWLAB short-connection polling." }
  ];
  const trace: TraceSnapshot = {
    traceId: "trc_issue959_finalresponse_flattened",
    status: "completed",
    events,
    eventCount: events.length,
    finalResponse: { text: flattenedFinalResponse },
    lastEventLabel: "agentrun:result:completed"
  };

  const merged = mergeTraceResults(terminal, trace);

  assert.equal(merged.assistantText, resultMarkdown);
  assert.match(merged.assistantText ?? "", /ISSUE959_MARKDOWN_OK\n\n## hwpod-spec/u);
  assert.doesNotMatch(merged.assistantText ?? "", /^Let me read context first/u);
});

test("mergeTraceResults prefers persisted finalResponse text for expired or compact natural traces", () => {
  const finalMarkdown = "**结论**\n\n- 自然结束和刷新后都应该显示这一段。\n- Markdown 换行必须保留。";
  const terminal: AgentChatResultResponse = {
    status: "completed",
    traceId: "trc_issue959_terminal",
    assistantText: "自然运行时的压平文本： **结论** - 自然结束和刷新后都应该显示这一段。"
  };
  const trace: TraceSnapshot = {
    traceId: "trc_issue959_terminal",
    status: "completed",
    events: [],
    eventCount: 63,
    finalResponse: { text: finalMarkdown },
    traceSummary: { sourceEventCount: 63 }
  };

  const merged = mergeTraceResults(terminal, trace);

  assert.equal(merged.assistantText, finalMarkdown);
  assert.match(merged.assistantText ?? "", /\*\*结论\*\*\n\n- 自然结束/u);
});

test("mergeTraceResults keeps full polled trace events when terminal runnerTrace is compact", () => {
  const terminal: AgentChatResultResponse = {
    status: "completed",
    traceId: "trc_compact_terminal",
    assistantText: "最终回复",
    runnerTrace: {
      traceId: "trc_compact_terminal",
      status: "completed",
      eventCount: 97,
      eventsCompacted: true,
      fullTraceLoaded: false
    }
  };
  const events: TraceEvent[] = [
    { seq: 1, label: "agentrun:backend:turn-started", status: "running" },
    { seq: 42, label: "agentrun:assistant:message", type: "assistant", status: "running", message: "中间回复" },
    { seq: 97, label: "agentrun:result:completed", type: "result", status: "completed" }
  ];
  const trace: TraceSnapshot = { traceId: "trc_compact_terminal", status: "completed", events, eventCount: 97, eventsCompacted: false, lastEventLabel: "agentrun:result:completed" };

  const merged = mergeTraceResults(terminal, trace);

  assert.equal(merged.runnerTrace?.events?.length, 3);
  assert.equal(merged.runnerTrace?.eventCount, 97);
  assert.equal(merged.runnerTrace?.eventsCompacted, false);
  assert.equal(merged.runnerTrace?.fullTraceLoaded, true);
  assert.equal(merged.traceEvents?.length, 3);
});

test("subscribeToTrace keeps polling result when trace terminal lacks final response text", async () => {
  const originalFetch = globalThis.fetch;
  const originalSetTimeout = globalThis.window?.setTimeout;
  const originalClearTimeout = globalThis.window?.clearTimeout;
  const fetches: string[] = [];
  const events: TraceEvent[] = [
    { seq: 1, label: "agentrun:request:accepted", status: "accepted" },
    { seq: 2, label: "agentrun:terminal:completed", type: "terminal", status: "completed", message: "AgentRun terminal status completed" }
  ];
  let resultPolls = 0;
  globalThis.window = {
    ...(globalThis.window ?? {}),
    setTimeout: ((callback: () => void) => { callback(); return 1; }) as typeof window.setTimeout,
    clearTimeout: (() => undefined) as typeof window.clearTimeout
  } as typeof window;
  globalThis.fetch = (async (input: RequestInfo | URL) => {
    const url = String(input);
    fetches.push(url);
    if (url.includes("/result/")) {
      resultPolls += 1;
      return jsonResponse(resultPolls === 1
        ? { status: "running", traceId: "trc_terminal_first" }
        : { status: "completed", traceId: "trc_terminal_first", assistantText: "最终回复" });
    }
    if (url.includes("/trace/")) {
      return jsonResponse({ status: "completed", traceId: "trc_terminal_first", events, eventCount: events.length });
    }
    return jsonResponse({});
  }) as typeof fetch;

  try {
    const completed = await new Promise<AgentChatResultResponse>((resolve, reject) => {
      void subscribeToTrace({
      traceId: "trc_terminal_first",
      initial: { status: "running", traceId: "trc_terminal_first", resultUrl: "/v1/agent/chat/result/trc_terminal_first" },
      onActivity: () => undefined,
      onSnapshot: () => undefined,
      onComplete: resolve,
      onInfrastructureError: (error) => reject(new Error(error)),
      signal: new AbortController().signal,
      inactivityTimeoutMs: 1000
      }).catch(reject);
    });
    assert.equal(completed.assistantText, "最终回复");
    assert.equal(completed.runnerTrace?.events?.length, 2);
    assert.equal(resultPolls, 2);
    assert.deepEqual(fetches, [
      "/v1/agent/chat/result/trc_terminal_first",
      "/v1/agent/chat/trace/trc_terminal_first?projectId=prj_hwpod_workbench",
      "/v1/agent/chat/result/trc_terminal_first",
      "/v1/agent/chat/trace/trc_terminal_first?projectId=prj_hwpod_workbench"
    ]);
  } finally {
    globalThis.fetch = originalFetch;
    if (globalThis.window) {
      globalThis.window.setTimeout = originalSetTimeout ?? globalThis.window.setTimeout;
      globalThis.window.clearTimeout = originalClearTimeout ?? globalThis.window.clearTimeout;
    }
  }
});

test("subscribeToTrace preserves trace when terminal result with text arrives before trace poll", async () => {
  const originalFetch = globalThis.fetch;
  const originalSetTimeout = globalThis.window?.setTimeout;
  const originalClearTimeout = globalThis.window?.clearTimeout;
  const fetches: string[] = [];
  const events: TraceEvent[] = [
    { seq: 1, label: "agentrun:request:accepted", status: "accepted" },
    { seq: 2, label: "agentrun:terminal:failed", type: "terminal", status: "failed", message: "provider failed" },
    { seq: 3, label: "agentrun:result:failed", type: "result", status: "failed", message: "provider failed" }
  ];
  globalThis.window = {
    ...(globalThis.window ?? {}),
    setTimeout: ((callback: () => void) => { callback(); return 1; }) as typeof window.setTimeout,
    clearTimeout: (() => undefined) as typeof window.clearTimeout
  } as typeof window;
  globalThis.fetch = (async (input: RequestInfo | URL) => {
    const url = String(input);
    fetches.push(url);
    if (url.includes("/result/")) {
      return jsonResponse({ status: "failed", traceId: "trc_failed_fast", assistantText: "provider failed" });
    }
    if (url.includes("/trace/")) {
      return jsonResponse({ status: "failed", traceId: "trc_failed_fast", events, eventCount: events.length });
    }
    return jsonResponse({});
  }) as typeof fetch;

  try {
    const completed = await new Promise<AgentChatResultResponse>((resolve, reject) => {
      void subscribeToTrace({
        traceId: "trc_failed_fast",
        initial: { status: "running", traceId: "trc_failed_fast", resultUrl: "/v1/agent/chat/result/trc_failed_fast" },
        onActivity: () => undefined,
        onSnapshot: () => undefined,
        onComplete: resolve,
        onInfrastructureError: (error) => reject(new Error(error)),
        signal: new AbortController().signal,
        inactivityTimeoutMs: 1000
      }).catch(reject);
    });
    assert.equal(completed.assistantText, "provider failed");
    assert.equal(completed.runnerTrace?.events?.length, 3);
    assert.equal(completed.runnerTrace?.eventCount, 3);
    assert.equal(completed.traceEvents?.length, 3);
    assert.deepEqual(fetches, [
      "/v1/agent/chat/result/trc_failed_fast",
      "/v1/agent/chat/trace/trc_failed_fast?projectId=prj_hwpod_workbench"
    ]);
  } finally {
    globalThis.fetch = originalFetch;
    if (globalThis.window) {
      globalThis.window.setTimeout = originalSetTimeout ?? globalThis.window.setTimeout;
      globalThis.window.clearTimeout = originalClearTimeout ?? globalThis.window.clearTimeout;
    }
  }
});

test("subscribeToTrace preserves trace when initial response is already terminal", async () => {
  const originalFetch = globalThis.fetch;
  const events: TraceEvent[] = [
    { seq: 1, label: "agentrun:request:accepted", status: "accepted" },
    { seq: 2, label: "agentrun:result:failed", type: "result", status: "failed", message: "provider failed" }
  ];
  const fetches: string[] = [];
  globalThis.fetch = (async (input: RequestInfo | URL) => {
    const url = String(input);
    fetches.push(url);
    if (url.includes("/trace/")) {
      return jsonResponse({ status: "failed", traceId: "trc_initial_terminal", events, eventCount: events.length });
    }
    return jsonResponse({});
  }) as typeof fetch;

  try {
    const snapshots: TraceSnapshot[] = [];
    const completed = await new Promise<AgentChatResultResponse>((resolve, reject) => {
      void subscribeToTrace({
        traceId: "trc_initial_terminal",
        initial: { status: "failed", traceId: "trc_initial_terminal", assistantText: "provider failed" },
        onActivity: () => undefined,
        onSnapshot: (snapshot) => snapshots.push(snapshot),
        onComplete: resolve,
        onInfrastructureError: (error) => reject(new Error(error)),
        signal: new AbortController().signal,
        inactivityTimeoutMs: 1000
      }).catch(reject);
    });
    assert.equal(completed.assistantText, "provider failed");
    assert.equal(completed.runnerTrace?.events?.length, 2);
    assert.equal(completed.runnerTrace?.eventCount, 2);
    assert.equal(completed.traceEvents?.length, 2);
    assert.equal(snapshots.length, 1);
    assert.deepEqual(fetches, ["/v1/agent/chat/trace/trc_initial_terminal?projectId=prj_hwpod_workbench"]);
  } finally {
    globalThis.fetch = originalFetch;
  }
});

test("subscribeToTrace merges terminal result without text with trace assistant content", async () => {
  const originalFetch = globalThis.fetch;
  const originalSetTimeout = globalThis.window?.setTimeout;
  const originalClearTimeout = globalThis.window?.clearTimeout;
  const fetches: string[] = [];
  const events: TraceEvent[] = [
    { seq: 1, label: "agentrun:request:accepted", status: "accepted" },
    { seq: 2, label: "agentrun:assistant:message", type: "assistant", status: "running", message: "trace 最终回复" },
    { seq: 3, label: "agentrun:result:completed", type: "result", status: "completed", message: "AgentRun result is ready for HWLAB short-connection polling." }
  ];
  globalThis.window = {
    ...(globalThis.window ?? {}),
    setTimeout: ((callback: () => void) => { callback(); return 1; }) as typeof window.setTimeout,
    clearTimeout: (() => undefined) as typeof window.clearTimeout
  } as typeof window;
  globalThis.fetch = (async (input: RequestInfo | URL) => {
    const url = String(input);
    fetches.push(url);
    if (url.includes("/result/")) {
      return jsonResponse({ status: "completed", traceId: "trc_result_first", message: "AgentRun result is ready for HWLAB short-connection polling." });
    }
    if (url.includes("/trace/")) {
      return jsonResponse({ status: "completed", traceId: "trc_result_first", events, eventCount: events.length });
    }
    return jsonResponse({});
  }) as typeof fetch;

  try {
    const completed = await new Promise<AgentChatResultResponse>((resolve, reject) => {
      void subscribeToTrace({
        traceId: "trc_result_first",
        initial: { status: "running", traceId: "trc_result_first", resultUrl: "/v1/agent/chat/result/trc_result_first" },
        onActivity: () => undefined,
        onSnapshot: () => undefined,
        onComplete: resolve,
        onInfrastructureError: (error) => reject(new Error(error)),
        signal: new AbortController().signal,
        inactivityTimeoutMs: 1000
      }).catch(reject);
    });
    assert.equal(completed.assistantText, "trace 最终回复");
    assert.equal(completed.runnerTrace?.events?.length, 3);
    assert.deepEqual(fetches, [
      "/v1/agent/chat/result/trc_result_first",
      "/v1/agent/chat/trace/trc_result_first?projectId=prj_hwpod_workbench"
    ]);
  } finally {
    globalThis.fetch = originalFetch;
    if (globalThis.window) {
      globalThis.window.setTimeout = originalSetTimeout ?? globalThis.window.setTimeout;
      globalThis.window.clearTimeout = originalClearTimeout ?? globalThis.window.clearTimeout;
    }
  }
});

test("subscribeToTrace starts result and trace polls in the same refresh tick (#1000)", async () => {
  const originalFetch = globalThis.fetch;
  const originalSetTimeout = globalThis.window?.setTimeout;
  const originalClearTimeout = globalThis.window?.clearTimeout;
  const fetches: string[] = [];
  let traceStartedBeforeResultResolved = false;
  let releaseResultGate: () => void = () => undefined;
  const resultGate = new Promise<void>((resolve) => { releaseResultGate = () => resolve(); });
  globalThis.window = {
    ...(globalThis.window ?? {}),
    setTimeout: ((callback: () => void) => { callback(); return 1; }) as typeof window.setTimeout,
    clearTimeout: (() => undefined) as typeof window.clearTimeout
  } as typeof window;
  globalThis.fetch = (async (input: RequestInfo | URL) => {
    const url = String(input);
    fetches.push(url);
    if (url.includes("/result/")) {
      await resultGate;
      return jsonResponse({ status: "completed", traceId: "trc_issue1000_parallel", assistantText: "最终回复" });
    }
    if (url.includes("/trace/")) {
      traceStartedBeforeResultResolved = true;
      releaseResultGate();
      return jsonResponse({
        status: "running",
        traceId: "trc_issue1000_parallel",
        events: [{ seq: 1, label: "agentrun:assistant:message", type: "assistant", status: "running", message: "trace 已更新" }],
        eventCount: 1
      });
    }
    return jsonResponse({});
  }) as typeof fetch;

  try {
    const snapshots: TraceSnapshot[] = [];
    const completed = await new Promise<AgentChatResultResponse>((resolve, reject) => {
      void subscribeToTrace({
        traceId: "trc_issue1000_parallel",
        initial: { status: "running", traceId: "trc_issue1000_parallel", resultUrl: "/v1/agent/chat/result/trc_issue1000_parallel" },
        onActivity: () => undefined,
        onSnapshot: (snapshot) => snapshots.push(snapshot),
        onComplete: resolve,
        onInfrastructureError: (error) => reject(new Error(error)),
        signal: new AbortController().signal,
        inactivityTimeoutMs: 1000
      }).catch(reject);
    });
    assert.equal(traceStartedBeforeResultResolved, true);
    assert.equal(snapshots.length, 1);
    const firstSnapshot = snapshots[0];
    assert.ok(firstSnapshot);
    assert.equal(firstSnapshot.eventCount, 1);
    assert.equal(completed.assistantText, "最终回复");
    assert.deepEqual(fetches, [
      "/v1/agent/chat/result/trc_issue1000_parallel",
      "/v1/agent/chat/trace/trc_issue1000_parallel?projectId=prj_hwpod_workbench"
    ]);
  } finally {
    releaseResultGate();
    globalThis.fetch = originalFetch;
    if (globalThis.window) {
      globalThis.window.setTimeout = originalSetTimeout ?? globalThis.window.setTimeout;
      globalThis.window.clearTimeout = originalClearTimeout ?? globalThis.window.clearTimeout;
    }
  }
});

test("replayFullTrace retries transient trace API failures before returning an empty replay", async () => {
  const originalFetch = globalThis.fetch;
  const originalSetTimeout = globalThis.window?.setTimeout;
  const originalClearTimeout = globalThis.window?.clearTimeout;
  const fetches: string[] = [];
  globalThis.window = {
    ...(globalThis.window ?? {}),
    setTimeout: ((callback: () => void) => { callback(); return 1; }) as typeof window.setTimeout,
    clearTimeout: (() => undefined) as typeof window.clearTimeout
  } as typeof window;
  globalThis.fetch = (async (input: RequestInfo | URL) => {
    const url = String(input);
    fetches.push(url);
    if (fetches.length === 1) {
      return new Response(JSON.stringify({ error: "trace store warming" }), { status: 500, headers: { "content-type": "application/json" } });
    }
    return jsonResponse({
      status: "completed",
      traceId: "trc_replay_retry",
      events: [
        { seq: 1, label: "agentrun:request:accepted", status: "accepted" },
        { seq: 2, label: "agentrun:assistant:message", type: "assistant", status: "completed", message: "恢复后的 trace" }
      ],
      eventCount: 2
    });
  }) as typeof fetch;

  try {
    const snapshot = await replayFullTrace("trc_replay_retry", 2000);
    assert.equal(snapshot?.events?.length, 2);
    assert.equal(snapshot?.eventCount, 2);
    assert.equal(snapshot?.lastEventLabel, "agentrun:assistant:message");
    assert.equal(fetches.length, 2);
  } finally {
    globalThis.fetch = originalFetch;
    if (globalThis.window) {
      globalThis.window.setTimeout = originalSetTimeout ?? globalThis.window.setTimeout;
      globalThis.window.clearTimeout = originalClearTimeout ?? globalThis.window.clearTimeout;
    }
  }
});

test("fetchTraceSnapshot retries until fast-fail trace events are readable", async () => {
  const originalFetch = globalThis.fetch;
  const originalSetTimeout = globalThis.window?.setTimeout;
  const originalClearTimeout = globalThis.window?.clearTimeout;
  const fetches: string[] = [];
  globalThis.window = {
    ...(globalThis.window ?? {}),
    setTimeout: ((callback: () => void) => { callback(); return 1; }) as typeof window.setTimeout,
    clearTimeout: (() => undefined) as typeof window.clearTimeout
  } as typeof window;
  globalThis.fetch = (async (input: RequestInfo | URL) => {
    const url = String(input);
    fetches.push(url);
    if (fetches.length === 1) {
      return jsonResponse({ status: "failed", traceId: "trc_fast_fail_retry", events: [], eventCount: 0 });
    }
    return jsonResponse({
      status: "failed",
      traceId: "trc_fast_fail_retry",
      events: [
        { seq: 1, label: "agentrun:request:accepted", status: "accepted" },
        { seq: 35, label: "agentrun:result:failed", type: "result", status: "failed", message: "provider failed" }
      ],
      eventCount: 35
    });
  }) as typeof fetch;

  try {
    const snapshot = await fetchTraceSnapshot("trc_fast_fail_retry", "trc_fast_fail_retry", 1000);
    assert.equal(snapshot?.status, "failed");
    assert.equal(snapshot?.events?.length, 2);
    assert.equal(snapshot?.eventCount, 35);
    assert.equal(snapshot?.lastEventLabel, "agentrun:result:failed");
    assert.deepEqual(fetches, [
      "/v1/agent/chat/trace/trc_fast_fail_retry?projectId=prj_hwpod_workbench",
      "/v1/agent/chat/trace/trc_fast_fail_retry?projectId=prj_hwpod_workbench"
    ]);
  } finally {
    globalThis.fetch = originalFetch;
    if (globalThis.window) {
      globalThis.window.setTimeout = originalSetTimeout ?? globalThis.window.setTimeout;
      globalThis.window.clearTimeout = originalClearTimeout ?? globalThis.window.clearTimeout;
    }
  }
});

test("fetchTraceSnapshot and replayFullTrace use the active workbench project id", async () => {
  const originalFetch = globalThis.fetch;
  const originalSetTimeout = globalThis.window?.setTimeout;
  const originalClearTimeout = globalThis.window?.clearTimeout;
  const fetches: string[] = [];
  globalThis.window = {
    ...(globalThis.window ?? {}),
    setTimeout: ((callback: () => void) => { callback(); return 1; }) as typeof window.setTimeout,
    clearTimeout: (() => undefined) as typeof window.clearTimeout
  } as typeof window;
  globalThis.fetch = (async (input: RequestInfo | URL) => {
    const url = String(input);
    fetches.push(url);
    return jsonResponse({
      status: "completed",
      traceId: url.includes("custom_replay") ? "trc_custom_replay" : "trc_custom_snapshot",
      events: [{ seq: 1, label: "agentrun:result:completed", status: "completed" }],
      eventCount: 1
    });
  }) as typeof fetch;

  try {
    await fetchTraceSnapshot("trc_custom_snapshot", "trc_custom_snapshot", 1000, undefined, "prj_v02_code_agent");
    await replayFullTrace("trc_custom_replay", 1000, "prj_v02_code_agent");
    assert.deepEqual(fetches, [
      "/v1/agent/chat/trace/trc_custom_snapshot?projectId=prj_v02_code_agent",
      "/v1/agent/chat/trace/trc_custom_replay?projectId=prj_v02_code_agent"
    ]);
  } finally {
    globalThis.fetch = originalFetch;
    if (globalThis.window) {
      globalThis.window.setTimeout = originalSetTimeout ?? globalThis.window.setTimeout;
      globalThis.window.clearTimeout = originalClearTimeout ?? globalThis.window.clearTimeout;
    }
  }
});

test("fetchTraceSnapshot accepts expired AgentRun terminal evidence without waiting for raw events", async () => {
  const originalFetch = globalThis.fetch;
  const fetches: string[] = [];
  globalThis.fetch = (async (input: RequestInfo | URL) => {
    const url = String(input);
    fetches.push(url);
    return jsonResponse({
      status: "completed",
      traceStatus: "expired",
      traceId: "trc_expired_terminal",
      events: [],
      eventCount: 0,
      terminalEvidence: { available: true, source: "agentrun-command-result" },
      traceSummary: { source: "agentrun-command-result", sourceEventCount: 31, terminalStatus: "completed" },
      finalResponse: { text: "过期 trace 的最终回复" }
    });
  }) as typeof fetch;

  try {
    const snapshot = await fetchTraceSnapshot("trc_expired_terminal", "trc_expired_terminal", 1000);
    assert.equal(snapshot?.status, "expired");
    assert.equal(snapshot?.eventCount, 31);
    assert.equal((snapshot?.terminalEvidence as { source?: string })?.source, "agentrun-command-result");
    assert.equal((snapshot?.finalResponse as { text?: string })?.text, "过期 trace 的最终回复");
    assert.equal(fetches.length, 1);
  } finally {
    globalThis.fetch = originalFetch;
  }
});

function jsonResponse(payload: unknown): Response {
  return new Response(JSON.stringify(payload), { status: 200, headers: { "content-type": "application/json" } });
}
