import assert from "node:assert/strict";
import { mkdtemp, mkdir, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { describe, it } from "node:test";
import { GoogleToolAuditLogger } from "../GoogleToolAuditLogger.ts";
import { routeGoogleToolCall } from "../GoogleToolCallRouter.ts";

describe("GoogleToolCallRouter", () => {
  it("rejects unauthorized tools via the exact role capability gate", async () => {
    const result = await routeGoogleToolCall({
      projectRoot: "/tmp",
      agentId: "DEV-01",
      toolName: "write_task",
      args: { recipient: "QA", body: "x" },
      callId: "c1",
      useFcopOneShot: false,
      auditLogger: new GoogleToolAuditLogger(),
    });
    assert.equal(result.ok, false);
    assert.equal(result.code, "ROLE_CAPABILITY_DENIED");
    assert.match(result.error ?? "", /canonical tool id/);
  });

  it("routes workspace read_file without MCP", async () => {
    const tmp = await mkdtemp(path.join(os.tmpdir(), "cfmu-router-"));
    await writeFile(path.join(tmp, "note.txt"), "router-ok\n", "utf8");
    const result = await routeGoogleToolCall({
      projectRoot: tmp,
      agentId: "DEV-01",
      toolName: "read_file",
      args: { path: "note.txt" },
      callId: "c2",
      useFcopOneShot: false,
      auditLogger: new GoogleToolAuditLogger(),
    });
    assert.equal(result.ok, true);
    assert.match(result.output ?? "", /router-ok/);
  });

  it("routes fcop tools through mcpClient when one-shot disabled", async () => {
    let called: { name: string; args: Record<string, unknown> } | null = null;
    const result = await routeGoogleToolCall({
      projectRoot: "/tmp",
      agentId: "DEV-01",
      toolName: "fcop_report",
      args: {},
      callId: "c3",
      useFcopOneShot: false,
      mcpClient: {
        callTool: async (name, args) => {
          called = { name, args };
          return { ok: true, summary: "report" };
        },
      },
      auditLogger: new GoogleToolAuditLogger(),
    });
    assert.equal(result.ok, true);
    const firstCall = called as unknown as { name: string } | null;
    assert.equal(firstCall?.name, "fcop_report");
  });

  it("PM fcop_check on routine patrol is soft-skipped without MCP invoke", async () => {
    let invoked = false;
    const patrolPrompt = [
      "[PM 轻量巡检/催促]",
      "fcop_report() 或 get_team_status（二选一）",
    ].join("\n");
    const result = await routeGoogleToolCall({
      projectRoot: "/tmp",
      agentId: "PM-01",
      toolName: "fcop_check",
      args: {},
      callId: "c-pm-skip",
      useFcopOneShot: false,
      promptRoutingText: patrolPrompt,
      mcpClient: {
        callTool: async () => {
          invoked = true;
          return { ok: true };
        },
      },
      auditLogger: new GoogleToolAuditLogger(),
    });
    assert.equal(result.ok, true);
    assert.equal(invoked, false);
    const parsed = JSON.parse(result.output ?? "{}") as { skipped?: boolean };
    assert.equal(parsed.skipped, true);
  });

  it("PM list_reports gets limit cap and task scope before MCP invoke", async () => {
    let called: { name: string; args: Record<string, unknown> } | null = null;
    const result = await routeGoogleToolCall({
      projectRoot: "/tmp",
      agentId: "PM-01",
      toolName: "list_reports",
      args: { limit: 500 },
      callId: "c-pm-list",
      taskId: "TASK-20260612-010-ADMIN-to-PM",
      useFcopOneShot: false,
      mcpClient: {
        callTool: async (name, args) => {
          called = { name, args };
          return { ok: true, reports: [] };
        },
      },
      auditLogger: new GoogleToolAuditLogger(),
    });
    assert.equal(result.ok, true);
    const listCall = called as unknown as {
      name: string;
      args: Record<string, unknown>;
    } | null;
    assert.equal(listCall?.name, "list_reports");
    assert.equal(listCall?.args.limit, 20);
    assert.equal(listCall?.args.task_id, "TASK-20260612-010-ADMIN-to-PM");
  });

  it("returns TOOL_FAILED when no execution path exists", async () => {
    const result = await routeGoogleToolCall({
      projectRoot: "/tmp",
      agentId: "DEV-01",
      toolName: "list_tasks",
      args: {},
      callId: "c4",
      useFcopOneShot: false,
      auditLogger: new GoogleToolAuditLogger(),
    });
    assert.equal(result.ok, false);
    assert.match(result.error ?? "", /No execution path/);
  });

  it("routes a bounded PM protected-product write into operation approval", async () => {
    const tmp = await mkdtemp(path.join(os.tmpdir(), "cfmu-router-prod-"));
    const result = await routeGoogleToolCall({
      projectRoot: tmp,
      agentId: "PM-01",
      toolName: "write_file",
      args: {
        path: "packages/codeflowmu-runtime/src/registry/RoleToolPolicy.ts",
        content: "bad",
      },
      callId: "c5b",
      useFcopOneShot: false,
      auditLogger: new GoogleToolAuditLogger(),
    });
    assert.equal(result.ok, false);
    assert.equal(result.code, "OPERATION_APPROVAL_REQUIRED");
    assert.ok(result.approval_id);
  });

  it("normalizes legacy relation strings before writing a REPORT", async () => {
    let called: { name: string; args: Record<string, unknown> } | null = null;
    const result = await routeGoogleToolCall({
      projectRoot: "/tmp",
      agentId: "DEV-01",
      toolName: "write_report",
      args: {
        task_id: "TASK-20260731-002",
        reporter: "DEV",
        recipient: "PM",
        body: "done",
        references: "['TASK-20260731-002']",
        depends_on: '["TASK-20260731-001"]',
      },
      callId: "c-report-relations",
      taskId: "TASK-20260731-002",
      useFcopOneShot: false,
      mcpClient: {
        callTool: async (name, args) => {
          called = { name, args };
          return { ok: true };
        },
      },
      auditLogger: new GoogleToolAuditLogger(),
    });
    assert.equal(result.ok, true);
    const writeCall = called as unknown as {
      name: string;
      args: Record<string, unknown>;
    };
    assert.equal(writeCall.name, "write_report");
    assert.deepEqual(writeCall.args.references, ["TASK-20260731-002"]);
    assert.deepEqual(writeCall.args.depends_on, ["TASK-20260731-001"]);
  });

  it("rejects workspace write to fcop envelope paths", async () => {
    const tmp = await mkdtemp(path.join(os.tmpdir(), "cfmu-router-"));
    await mkdir(path.join(tmp, "fcop", "_lifecycle", "inbox"), {
      recursive: true,
    });
    const result = await routeGoogleToolCall({
      projectRoot: tmp,
      agentId: "PM-01",
      toolName: "write_file",
      args: {
        path: "fcop/_lifecycle/inbox/TASK-evil.md",
        content: "bad",
      },
      callId: "c5",
      useFcopOneShot: false,
      auditLogger: new GoogleToolAuditLogger(),
    });
    assert.equal(result.ok, false);
    assert.equal(result.code, "OPERATION_APPROVAL_REQUIRED");
    assert.match(result.error ?? "", /NEG\.GOVERNANCE\.BYPASS/);
    assert.ok(result.approval_id);
  });
});
