import assert from "node:assert/strict";
import test from "node:test";
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";

import { DoubaoArkAdapter } from "../DoubaoArkAdapter.ts";
import { DoubaoHostAdapter } from "../../switchboard/hosts/DoubaoHostAdapter.ts";

test("DoubaoArkAdapter exposes a real RunHandle event lifecycle", async () => {
  const calls: Array<{ url: string; init?: RequestInit }> = [];
  const adapter = new DoubaoArkAdapter({
    apiKey: "test-only",
    baseUrl: "https://ark.example/api/v3",
    defaultModel: "ep-test",
    fetchImpl: async (input, init) => {
      calls.push({ url: String(input), init });
      return new Response(JSON.stringify({
        id: "resp-test",
        output: [{ type: "message", content: [{ type: "output_text", text: "CODEFLOWMU_DOUBAO_HOST_OK" }] }],
        usage: { total_tokens: 9 },
      }), { status: 200 });
    },
  });
  const created = await adapter.create({
    agentId: "DEV-01", role: "DEV", layer: "worker", runtime: "local",
  });
  const handle = await adapter.send({
    sessionId: "session-doubao-test",
    agentId: "DEV-01",
    text: "hello",
  }, created.sdk_agent_id);
  const events: string[] = [];
  handle.onEvent((event) => events.push(event.event_type));
  const settled = await handle.whenSettled();
  assert.equal(settled.status, "finished");
  assert.equal(calls[0]?.url, "https://ark.example/api/v3/responses");
  assert.ok(events.includes("sdk.assistant"));
  assert.ok(events.includes("runtime.session_ended"));
});

test("DoubaoHostAdapter declares governed tool capabilities", async () => {
  const host = new DoubaoHostAdapter(new DoubaoArkAdapter({
    apiKey: "test-only", defaultModel: "ep-test",
  }));
  const descriptor = await host.describe();
  assert.equal(descriptor.adapterType, "doubao-ark");
  assert.equal(descriptor.capabilities.level, "H2");
  assert.equal(descriptor.capabilities.workspaceBinding, true);
  assert.equal(descriptor.capabilities.resumableSession, false);
  assert.equal(descriptor.metadata?.mode, "shadow");
});

test("DoubaoArkAdapter accepts an existing host-neutral logical agent id", async () => {
  const adapter = new DoubaoArkAdapter({
    apiKey: "test-only",
    defaultModel: "ep-test",
    fetchImpl: async () => new Response(JSON.stringify({
      id: "resp-existing-agent",
      output: [{ type: "message", content: [{ type: "output_text", text: "ok" }] }],
    }), { status: 200 }),
  });
  const existingId = "agent-4acf5e85-3ae4-4b31-82b9-de43d49226ea";

  await adapter.resume(existingId);
  assert.deepEqual(await adapter.list(), [existingId]);

  const handle = await adapter.send({
    sessionId: "session-existing-agent",
    agentId: "PM-01",
    text: "hello",
    modelId: "auto-smart",
  }, existingId);
  assert.equal((await handle.whenSettled()).status, "finished");
});

test("DoubaoArkAdapter resolves CodeFlowMu model aliases through the Host default", async () => {
  const requests: Array<Record<string, unknown>> = [];
  const adapter = new DoubaoArkAdapter({
    apiKey: "test-only",
    defaultModel: "doubao-seed-2-0-pro-260215",
    fetchImpl: async (_input, init) => {
      requests.push(JSON.parse(String(init?.body)) as Record<string, unknown>);
      return new Response(JSON.stringify({
        output: [{ type: "message", content: [{ type: "output_text", text: "ok" }] }],
      }), { status: 200 });
    },
  });
  const created = await adapter.create({
    agentId: "PM-01", role: "PM", layer: "leader", runtime: "local",
  });
  for (const modelId of ["default", "auto-smart", "inherit"]) {
    const handle = await adapter.send({
      sessionId: `session-${modelId}`,
      agentId: "PM-01",
      text: "hello",
      modelId,
    }, created.sdk_agent_id);
    assert.equal((await handle.whenSettled()).status, "finished");
  }
  assert.deepEqual(
    requests.map((request) => request["model"]),
    Array(3).fill("doubao-seed-2-0-pro-260215"),
  );
});

