import assert from "node:assert/strict";
import { afterEach, beforeEach, describe, it } from "node:test";
import { EXECUTOR_TOOLS, LEADER_TOOLS } from "../../skill/FcopToolProfile.ts";
import {
  isAdminRejectColdPathPrompt,
  isAdminRejectHotPathPrompt,
  isPmDevColdDispatchContext,
  isPmSelfExecuteHotPathPrompt,
  isRuntimeTaskDispatchPrompt,
  selectGeminiToolNames,
  shouldSplitPromptOnSystemDelimiter,
} from "../geminiToolSelection.ts";

const RUNTIME_DISPATCH_PROMPT = `
任务正文已在下方；禁止 claim_task / read_task。
Step 1: write_report 回执。

---
# TASK-20260606-001-ADMIN-to-PM
`;

describe("shouldSplitPromptOnSystemDelimiter", () => {
  it("splits TaskDispatcher wake/dispatch prompts at first delimiter only", () => {
    const dispatchPrompt = `
任务正文已在下方；禁止 claim_task / read_task。
Step 1: write_report 回执。

---
---
protocol: fcop
---
# 返工任务

正文段落

---

## state_history (auto-appended by runtime)
| t | s |
`;
    assert.equal(shouldSplitPromptOnSystemDelimiter(dispatchPrompt, "PM-01"), true);
    const userText = dispatchPrompt.split("\n\n---").slice(1).join("\n\n---").trim();
    assert.match(userText, /state_history/);
    assert.match(userText, /返工任务/);
  });

  it("does not split admin reject cold path when task body contains \\n\\n---", () => {
    const coldPathPrompt = `[ADMIN ↔ PM · ADMIN 打回协调 · Cold Path（非闲聊 · 必须 write_task）]
## 最高优先级 · ADMIN 判定打回
又是死循环！
## 当前 TASK 正文（Runtime 已预载 · ADMIN 打回 Cold Path）
**任务正文已在下方**
\`\`\`markdown
---
protocol: fcop
---
# 返工任务

请协调 DEV/QA/OPS。

---

## state_history (auto-appended by runtime)
| t | s |
\`\`\`
ADMIN: 又是死循环！
`;
    assert.equal(shouldSplitPromptOnSystemDelimiter(coldPathPrompt, "PM-01"), false);
    assert.equal(isAdminRejectColdPathPrompt(coldPathPrompt, "PM-01"), true);
    assert.equal(isRuntimeTaskDispatchPrompt(coldPathPrompt), true);
  });

  it("does not split admin reject hot path when task body contains \\n\\n---", () => {
    const hotPathPrompt = `[ADMIN ↔ PM · ADMIN 打回协调 · Hot Path（PM 治理核查/协调 · 不代表可修改产品代码）]
## 最高优先级 · ADMIN 判定打回（Hot Path · PM 亲自完成治理核查/协调，不代表可修改产品代码）
重新启动，重新做！
## 当前 TASK 正文（Runtime 已预载 · ADMIN 打回 Hot Path）
**任务正文已在下方**
\`\`\`markdown
---
protocol: fcop
---
# Hot Path 返工

PM 亲自执行 fcop_check。

---

## state_history (auto-appended by runtime)
| t | s |
\`\`\`
ADMIN: 重新做！
`;
    assert.equal(shouldSplitPromptOnSystemDelimiter(hotPathPrompt, "PM-01"), false);
    assert.equal(isAdminRejectHotPathPrompt(hotPathPrompt, "PM-01"), true);
    assert.equal(isAdminRejectColdPathPrompt(hotPathPrompt, "PM-01"), false);
  });

  it("does not split generic chat even if message contains horizontal rule", () => {
    const chat = "你好\n\n---\n\n这是 markdown 分隔线，不是 system/user split";
    assert.equal(shouldSplitPromptOnSystemDelimiter(chat, "PM-01"), false);
  });
});

describe("isAdminRejectColdPathPrompt", () => {
  it("detects cold path markers in full prompt before task-body delimiter", () => {
    const fullPrompt = `[ADMIN ↔ PM · Cold Path]
## 最高优先级 · ADMIN 判定打回
又是死循环！
## 当前 TASK 正文（Runtime 已预载）
---
protocol: fcop
`;
    assert.equal(isAdminRejectColdPathPrompt(fullPrompt, "PM-01"), true);
  });

  it("does not match after naive split when markers only in system half", () => {
    const fullPrompt = `[Cold Path · ADMIN 判定打回]
---
protocol: fcop
`;
    const userTextOnly = fullPrompt.split("\n\n---").slice(1).join("\n\n---").trim();
    assert.equal(isAdminRejectColdPathPrompt(userTextOnly, "PM-01"), false);
    assert.equal(isAdminRejectColdPathPrompt(fullPrompt, "PM-01"), true);
  });

  it("ignores non-leader agents", () => {
    assert.equal(
      isAdminRejectColdPathPrompt("ADMIN 判定打回 Cold Path", "DEV-01"),
      false,
    );
  });
});

