import { mkdtemp, readFile, rm } from "node:fs/promises";
import os from "node:os";
import path from "node:path";

import { afterEach, expect, test } from "bun:test";

import { createNativeAgentRunWorkbenchApplication } from "./native-agentrun-application.ts";

const temporaryDirectories: string[] = [];

afterEach(async () => {
  await Promise.all(temporaryDirectories.splice(0).map((directory) => rm(directory, { force: true, recursive: true })));
});

test("L1 AgentRun application dispatches gpt.pika without polling or projecting a terminal result", async () => {
  const directory = await mkdtemp(path.join(os.tmpdir(), "hwlab-workbench-agentrun-"));
  temporaryDirectories.push(directory);
  const stateFile = path.join(directory, "state.json");
  const calls: Array<{ method: string; path: string; authorization: string | null; body: any }> = [];
  const app = createNativeAgentRunWorkbenchApplication({
    stateFile,
    env: agentRunEnvironment(),
    fetchImpl: fakeAgentRunManager(calls),
    now: () => "2026-07-20T01:13:12.232Z",
  });

  const created = await app.createSession({ actor: { id: "usr_native", role: "user" }, params: { providerProfile: "gpt.pika" } });
  const session = created.session as Record<string, any>;
  const input = {
    actor: { id: "usr_native", role: "user" },
    traceId: "trc_l1_gpt_pika_smoke",
    sessionId: session.sessionId,
    params: { message: "hi", submittedAt: "2026-07-20T01:13:07.699Z", providerProfile: "gpt.pika" },
  };
  const admitted = await app.admitTurn(input);
  const result = await app.dispatchTurn(admitted);

  expect(result).toMatchObject({
    status: "running",
    terminal: false,
    terminalAuthority: "hwlab.event.v1",
    resultSynthesized: false,
    agentRun: {
      runId: "run_l1_gpt_pika",
      commandId: "cmd_l1_gpt_pika",
      runnerJobId: "rjob_l1_gpt_pika",
    },
  });
  const state = JSON.parse(await readFile(stateFile, "utf8"));
  expect(state.turns[input.traceId]).toMatchObject({
    status: "running",
    createdAt: "2026-07-20T01:13:07.699Z",
    terminal: false,
    runId: "run_l1_gpt_pika",
    commandId: "cmd_l1_gpt_pika",
    runnerJobId: "rjob_l1_gpt_pika",
  });
  expect(state.turns[input.traceId]).not.toHaveProperty("result");
  expect(state.turns[input.traceId]).not.toHaveProperty("finalResponse");
  expect(state.sessions[session.sessionId]).toMatchObject({ status: "running", providerProfile: "gpt.pika" });
  expect(state.sessions[session.sessionId].messages).toHaveLength(1);
  expect(calls.map((call) => `${call.method} ${call.path}`)).toEqual([
    "POST /api/v1/sessions",
    "POST /api/v1/runs",
    "POST /api/v1/runs/run_l1_gpt_pika/commands",
  ]);
  expect(calls.every((call) => call.authorization === "Bearer test-agentrun-key")).toBe(true);
});

test("L1 AgentRun application leaves dispatch failures to Temporal instead of projecting a terminal snapshot", async () => {
  const directory = await mkdtemp(path.join(os.tmpdir(), "hwlab-workbench-agentrun-failure-"));
  temporaryDirectories.push(directory);
  const stateFile = path.join(directory, "state.json");
  const app = createNativeAgentRunWorkbenchApplication({
    stateFile,
    env: agentRunEnvironment(),
    fetchImpl: (async () => Response.json({ ok: false, failureKind: "manager-unavailable", message: "manager unavailable" }, { status: 503 })) as typeof fetch,
  });
  const created = await app.createSession({ actor: { id: "usr_native" }, params: { providerProfile: "gpt.pika" } });
  const session = created.session as Record<string, any>;
  const input = await app.admitTurn({
    actor: { id: "usr_native" },
    traceId: "trc_l1_manager_failure",
    sessionId: session.sessionId,
    params: { message: "hi", providerProfile: "gpt.pika" },
  });

  await expect(app.dispatchTurn(input)).rejects.toMatchObject({ code: "manager-unavailable" });
  expect((await app.snapshot!()).turns[input.traceId]).toMatchObject({ status: "admitted", terminal: false });
});