test("DoubaoArkAdapter rejects malformed logical agent ids", async () => {
  const adapter = new DoubaoArkAdapter({ apiKey: "test-only", defaultModel: "ep-test" });
  await assert.rejects(() => adapter.resume("agent id with spaces"), /invalid Doubao logical agent id/);
});

test("DoubaoArkAdapter executes a governed workspace tool and returns its result", async () => {
  const root = await mkdtemp(path.join(os.tmpdir(), "doubao-h2-"));
  const requests: Array<Record<string, unknown>> = [];
  try {
    await writeFile(path.join(root, "hello.txt"), "GROUND_TRUTH\n", "utf8");
    const adapter = new DoubaoArkAdapter({
      apiKey: "test-only",
      baseUrl: "https://ark.example/api/v3",
      defaultModel: "ep-test",
      fetchImpl: async (_input, init) => {
        const request = JSON.parse(String(init?.body)) as Record<string, unknown>;
        requests.push(request);
        if (requests.length === 1) {
          return new Response(JSON.stringify({
            id: "resp-tool-1",
            output: [{
              type: "function_call",
              call_id: "call-read-1",
              name: "read_file",
              arguments: JSON.stringify({ path: "hello.txt" }),
            }],
          }), { status: 200 });
        }
        return new Response(JSON.stringify({
          id: "resp-tool-2",
          output: [{
            type: "message",
            content: [{ type: "output_text", text: "FILE_CONTENT_SEEN" }],
          }],
        }), { status: 200 });
      },
    });
    const created = await adapter.create({
      agentId: "PM-01", role: "PM", layer: "leader", runtime: "local",
    });
    const handle = await adapter.send({
      sessionId: "session-doubao-tool-test",
      agentId: "PM-01",
      text: "Read hello.txt",
      workspace: root,
    }, created.sdk_agent_id);
    const events: string[] = [];
    handle.onEvent((event) => events.push(event.event_type));
    const settled = await handle.whenSettled();

    assert.equal(settled.status, "finished");
    assert.equal(settled.tool_calls_count, 1);
    assert.equal(requests.length, 2);
    assert.ok(Array.isArray(requests[0]?.["tools"]));
    assert.equal(requests[1]?.["previous_response_id"], "resp-tool-1");
    assert.match(JSON.stringify(requests[1]?.["input"]), /GROUND_TRUTH/);
    assert.ok(events.includes("sdk.tool_call"));
    assert.ok(events.includes("sdk.tool_result"));
  } finally {
    await rm(root, { recursive: true, force: true });
  }
});

test("DoubaoArkAdapter safely executes Doubao legacy function markers", async () => {
  const root = await mkdtemp(path.join(os.tmpdir(), "doubao-h2-legacy-"));
  const requests: Array<Record<string, unknown>> = [];
  try {
    await writeFile(path.join(root, "truth.txt"), "LEGACY_GROUND_TRUTH\n", "utf8");
    const adapter = new DoubaoArkAdapter({
      apiKey: "test-only",
      defaultModel: "ep-test",
      fetchImpl: async (_input, init) => {
        const request = JSON.parse(String(init?.body)) as Record<string, unknown>;
        requests.push(request);
        if (requests.length === 1) {
          return new Response(JSON.stringify({
            id: "legacy-response-1",
            output: [{
              type: "message",
              content: [{
                type: "output_text",
                text: '<|FunctionCallBegin|>[{"name":"read_file","parameters":{"path":"truth.txt"}}]<|FunctionCallEnd|>',
              }],
            }],
          }), { status: 200 });
        }
        return new Response(JSON.stringify({
          id: "legacy-response-2",
          output: [{ type: "message", content: [{ type: "output_text", text: "LEGACY_TOOL_OK" }] }],
        }), { status: 200 });
      },
    });
    const created = await adapter.create({
      agentId: "PM-01", role: "PM", layer: "leader", runtime: "local",
    });
    const handle = await adapter.send({
      sessionId: "session-doubao-legacy-test",
      agentId: "PM-01",
      text: "Read truth.txt",
      workspace: root,
    }, created.sdk_agent_id);
    const settled = await handle.whenSettled();

    assert.equal(settled.status, "finished");
    assert.equal(settled.tool_calls_count, 1);
    assert.equal(requests[1]?.["previous_response_id"], "legacy-response-1");
    assert.match(String(requests[1]?.["input"]), /LEGACY_GROUND_TRUTH/);
  } finally {
    await rm(root, { recursive: true, force: true });
  }
});
