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

import { submitAgentRunChatTurn } from "./code-agent-agentrun-adapter.ts";
import { createCodeAgentTraceStore } from "./code-agent-trace-store.ts";

const sourceCommit = "0123456789abcdef0123456789abcdef01234567";

test("fresh gpt.pika turn returns running only after durable dispatch intent is confirmed", async () => {
  const traceId = "trc_gpt_pika_durable_admission";
  const calls = [];
  const traceStore = createCodeAgentTraceStore();
  const result = await submitAgentRunChatTurn({
    traceId,
    traceStore,
    params: {
      ownerUserId: "usr_gpt_pika",
      projectId: "pikasTech/HWLAB",
      conversationId: "cnv_gpt_pika_durable",
      sessionId: "ses_gpt_pika_durable",
      message: "verify durable admission",
      providerProfile: "gpt.pika"
    },
    options: {
      env: agentRunTestEnv(),
      accessController: {
        async codeAgentToolCapabilitiesForOwner() {
          return { tools: { hwpod: { allowed: true } } };
        },
        store: {
          async findActiveDefaultApiKeyForUser() {
            return { displaySecret: "hwl_live_must_not_be_persisted" };
          }
        }
      },
      fetchImpl: createAgentRunFetch(calls, {
        dispatchIntent: {
          id: "dispatch_gpt_pika_durable",
          state: "pending",
          runnerJobId: "rjob_gpt_pika_durable",
          attemptCount: 0,
          durable: true
        }
      })
    }
  });

  assert.equal(result.status, "running");
  assert.equal(result.agentRun.runId, "run_gpt_pika_durable");
  assert.equal(result.agentRun.commandId, "cmd_gpt_pika_durable");
  assert.equal(result.agentRun.dispatchIntentId, "dispatch_gpt_pika_durable");
  assert.equal(result.agentRun.dispatchIntentState, "pending");
  assert.equal(result.agentRun.runnerJobId, "rjob_gpt_pika_durable");
  assert.equal(result.agentRun.durableDispatch, true);
  assert.equal(result.agentRun.status, "dispatch-intent-pending");

  const runCall = calls.find((call) => call.path === "/api/v1/runs");
  assert.ok(runCall);
  assert.deepEqual(runCall.body.workspaceRef, { kind: "opaque", path: "." });
  const commandCall = calls.find((call) => call.path.endsWith("/commands"));
  assert.ok(commandCall);
  assert.equal(commandCall.body.idempotencyKey, traceId);
  assert.equal(commandCall.body.payload.traceId, traceId);
  assert.equal(commandCall.body.payload.sessionId, "ses_agentrun_gpt_pika_durable");
  assert.equal(commandCall.body.payload.hwlabSessionId, "ses_gpt_pika_durable");
  assert.equal(commandCall.body.payload.conversationId, "cnv_gpt_pika_durable");
  assert.equal(commandCall.body.dispatch.kind, "kubernetes-job");
  assert.equal(Object.hasOwn(commandCall.body.dispatch.input, "commandId"), false);
  assert.equal(commandCall.body.dispatch.input.idempotencyKey, traceId);
  assert.equal(Object.hasOwn(commandCall.body.dispatch.input, "namespace"), false);
  assert.equal(Object.hasOwn(commandCall.body.dispatch.input, "image"), false);
  assert.equal(Object.hasOwn(commandCall.body.dispatch.input, "serviceAccountName"), false);
  assert.equal(commandCall.body.dispatch.input.transientEnv.every((entry) => entry.sensitive === false), true);
  assert.equal(commandCall.body.dispatch.input.transientEnv.every((entry) => !entry.name.startsWith("AGENTRUN_") && !entry.name.startsWith("KUBERNETES_")), true);
  assert.equal(JSON.stringify(commandCall.body).includes("hwl_live_must_not_be_persisted"), false);
  assert.equal(calls.some((call) => call.path.endsWith("/runner-jobs")), false);
  assert.ok(traceStore.snapshot(traceId).events.some((event) => event.label === "agentrun:dispatch-intent:admitted"));
});

test("fresh turn rejects an unconfirmed dispatch intent instead of returning false running", async () => {
  const calls = [];
  await assert.rejects(
    submitAgentRunChatTurn({
      traceId: "trc_dispatch_intent_missing",
      traceStore: createCodeAgentTraceStore(),
      params: {
        sessionId: "ses_dispatch_intent_missing",
        conversationId: "cnv_dispatch_intent_missing",
        message: "must fail admission",
        providerProfile: "gpt.pika"
      },
      options: {
        env: agentRunTestEnv(),
        fetchImpl: createAgentRunFetch(calls, { dispatchIntent: null })
      }
    }),
    (error) => error?.code === "agentrun_dispatch_intent_unconfirmed" && error?.statusCode === 502
  );
  assert.equal(calls.some((call) => call.path.endsWith("/runner-jobs")), false);
});