test("L0 publishes admission, bounded ConnectionRefused retry, and semantic terminal failure to Kafka authority", async () => {
  const directory = await mkdtemp(path.join(os.tmpdir(), "hwlab-workbench-agentrun-visible-failure-"));
  temporaryDirectories.push(directory);
  const published: Array<Record<string, any>> = [];
  let runCreateAttempts = 0;
  const app = createNativeAgentRunWorkbenchApplication({
    stateFile: path.join(directory, "state.json"),
    env: agentRunEnvironment(),
    eventPublisher: {
      async publish(input) { published.push(input); return { published: true }; },
    },
    fetchImpl: (async (request) => {
      const url = new URL(String(request));
      if (url.pathname === "/api/v1/sessions") {
        return Response.json({ ok: true, data: { session: { id: "ses_agentrun_l0_visible_failure", metadata: {} } } });
      }
      runCreateAttempts += 1;
      throw Object.assign(new Error("Unable to connect. Is the computer able to access the url?"), { code: "ConnectionRefused" });
    }) as typeof fetch,
    now: () => "2026-07-20T11:45:01.000Z",
  });
  const created = await app.createSession({ actor: { id: "usr_native" }, params: { providerProfile: "gpt.pika" } });
  const session = created.session as Record<string, any>;
  const input = await app.admitTurn({
    actor: { id: "usr_native" },
    traceId: "trc_l0_connection_refused",
    sessionId: session.sessionId,
    params: { message: "hi", providerProfile: "gpt.pika" },
  });

  await expect(app.dispatchTurn(input)).rejects.toMatchObject({
    failureKind: "connection-refused",
    retryable: true,
    retryExhausted: true,
    retryAttempt: 1,
  });
  expect(runCreateAttempts).toBe(2);
  expect(published.map((item) => item.event.type)).toEqual([
    "backend_status",
    "backend_status",
    "error",
    "terminal_status",
  ]);
  expect(published[0]?.event).toMatchObject({ phase: "request-admitted", summary: "请求已接纳，正在启动 AgentRun" });
  expect(published[1]?.event).toMatchObject({
    retryPhase: "retryScheduled",
    failureDomain: "infrastructure",
    component: "agentrun-manager",
    code: "connection-refused",
    attempt: 1,
    maxAttempts: 1,
  });
  expect(published[2]?.event).toMatchObject({ retryPhase: "retryExhausted", summary: "AgentRun 服务暂时不可达" });
  expect(published[3]?.event).toMatchObject({ terminalStatus: "failed", failureKind: "connection-refused", terminal: true });
  expect(published.every((item) => item.traceId === input.traceId && item.sessionId === input.sessionId)).toBe(true);
});

function agentRunEnvironment(): Record<string, string> {
  return {
    WORKBENCH_MODE: "agentrun-native",
    AGENTRUN_MGR_URL: "http://127.0.0.1:65535",
    AGENTRUN_API_KEY: "test-agentrun-key",
    HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1",
    HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "NC01",
    HWLAB_CODE_AGENT_AGENTRUN_GPT_PIKA_SECRET_NAME: "provider-gpt-pika",
    HWLAB_CODE_AGENT_AGENTRUN_GITHUB_TOOL_SECRET_NAME: "agentrun-test-tool-github-pr",
    HWLAB_CODE_AGENT_AGENTRUN_UNIDESK_SSH_TOOL_SECRET_NAME: "agentrun-test-tool-unidesk-ssh",
    HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: "0123456789abcdef0123456789abcdef01234567",
    HWLAB_CODE_AGENT_AGENTRUN_REPO_URL: "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git",
    HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "gpt.pika",
    HWLAB_CODE_AGENT_AGENTRUN_RUNNER_NAMESPACE: "agentrun-v02",
    HWLAB_CODE_AGENT_AGENTRUN_DISPATCH_RETRY_MAX: "1",
    HWLAB_CODE_AGENT_AGENTRUN_DISPATCH_RETRY_BASE_MS: "1",
    HWLAB_CODE_AGENT_AGENTRUN_DISPATCH_RETRY_MAX_DELAY_MS: "1",
    HWLAB_RUNTIME_API_URL: "http://hwlab-cloud-api.hwlab-v03.svc.cluster.local:6667",
    HWLAB_RUNTIME_WEB_URL: "http://hwlab-cloud-web.hwlab-v03.svc.cluster.local:8080",
    HWLAB_RUNTIME_NAMESPACE: "hwlab-v03",
    HWLAB_RUNTIME_LANE: "v03",
    HWLAB_RUNTIME_ENDPOINT_LOCKED: "1",
    HWLAB_CODE_AGENT_ASSEMBLED_RUNTIME: "1",
  };
}

function fakeAgentRunManager(calls: Array<{ method: string; path: string; authorization: string | null; body: any }>): typeof fetch {
  return (async (input: string | URL | Request, init: RequestInit = {}) => {
    const url = new URL(String(input));
    const method = init.method ?? "GET";
    const body = init.body ? JSON.parse(String(init.body)) : null;
    calls.push({ method, path: url.pathname, authorization: new Headers(init.headers).get("authorization"), body });
    const send = (data: unknown) => Response.json({ ok: true, data });
    if (method === "POST" && url.pathname === "/api/v1/sessions") {
      return send({ session: { id: "ses_agentrun_l1_gpt_pika", metadata: {} } });
    }
    if (method === "POST" && url.pathname === "/api/v1/runs") {
      expect(body.backendProfile).toBe("gpt-pika");
      return send({ id: "run_l1_gpt_pika", status: "pending", sessionRef: body.sessionRef, backendProfile: body.backendProfile });
    }
    if (method === "POST" && url.pathname === "/api/v1/runs/run_l1_gpt_pika/commands") {
      expect(body.payload.prompt).toBe("hi");
      expect(body.payload.submittedAt).toBe("2026-07-20T01:13:07.699Z");
      return send({
        id: "cmd_l1_gpt_pika",
        runId: "run_l1_gpt_pika",
        state: "pending",
        type: "turn",
        seq: 1,
        dispatchIntent: { id: "dispatch_l1_gpt_pika", state: "pending", runnerJobId: "rjob_l1_gpt_pika", attemptCount: 0, durable: true },
      });
    }
    throw new Error(`unexpected AgentRun call ${method} ${url.pathname}`);
  }) as typeof fetch;
}
