import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { readdir, readFile } from "node:fs/promises";
import { join } from "node:path";

import { InMemorySdkAdapter, CursorSdkAdapter } from "../../registry/AgentSdkAdapter.ts";
import {
  CursorHostAdapter,
  CURSOR_HOST_CAPABILITIES,
  mapCursorSdkMessageType,
  mapCursorRuntimeEventTypeToHostType,
  mapCursorRuntimeEventToHostEnvelope,
  classifyCursorHostFailure,
  mapCursorFailureCategoryToHostKind,
  AgentHostRegistry,
} from "../index.ts";

describe("WP-04 CursorHostAdapter", () => {
  it("maps Cursor SDK message types to the SdkRunHandle baseline", () => {
    assert.equal(mapCursorSdkMessageType("thinking"), "sdk.thinking");
    assert.equal(mapCursorSdkMessageType("assistant"), "sdk.assistant");
    assert.equal(mapCursorSdkMessageType("tool_call"), "sdk.tool_call");
    assert.equal(mapCursorSdkMessageType("result"), "sdk.result");
    assert.equal(mapCursorSdkMessageType("unknown-future"), "sdk.system");
    assert.equal(mapCursorRuntimeEventTypeToHostType("sdk.thinking"), "host.thinking");
    assert.equal(mapCursorRuntimeEventTypeToHostType("sdk.result"), "host.run.completed");
  });

  it("classifies Cursor failures onto HostFailure kinds", () => {
    const rate = classifyCursorHostFailure(new Error("429 rate limit exceeded"));
    assert.equal(rate.kind, "rate_limited");
    assert.equal(rate.retryable, true);

    const policy = classifyCursorHostFailure(
      new Error("codeflowmu_policy_blocked: no"),
    );
    assert.equal(policy.kind, "permission");

    assert.equal(mapCursorFailureCategoryToHostKind("transient_network"), "transient");
    assert.equal(
      mapCursorFailureCategoryToHostKind("cursor_sdk_error_no_detail"),
      "delivery_unknown",
    );
  });

  it("wraps AgentSdkAdapter with cursor adapterType and registers in AgentHostRegistry", async () => {
    const sdk = new InMemorySdkAdapter();
    const host = new CursorHostAdapter(sdk, { hostId: "host-cursor-test" });
    assert.equal(host.underlyingSdkAdapter, sdk);

    const d = await host.describe();
    assert.equal(d.adapterType, "cursor");
    assert.equal(d.capabilities.level, CURSOR_HOST_CAPABILITIES.level);
    assert.equal(d.metadata?.wraps, "CursorSdkAdapter");

    const prepared = await host.prepareSession({
      worker: { workerId: "DEV-01", roleId: "DEV", layer: "worker" },
    });
    assert.equal(prepared.created, true);
    assert.ok(prepared.reference.externalSessionId.startsWith("sdk-fake-"));

    const run = await host.startRun(prepared.reference, {
      runId: "run-c1",
      workerId: "DEV-01",
      prompt: "hi",
      deliveryAttemptId: "attempt-c1",
    });
    assert.ok(run.externalRunId);
    assert.equal(sdk.calls.send.length, 1);

    const registry = new AgentHostRegistry();
    registry.register(host);
    assert.equal(registry.has("host-cursor-test"), true);
  });

  it("maps RuntimeEvent through CursorHostEventMapper without dropping raw", () => {
    const envelope = mapCursorRuntimeEventToHostEnvelope({
      hostId: "host-cursor",
      workerId: "DEV-01",
      runId: "run-1",
      event: {
        event_id: "e1",
        at: "2026-08-04T12:00:00.000Z",
        event_type: "sdk.assistant",
        session_id: "session-1",
        agent_id: "DEV-01",
        payload: { text: "ok" },
      },
    });
    assert.equal(envelope.type, "host.assistant");
    assert.deepEqual(envelope.payload, { text: "ok" });
    assert.ok(envelope.raw);
  });

  it("keeps package-root CursorSdkAdapter import and contracts free of Cursor tokens", async () => {
    const mod = await import("../../index.ts");
    assert.equal(typeof mod.CursorSdkAdapter, "function");
    assert.equal(typeof mod.CursorHostAdapter, "function");
    assert.equal(CursorSdkAdapter, mod.CursorSdkAdapter);

    const contractsDir = join(
      process.cwd(),
      "src",
      "switchboard",
      "contracts",
    );
    const names = await readdir(contractsDir);
    for (const name of names) {
      if (!name.endsWith(".ts")) continue;
      const raw = await readFile(join(contractsDir, name), "utf-8");
      assert.equal(
        /\bCursor\b|\bsdk_agent_id\b/i.test(raw),
        false,
        `${name} must not introduce Cursor vendor tokens`,
      );
    }
  });
});
