import { describe, it } from "node:test";
import assert from "node:assert/strict";
import {
  buildIndex,
  match,
  parseFlags,
  parseKeyValues,
  pathEndsWith,
  commandHead,
  tokenize,
} from "./matcher.ts";
import type { SkillStandardConfig, ToolCall } from "./types.ts";

const configs: SkillStandardConfig[] = [
  {
    skillName: "model-usage",
    version: "1.0.0",
    functions: [
      { id: "usage_current", name: "当前用量", match: { type: "script", script: "scripts/model_usage.py", argRules: [{ flag: "--mode", value: "current" }] } },
      { id: "usage_all", name: "全部用量", match: { type: "script", script: "scripts/model_usage.py", argRules: [{ flag: "--mode", value: "all" }] } },
    ],
  },
  {
    skillName: "openai-whisper-api",
    version: "1.0.0",
    functions: [{ id: "transcribe", name: "转写", match: { type: "script", script: "scripts/transcribe.sh" } }],
  },
  {
    skillName: "mcporter",
    version: "1.0.0",
    functions: [{ id: "list_issues", name: "列issue", match: { type: "command", command: "mcporter", targetPattern: "call linear.list_issues" } }],
  },
  {
    skillName: "linear-mcp",
    version: "1.0.0",
    functions: [
      { id: "create_issue", name: "建issue", match: { type: "tool", toolName: "linear_create_issue" } },
      { id: "transcribe_http", name: "转写端点", match: { type: "http", urlContains: "/v1/audio/transcriptions" } },
    ],
  },
];

const index = buildIndex(configs);
const noActive = new Set<string>();

function exec(command: string): ToolCall {
  return { toolName: "exec", params: { command } };
}

describe("pathEndsWith / tokenize / commandHead", () => {
  it("脚本路径按段对齐，规避 fooX.py 误判", () => {
    assert.equal(pathEndsWith("/a/b/scripts/model_usage.py", "scripts/model_usage.py"), true);
    assert.equal(pathEndsWith("scripts/model_usage.py", "scripts/model_usage.py"), true);
    assert.equal(pathEndsWith("/a/xscripts/model_usage.py", "scripts/model_usage.py"), false);
    assert.equal(pathEndsWith("/a/foomodel_usage.py", "model_usage.py"), false);
  });

  it("tokenize 处理引号", () => {
    assert.deepEqual(tokenize(`a "b c" 'd e' f`), ["a", "b c", "d e", "f"]);
  });

  it("commandHead 跳过环境前缀与解释器", () => {
    assert.equal(commandHead(tokenize("FOO=1 python /x/scripts/y.py --a")), "y.py");
    assert.equal(commandHead(tokenize("mcporter call linear.list_issues")), "mcporter");
  });
});

describe("parseFlags / parseKeyValues", () => {
  it("--k v / --k=v / 布尔 flag", () => {
    const f = parseFlags(tokenize("x --mode current --pretty --n=3 -v"));
    assert.equal(f.mode, "current");
    assert.equal(f.pretty, true);
    assert.equal(f.n, "3");
    assert.equal(f.v, true);
  });

  it("k=v / k:v，跳过 URL", () => {
    const kv = parseKeyValues(tokenize("call linear.list_issues team=ENG limit:5 url=https://x/y"));
    assert.equal(kv.team, "ENG");
    assert.equal(kv.limit, "5");
    assert.equal(kv.url, undefined); // 含 :// 被跳过
  });
});

describe("match - script", () => {
  it("按 --mode 细分功能点", () => {
    const cur = match(exec("python /e/skills/model-usage/scripts/model_usage.py --mode current"), noActive, index);
    assert.equal(cur?.functionId, "usage_current");
    assert.equal(cur?.matchType, "script");
    assert.equal(cur?.args.mode, "current");

    const all = match(exec("python /e/skills/model-usage/scripts/model_usage.py --mode all --pretty"), noActive, index);
    assert.equal(all?.functionId, "usage_all");
  });

  it("argRules 不满足则不命中该功能点", () => {
    const r = match(exec("python /e/scripts/model_usage.py --mode none"), noActive, index);
    assert.equal(r, null);
  });

  it("解释器无关：bash 跑 .sh 命中", () => {
    const r = match(exec("bash /e/openai-whisper-api/scripts/transcribe.sh a.mp3"), noActive, index);
    assert.equal(r?.functionId, "transcribe");
  });
});

describe("match - command / tool / http", () => {
  it("command: mcporter call", () => {
    const r = match(exec("mcporter call linear.list_issues team=ENG"), noActive, index);
    assert.equal(r?.functionId, "list_issues");
    assert.equal(r?.matchType, "command");
    assert.equal(r?.args.team, "ENG");
  });

  it("tool: 非 exec 工具直调", () => {
    const r = match({ toolName: "linear_create_issue", params: { title: "bug" } }, noActive, index);
    assert.equal(r?.functionId, "create_issue");
    assert.equal(r?.matchType, "tool");
    assert.equal(r?.args.title, "bug");
  });

  it("http: curl 命中端点", () => {
    const r = match(exec("curl -s https://api.openai.com/v1/audio/transcriptions -F file=@a.mp3"), noActive, index);
    assert.equal(r?.functionId, "transcribe_http");
    assert.equal(r?.matchType, "http");
  });

  it("http: fetch 类工具 url 参数命中", () => {
    const r = match({ toolName: "fetch", params: { url: "https://api.openai.com/v1/audio/transcriptions?x=1" } }, noActive, index);
    assert.equal(r?.functionId, "transcribe_http");
    assert.equal(r?.args.x, "1");
  });

  it("无匹配返回 null", () => {
    assert.equal(match(exec("ls -la"), noActive, index), null);
    assert.equal(match({ toolName: "unknown_tool", params: {} }, noActive, index), null);
  });
});

describe("match - 弱匹配（cd 后相对路径）仅在 skill 激活时采纳", () => {
  it("仅 basename 相同：未激活 → null，激活 → 命中", () => {
    // `cd .../openai-whisper-api/scripts && bash transcribe.sh a.mp3`
    const call = exec("bash transcribe.sh a.mp3");
    assert.equal(match(call, noActive, index), null);
    const r = match(call, new Set(["openai-whisper-api"]), index);
    assert.equal(r?.functionId, "transcribe");
  });

  it("强匹配优先于弱匹配", () => {
    // 同时出现强（全路径）与弱（裸名）token 时取强匹配
    const r = match(exec("bash /e/openai-whisper-api/scripts/transcribe.sh"), noActive, index);
    assert.equal(r?.functionId, "transcribe");
  });
});

describe("match - 歧义消解优先 activeSkills", () => {
  const ambConfigs: SkillStandardConfig[] = [
    { skillName: "A", version: "1", functions: [{ id: "a", name: "a", match: { type: "tool", toolNamePrefix: "x_" } }] },
    { skillName: "B", version: "1", functions: [{ id: "b", name: "b", match: { type: "tool", toolNamePrefix: "x_" } }] },
  ];
  const ambIndex = buildIndex(ambConfigs);

  it("无激活时取第一候选", () => {
    assert.equal(match({ toolName: "x_do", params: {} }, noActive, ambIndex)?.skillName, "A");
  });

  it("激活 B 时归属 B", () => {
    assert.equal(match({ toolName: "x_do", params: {} }, new Set(["B"]), ambIndex)?.skillName, "B");
  });
});
