import assert from "node:assert/strict";
import path from "node:path";
import { promises as fs } from "node:fs";
import { describe, it } from "node:test";
import { fileURLToPath } from "node:url";

import {
  normalizeHostCapabilities,
  toHostEventEnvelope,
  type AgentHostAdapter,
  type DoorbellMessage,
  type HostDescriptor,
  type HostRunRef,
  type RunRecord,
  type WorkerBindingRevision,
  type ContextProjectionManifest,
} from "../contracts/index.ts";

describe("WP-01 switchboard contracts", () => {
  it("shapes: HostDescriptor / HostRun / Doorbell / RunRecord / Binding / Projection", () => {
    const desc: HostDescriptor = {
      hostId: "host-a",
      adapterType: "in-memory",
      displayName: "Mem",
      enabled: true,
      capabilities: normalizeHostCapabilities({
        level: "H1",
        persistentSession: true,
        resumableSession: false,
        structuredEvents: true,
        cancellableRun: true,
        inspectableSession: false,
        workspaceBinding: true,
        concurrentWorkers: false,
      }),
    };
    assert.equal(desc.capabilities.level, "H1");

    const run: HostRunRef = {
      hostId: "host-a",
      externalRunId: "ext-1",
      session: { hostId: "host-a", externalSessionId: "sess-1" },
    };
    assert.equal(run.session.externalSessionId, "sess-1");

    const door: DoorbellMessage = {
      schema: "codeflowmu.doorbell/v1",
      doorbellId: "DB-1",
      recipientWorkerId: "worker-01",
      topic: "fcop.fact.available",
      sourceFactRefs: ["fcop/_lifecycle/active/TASK-1.md"],
      summary: "wake",
      priority: "P2",
      dedupeKey: "k1",
      createdAt: "2026-08-04T06:00:00.000Z",
    };
    assert.equal(door.schema, "codeflowmu.doorbell/v1");

    const binding: WorkerBindingRevision = {
      schema: "codeflowmu.worker-binding/v1",
      bindingId: "bind-1",
      revision: 1,
      workerId: "worker-01",
      host: { hostId: "host-a", externalSessionId: "sess-1" },
      state: "ready",
      createdAt: "2026-08-04T06:00:00.000Z",
      createdBy: "system",
    };
    assert.equal(binding.revision, 1);

    const record: RunRecord = {
      schema: "codeflowmu.run/v1",
      runId: "run-1",
      workerId: "worker-01",
      doorbellId: "DB-1",
      bindingId: "bind-1",
      hostId: "host-a",
      deliveryAttemptId: "attempt-1",
      state: "created",
      sourceFactRefs: [],
      continuationMode: "new_session_from_files",
    };
    assert.equal(record.deliveryAttemptId, "attempt-1");

    const proj: ContextProjectionManifest = {
      schema: "codeflowmu.context-projection/v1",
      projectionId: "proj-1",
      workerId: "worker-01",
      createdAt: "2026-08-04T06:00:00.000Z",
      entries: [{ kind: "task", path: "fcop/_lifecycle/active/TASK-1.md" }],
    };
    assert.equal(proj.entries.length, 1);
  });

  it("AgentHostAdapter type is structurally required (compile-time surface)", () => {
    const adapter: Pick<AgentHostAdapter, "hostId"> = { hostId: "h" };
    assert.equal(adapter.hostId, "h");
  });

  it("unknown host events are preserved as opaque envelopes", () => {
    const env = toHostEventEnvelope({
      eventId: "e1",
      at: "2026-08-04T06:00:00.000Z",
      hostId: "host-a",
      workerId: "w1",
      runId: "r1",
      payload: { vendorWeird: true },
    });
    assert.equal(env.type, "host.event.opaque");
    assert.deepEqual(env.raw, { vendorWeird: true });
  });

  it("unknown capability extensions do not fail normalization", () => {
    const caps = normalizeHostCapabilities({
      level: "H2",
      persistentSession: true,
      resumableSession: true,
      structuredEvents: true,
      cancellableRun: true,
      inspectableSession: true,
      workspaceBinding: true,
      concurrentWorkers: true,
      futureFlag: 42,
    } as Parameters<typeof normalizeHostCapabilities>[0]);
    assert.equal(caps.level, "H2");
    assert.equal(caps.extensions?.futureFlag, 42);
  });

  it("generic contract sources contain no vendor-specific tokens", async () => {
    const root = path.resolve(
      path.dirname(fileURLToPath(import.meta.url)),
      "../contracts",
    );
    const names = await fs.readdir(root);
    const forbidden = [
      /\bCursor\b/,
      /sdk_agent_id/,
      /@cursor\//,
      /\bClaude\b/,
      /\bCodex\b/,
      /\bPM\b/,
      /\bQA\b/,
    ];
    for (const name of names) {
      if (!name.endsWith(".ts")) continue;
      const text = await fs.readFile(path.join(root, name), "utf8");
      for (const re of forbidden) {
        assert.doesNotMatch(text, re, `${name} must not match ${re}`);
      }
    }
  });

  it("compatibility: legacy CursorSdkAdapter export remains importable from package root", async () => {
    const mod = await import("../../index.ts");
    assert.equal(typeof mod.CursorSdkAdapter, "function");
  });
});
