import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { cp, mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { ToolRegistry, type ToolContext } from "../../src/agent/tools.js";
import { cphCheckTool, cphBuildTool } from "../../src/agent/cph.js";

const EXAMPLES_DIR = join(process.cwd(), "..", "examples", "TH-141");

describe("cph subprocess tools (integration, real cph binary)", () => {
  let ws: string;
  let tools: ToolRegistry;

  beforeEach(async () => {
    // Use a writable copy of the real TH-141 example as the workspace.
    ws = await mkdtemp(join(tmpdir(), "hub-cph-example-"));
    await cp(EXAMPLES_DIR, ws, { recursive: true });
    tools = new ToolRegistry();
    tools.register("cph_check", cphCheckTool);
    tools.register("cph_build", cphBuildTool);
  });

  afterEach(async () => {
    await rm(ws, { recursive: true, force: true });
  });

  it("cph_check runs on the TH-141 example and returns diagnostics", async () => {
    const ctx = { runId: "r", projectId: "p", boundChatId: "c", workspaceDir: ws };
    const out = await executeTool(tools, "cph_check", {}, ctx);
    const result = JSON.parse(out) as { exitCode: number; stdout: string; stderr: string };

    // cph check exits 0 on a legal lesson (no error diagnostics, ADR-0010).
    // If the example has warnings, exit is still 0.
    expect(result.exitCode).toBe(0);
  }, 30000);

  it("cph_build renders the student target PDF", async () => {
    const ctx = { runId: "r", projectId: "p", boundChatId: "c", workspaceDir: ws };
    const out = await executeTool(tools, "cph_build", { target: "student", output: "build/test-student.pdf" }, ctx);
    const result = JSON.parse(out) as { exitCode: number; stdout: string; stderr: string; output: string };

    expect(result.exitCode).toBe(0);
  }, 30000);

  it("cph_check on a temp dir with no engineering file returns non-zero", async () => {
    const empty = await mkdtemp(join(tmpdir(), "hub-cph-empty-"));
    try {
      const ctx = { runId: "r", projectId: "p", boundChatId: "c", workspaceDir: empty };
      const out = await executeTool(tools, "cph_check", {}, ctx);
      const result = JSON.parse(out) as { exitCode: number };
      expect(result.exitCode).not.toBe(0);
    } finally {
      await rm(empty, { recursive: true, force: true });
    }
  }, 15000);
});

async function executeTool(tools: ToolRegistry, name: string, args: unknown, ctx: ToolContext): Promise<string> {
  const def = tools.build(ctx)[name];
  if (def?.execute === undefined) {
    throw new Error(`missing executable tool: ${name}`);
  }
  return def.execute(args, { toolCallId: "call", messages: [], context: undefined }) as Promise<string>;
}
