import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { existsSync, readFileSync } from "node:fs";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerCalendarTools } from "../src/tools/calendar.js";
import { registerContactsTools } from "../src/tools/contacts.js";
import { registerFileTools } from "../src/tools/files.js";
import { registerKeynoteTools } from "../src/tools/keynote.js";
import { registerMailTools } from "../src/tools/mail.js";
import { registerNotesTools } from "../src/tools/notes.js";
import { registerNumbersTools } from "../src/tools/numbers.js";
import { registerPagesTools } from "../src/tools/pages.js";
import { registerReminderTools } from "../src/tools/reminders.js";
import { registerSystemTools } from "../src/tools/system.js";

const repoRoot = new URL("../", import.meta.url);

function readRepoFile(path: string): string {
  return readFileSync(new URL(path, repoRoot), "utf8");
}

const packageJson = JSON.parse(readRepoFile("package.json")) as {
  version: string;
  engines: { node: string };
  description: string;
};

function registeredToolCount(): number {
  const server = new McpServer({ name: "orchard-mcp", version: packageJson.version });
  registerCalendarTools(server);
  registerMailTools(server);
  registerReminderTools(server);
  registerSystemTools(server);
  registerFileTools(server);
  registerNumbersTools(server);
  registerPagesTools(server);
  registerKeynoteTools(server);
  registerNotesTools(server);
  registerContactsTools(server);
  return Object.keys((server as any)._registeredTools as Record<string, unknown>).length;
}

describe("metadata stays in sync", () => {
  it("keeps package-lock root metadata aligned with package.json", () => {
    const packageLock = JSON.parse(readRepoFile("package-lock.json")) as {
      version: string;
      packages: { "": { version: string } };
    };
    assert.equal(packageLock.version, packageJson.version);
    assert.equal(packageLock.packages[""].version, packageJson.version);
  });

  it("uses package.json as the TypeScript server version source", () => {
    const index = readRepoFile("src/index.ts");
    assert.match(index, /version:\s*packageJson\.version/);
    assert.doesNotMatch(index, /version:\s*["']\d+\.\d+\.\d+["']/);
  });

  it("keeps Swift bridge versions aligned with package.json", () => {
    const appleBridge = readRepoFile("swift/Sources/AppleBridge/AppleBridge.swift");
    const doctor = readRepoFile("swift/Sources/AppleBridge/Doctor.swift");
    assert.match(appleBridge, new RegExp(`version: "${packageJson.version}"`));
    assert.match(doctor, new RegExp(`"version": "${packageJson.version}"`));
  });

  it("does not keep stale package snapshots in the repo", () => {
    assert.equal(
      existsSync(new URL("package/package.json", repoRoot)),
      false,
      "remove generated package snapshots instead of maintaining duplicate package metadata"
    );
  });

  it("keeps docs aligned with the supported Node.js floor", () => {
    const supportedNode = packageJson.engines.node.replace(">=", "");
    for (const path of ["README.md", "CONTRIBUTING.md", "AGENTS.md"]) {
      const doc = readRepoFile(path);
      assert.match(doc, new RegExp(`Node\\.js ${supportedNode.replace(/\..*$/, "")}\\+`), path);
      assert.doesNotMatch(doc, /Node\.js 18\+/, path);
    }
  });

  it("keeps live docs aligned with the current tool surface", () => {
    const count = registeredToolCount();
    const readme = readRepoFile("README.md");
    const contributing = readRepoFile("CONTRIBUTING.md");
    const agents = readRepoFile("AGENTS.md");

    for (const heading of ["Numbers", "Pages", "Keynote", "Notes", "Contacts"]) {
      assert.match(readme, new RegExp(`### ${heading}`));
    }

    assert.match(contributing, new RegExp(`all ${count} tools`));
    assert.match(agents, new RegExp(`${count} tools total`));
    assert.match(agents, /registers tools from 10 modules/);
    assert.doesNotMatch(agents, /registers tools from 8 modules/);
    assert.match(agents, /notes\.\*/);
    assert.match(agents, /contacts\.\*/);
  });

  it("keeps CLAUDE.md as a thin pointer to AGENTS.md", () => {
    const claude = readRepoFile("CLAUDE.md");
    const gitignore = readRepoFile(".gitignore");

    assert.match(claude, /AGENTS\.md/);
    assert.match(claude, /Do not edit this file for project guidance/);
    assert.doesNotMatch(claude, /## Architecture/);
    assert.doesNotMatch(claude, /65 tools total/);
    assert.doesNotMatch(gitignore, /^CLAUDE\.md$/m);
  });

  it("removes stale live-documentation references", () => {
    const readme = readRepoFile("README.md");
    const contributing = readRepoFile("CONTRIBUTING.md");

    assert.doesNotMatch(packageJson.description, /Calendar, Mail, Reminders, and Files/);
    assert.doesNotMatch(readme, /docs\/PRD\.md/);
    assert.doesNotMatch(readme, /child_process\.execFile/);
    assert.doesNotMatch(contributing, /child_process\.execFile/);
  });

  it("documents every explicit Swift CLI subcommand", () => {
    const readme = readRepoFile("README.md");
    const appleBridge = readRepoFile("swift/Sources/AppleBridge/AppleBridge.swift");
    const commandNames = [
      ...appleBridge.matchAll(/commandName:\s*"([^"]+)"/g),
    ].map((match) => match[1]);

    for (const commandName of commandNames) {
      if (commandName === "apple-bridge") continue;
      assert.match(readme, new RegExp(`apple-bridge ${commandName}\\b`), commandName);
    }
  });

  it("routes every tool module through the app-safety bridge wrapper", () => {
    const toolModules = [
      "src/tools/calendar.ts",
      "src/tools/contacts.ts",
      "src/tools/files.ts",
      "src/tools/keynote.ts",
      "src/tools/mail.ts",
      "src/tools/notes.ts",
      "src/tools/numbers.ts",
      "src/tools/pages.ts",
      "src/tools/reminders.ts",
      "src/tools/system.ts",
    ];

    for (const path of toolModules) {
      const source = readRepoFile(path);
      assert.doesNotMatch(source, /from "\.\.\/bridge\.js"/, path);
      assert.doesNotMatch(source, /\bbridgeData\(/, path);
      assert.match(source, /from "\.\.\/safety\.js"/, path);
      assert.match(source, /\bsafeBridgeData\(/, path);
    }
  });

  it("documents app-safety guardrails for host applications", () => {
    const readme = readRepoFile("README.md");
    const agents = readRepoFile("AGENTS.md");
    const audit = readRepoFile("docs/app-safety-audit.md");

    for (const doc of [readme, agents, audit]) {
      assert.match(doc, /App Safety/i);
      assert.match(doc, /Mail\.app/);
      assert.match(doc, /refuse/i);
    }
    assert.match(audit, /OPERATION_PROFILES/);
    assert.match(audit, /mail\.save_attachment/);
    assert.match(audit, /Notes/);
    assert.match(audit, /Numbers/);
    assert.match(audit, /Files/);
  });
});