test("fresh turn rejects a durable dispatch intent with missing contract fields", async () => {
  await assert.rejects(
    submitAgentRunChatTurn({
      traceId: "trc_dispatch_intent_attempt_missing",
      traceStore: createCodeAgentTraceStore(),
      params: {
        sessionId: "ses_dispatch_intent_attempt_missing",
        message: "must require explicit attempt count",
        providerProfile: "gpt.pika"
      },
      options: {
        env: agentRunTestEnv(),
        fetchImpl: createAgentRunFetch([], {
          dispatchIntent: {
            id: "dispatch_attempt_missing",
            state: "pending",
            runnerJobId: "rjob_attempt_missing",
            durable: true
          }
        })
      }
    }),
    (error) => error?.code === "agentrun_dispatch_intent_unconfirmed" && error?.statusCode === 502
  );
});

for (const terminal of [
  { state: "failed", code: "agentrun_dispatch_intent_failed", statusCode: 503 },
  { state: "cancelled", code: "agentrun_dispatch_intent_cancelled", statusCode: 409 }
]) {
  test(`fresh turn exposes ${terminal.state} durable dispatch replay instead of returning running`, async () => {
    await assert.rejects(
      submitAgentRunChatTurn({
        traceId: `trc_dispatch_intent_${terminal.state}`,
        traceStore: createCodeAgentTraceStore(),
        params: {
          sessionId: `ses_dispatch_intent_${terminal.state}`,
          message: `surface ${terminal.state} admission`,
          providerProfile: "gpt.pika"
        },
        options: {
          env: agentRunTestEnv(),
          fetchImpl: createAgentRunFetch([], {
            dispatchIntent: {
              id: `dispatch_terminal_${terminal.state}`,
              state: terminal.state,
              runnerJobId: `rjob_terminal_${terminal.state}`,
              attemptCount: 2,
              durable: true
            }
          })
        }
      }),
      (error) => error?.code === terminal.code && error?.statusCode === terminal.statusCode
    );
  });
}

function agentRunTestEnv() {
  return {
    AGENTRUN_MGR_URL: "http://127.0.0.1:65535",
    HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL: "1",
    HWLAB_CODE_AGENT_AGENTRUN_DISPATCH_RETRY_MAX: "0",
    HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "NC01",
    HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: sourceCommit,
    HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "gpt.pika",
    HWLAB_CODE_AGENT_AGENTRUN_RUNNER_NAMESPACE: "caller-controlled-namespace-must-be-ignored",
    HWLAB_CODE_AGENT_AGENTRUN_RUNNER_IMAGE: "caller-controlled-image-must-be-ignored",
    HWLAB_CODE_AGENT_AGENTRUN_RUNNER_SERVICE_ACCOUNT: "caller-controlled-service-account-must-be-ignored",
    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",
    UNIDESK_MAIN_SERVER_IP: "http://127.0.0.1:18081"
  };
}

function createAgentRunFetch(calls, { dispatchIntent }) {
  return async (url, init = {}) => {
    const parsed = new URL(url);
    const method = init.method ?? "GET";
    const body = init.body ? JSON.parse(String(init.body)) : null;
    calls.push({ method, path: parsed.pathname, body });
    const send = (data) => new Response(JSON.stringify({ ok: true, data }), {
      status: 200,
      headers: { "content-type": "application/json" }
    });
    if (method === "POST" && parsed.pathname === "/api/v1/sessions") {
      return send({ session: { id: "ses_agentrun_gpt_pika_durable", metadata: {} } });
    }
    if (method === "POST" && parsed.pathname === "/api/v1/runs") {
      return send({
        id: "run_gpt_pika_durable",
        status: "pending",
        sessionRef: body.sessionRef,
        backendProfile: body.backendProfile
      });
    }
    if (method === "POST" && parsed.pathname === "/api/v1/runs/run_gpt_pika_durable/commands") {
      return send({
        id: "cmd_gpt_pika_durable",
        runId: "run_gpt_pika_durable",
        state: "pending",
        type: "turn",
        seq: 1,
        dispatchIntent
      });
    }
    throw new Error(`unexpected AgentRun call ${method} ${parsed.pathname}`);
  };
}