describe("isPmDevColdDispatchContext", () => {
  it("true for runtime task dispatch on leader", () => {
    assert.equal(isPmDevColdDispatchContext(RUNTIME_DISPATCH_PROMPT, "PM-01"), true);
  });

  it("true for admin reject cold path", () => {
    const prompt = `
## 最高优先级 · ADMIN 判定打回（Cold Path）
禁止 write_report ack
`;
    assert.equal(isPmDevColdDispatchContext(prompt, "PM-01"), true);
  });

  it("false for PM self-execute hot path", () => {
    const prompt = `
## Your playbook (PM — Hot Path · PM 亲自执行)
- **PM self-execute Hot Path**：禁止 write_task
`;
    assert.equal(isPmSelfExecuteHotPathPrompt(prompt, "PM-01"), true);
    assert.equal(isPmDevColdDispatchContext(prompt, "PM-01"), false);
  });

  it("false for pm_self_report_only runMode", () => {
    const prompt = `run_mode: pm_self_report_only`;
    assert.equal(
      isPmDevColdDispatchContext(prompt, "PM-01", "pm_self_report_only"),
      false,
    );
  });

  it("false for non-leader agent", () => {
    assert.equal(isPmDevColdDispatchContext(RUNTIME_DISPATCH_PROMPT, "DEV-01"), false);
  });
});

describe("isRuntimeTaskDispatchPrompt", () => {
  it("detects runtime-injected task body markers", () => {
    assert.equal(isRuntimeTaskDispatchPrompt(RUNTIME_DISPATCH_PROMPT), true);
  });

  it("does not match generic chat", () => {
    assert.equal(isRuntimeTaskDispatchPrompt("你好，请帮我看一下进度"), false);
    assert.equal(
      isRuntimeTaskDispatchPrompt("你好\n\n---\n\n这是 markdown 分隔线"),
      false,
    );
  });
});

describe("selectGeminiToolNames hot path", () => {
  const leaderAllowed = [...LEADER_TOOLS];
  const executorAllowed = [...EXECUTOR_TOOLS];

  const prevMode = process.env.CODEFLOW_GEMINI_TOOL_MODE;
  const prevMax = process.env.CODEFLOW_GEMINI_MAX_TOOLS;

  beforeEach(() => {
    delete process.env.CODEFLOW_GEMINI_TOOL_MODE;
    delete process.env.CODEFLOW_GEMINI_MAX_TOOLS;
  });

  afterEach(() => {
    if (prevMode === undefined) delete process.env.CODEFLOW_GEMINI_TOOL_MODE;
    else process.env.CODEFLOW_GEMINI_TOOL_MODE = prevMode;
    if (prevMax === undefined) delete process.env.CODEFLOW_GEMINI_MAX_TOOLS;
    else process.env.CODEFLOW_GEMINI_MAX_TOOLS = prevMax;
  });

  it("PM on runtime dispatch keeps write_task and create_task", () => {
    const selected = selectGeminiToolNames(
      leaderAllowed,
      RUNTIME_DISPATCH_PROMPT,
      "PM-01",
    );
    assert.ok(selected.includes("write_report"));
    assert.ok(selected.includes("write_task"));
    assert.ok(selected.includes("create_task"));
    assert.ok(selected.includes("list_tasks"));
    assert.ok(selected.includes("read_task"));
    assert.ok(selected.includes("read_report"));
    assert.ok(selected.includes("list_issues"));
  });

  it("DEV on runtime dispatch stays direct-bound and executor-only", () => {
    const selected = selectGeminiToolNames(
      executorAllowed,
      RUNTIME_DISPATCH_PROMPT,
      "DEV-01",
    );
    assert.deepEqual(selected, [
      "fcop_report",
      "list_my_tasks",
      "read_my_task",
      "write_report",
      "write_issue",
      "drop_suggestion",
    ]);
    assert.ok(!selected.includes("write_task"));
  });

  it("PM with trimmed budget still gets write_task when agent id is leader", () => {
    process.env.CODEFLOW_GEMINI_TOOL_MODE = "trim";
    process.env.CODEFLOW_GEMINI_MAX_TOOLS = "8";
    const selected = selectGeminiToolNames(
      leaderAllowed,
      "请给 DEV 派一个修复任务",
      "PM-01",
    );
    assert.ok(selected.includes("write_task"));
    assert.ok(selected.includes("create_task"));
  });

  it("profile mode exposes full leader allowlist for patrol chat", () => {
    process.env.CODEFLOW_GEMINI_TOOL_MODE = "profile";
    const selected = selectGeminiToolNames(
      leaderAllowed,
      "开始巡检，检查 inbox 和 reports",
      "PM-01",
    );
    assert.equal(selected.length, leaderAllowed.length);
    assert.ok(selected.includes("fcop_report"));
    assert.ok(selected.includes("fcop_check"));
    assert.ok(selected.includes("get_team_status"));
  });

  it("PM admin reject cold path includes workspace read tools in trim mode", () => {
    process.env.CODEFLOW_GEMINI_TOOL_MODE = "trim";
    const prompt = `
## 最高优先级 · ADMIN 判定打回（Cold Path）
禁止 write_report ack
`;
    const withWorkspace = [
      ...leaderAllowed,
      "read_file",
      "grep_files",
      "list_dir",
      "write_file",
    ];
    const selected = selectGeminiToolNames(withWorkspace, prompt, "PM-01");
    assert.ok(selected.includes("read_file"));
    assert.ok(selected.includes("grep_files"));
    assert.ok(selected.includes("list_dir"));
    assert.ok(!selected.includes("write_report"));
  });

  it("PM admin reject cold path prioritizes write_task over write_report", () => {
    const prompt = `
## 最高优先级 · ADMIN 判定打回（Cold Path）
禁止 write_report ack
`;
    const selected = selectGeminiToolNames(leaderAllowed, prompt, "PM-01");
    assert.ok(selected.includes("write_task"));
    assert.ok(!selected.includes("write_report"));
    assert.equal(selected[0], "write_task");
  });

  it("PM admin reject + preloaded task body excludes write_report", () => {
    const prompt = `
## 最高优先级 · ADMIN 判定打回（Cold Path）
**任务正文已在下方**
---
# TASK-20260606-007
`;
    const selected = selectGeminiToolNames(leaderAllowed, prompt, "PM-01");
    assert.ok(selected.includes("write_task"));
    assert.ok(!selected.includes("write_report"));
  });

  it("PM admin reject hot path includes write_task and excludes write_file", () => {
    const prompt = `
## 最高优先级 · ADMIN 判定打回（Hot Path · PM 亲自完成治理核查/协调，不代表可修改产品代码）
禁止 edit 产品代码
`;
    const withWorkspace = [
      ...leaderAllowed,
      "read_file",
      "grep_files",
      "write_file",
    ];
    const selected = selectGeminiToolNames(withWorkspace, prompt, "PM-01");
    assert.ok(selected.includes("write_task"));
    assert.ok(selected.includes("write_report"));
    assert.ok(!selected.includes("write_file"));
  });

  it("PM self-contained smoke (TASK-029) is Hot Path not Cold Path — no write_task", () => {
    const prompt = `
## Your playbook (PM — Hot Path · PM 亲自执行)
> **This is NOT ADMIN reject Cold Path. This is NOT rework dispatch.**

## Runtime context pack
- **PM self-execute Hot Path**：禁止 write_task

# 开启 Google PM 最小闭环冒烟测试。
- PM 不派发下游
禁止：
- 不调用 write_task
- 不派 DEV / OPS / QA
1. 本次是 Google PM 最小闭环冒烟。
`;
    assert.equal(isAdminRejectColdPathPrompt(prompt, "PM-01"), false);
    assert.equal(isPmSelfExecuteHotPathPrompt(prompt, "PM-01"), true);
    const selected = selectGeminiToolNames(leaderAllowed, prompt, "PM-01");
    assert.ok(selected.includes("write_report"));
    assert.ok(!selected.includes("write_task"));
  });

  it("pm_self_report_only runMode 仅暴露 write_report + 只读探针", () => {
    const prompt = `
## Runtime context pack
- run_mode: pm_self_report_only
- pinned_task_id: TASK-20260607-029
`;
    const selfReportAllowed = [...leaderAllowed, "read_file", "grep_files"];
    const selected = selectGeminiToolNames(selfReportAllowed, prompt, "PM-01", {
      runMode: "pm_self_report_only",
    });
    assert.deepEqual(selected, ["write_report", "read_file", "grep_files"]);
    assert.ok(!selected.includes("write_task"));
    assert.ok(!selected.includes("create_task"));
  });

  it("pm_self_report_only 从 prompt run_mode 标记识别", () => {
    const prompt = `
run_mode: pm_self_report_only
pinned_task_id: TASK-20260607-029
`;
    const selfReportAllowed = [...leaderAllowed, "read_file", "grep_files"];
    const selected = selectGeminiToolNames(selfReportAllowed, prompt, "PM-01");
    assert.deepEqual(selected, ["write_report", "read_file", "grep_files"]);
  });

  it("pm_self_report_only 无 workspace 白名单时仅 write_report", () => {
    const prompt = `run_mode: pm_self_report_only`;
    const selected = selectGeminiToolNames(leaderAllowed, prompt, "PM-01", {
      runMode: "pm_self_report_only",
    });
    assert.deepEqual(selected, ["write_report"]);
  });
});
