import { describe, expect, it } from "bun:test";
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";

import {
  assembleAgentContent,
  checkConsumerPointers,
  EXPECTED_POINTER_VERSION,
} from "../application/agent-content";
import { parseAgentContentRest } from "./cmd-skill";

describe("parseAgentContentRest", () => {
  it("returns help on --help / -h", () => {
    expect(parseAgentContentRest(["skill", "--help"]).kind).toBe("help");
    expect(parseAgentContentRest(["rule", "-h"]).kind).toBe("help");
  });

  it("runs with no extra arguments", () => {
    expect(parseAgentContentRest(["skill"])).toEqual({
      kind: "run",
      verb: "skill",
    });
    expect(parseAgentContentRest(["rule"])).toEqual({
      kind: "run",
      verb: "rule",
    });
  });

  it("errors on unexpected flags or positionals", () => {
    const skillJson = parseAgentContentRest(["skill", "--json"]);
    expect(skillJson.kind).toBe("error");
    if (skillJson.kind === "error") {
      expect(skillJson.message).toContain("unexpected argument");
    }
    const ruleExtra = parseAgentContentRest(["rule", "extra"]);
    expect(ruleExtra.kind).toBe("error");
    if (ruleExtra.kind === "error") {
      expect(ruleExtra.message).toContain("unexpected argument");
    }
  });
});

describe("agent content fetch surfaces", () => {
  it("assembles the skill from section files", () => {
    const text = assembleAgentContent("skill");
    expect(text).toContain("name: codemap");
    expect(text.length).toBeGreaterThan(500);
  });

  it("assembles the rule from section files", () => {
    const text = assembleAgentContent("rule");
    expect(text).toContain("alwaysApply: true");
    expect(text.length).toBeGreaterThan(500);
  });

  it("inlines auto-generated recipe catalog into the skill", () => {
    const text = assembleAgentContent("skill");
    expect(text).toContain("Recipe catalog (auto-generated)");
    // A few bundled ids that should always be present.
    expect(text).toContain("| `fan-out` |");
    expect(text).toContain("| `unimported-exports` |");
    // The "see live catalog" fallback line from the placeholder file
    // should NOT appear (renderer takes precedence).
    expect(text).not.toContain("See the live catalog:");
  });

  it("inlines auto-generated schema DDL into the skill", () => {
    const text = assembleAgentContent("skill");
    expect(text).toContain("Schema reference (auto-generated)");
    // A few stable tables — present since v1 of the schema.
    expect(text).toContain("### `symbols`");
    expect(text).toContain("### `imports`");
    expect(text).toContain("### `exports`");
    // Schema renderer fallback prose should not leak through.
    expect(text).not.toContain("See the live DDL:");
  });
});

describe("checkConsumerPointers (staleness detection)", () => {
  function seedSkill(root: string, body: string): void {
    const dir = join(root, ".agents", "skills", "codemap");
    mkdirSync(dir, { recursive: true });
    writeFileSync(join(dir, "SKILL.md"), body, "utf8");
  }

  it("returns no entries when .agents/ is empty", () => {
    const root = mkdtempSync(join(tmpdir(), "codemap-pointer-"));
    expect(checkConsumerPointers(root)).toEqual([]);
  });

  it("flags a stamped-but-old pointer as stale by version", () => {
    const root = mkdtempSync(join(tmpdir(), "codemap-pointer-"));
    seedSkill(root, "# Pointer\n\n<!-- codemap-pointer-version: 0 -->\n");
    const [status] = checkConsumerPointers(root);
    expect(status?.presentVersion).toBe(0);
    expect(status?.looksLegacy).toBe(false);
    expect(status!.presentVersion! < EXPECTED_POINTER_VERSION).toBe(true);
  });

  it("flags a fat unstamped file as legacy", () => {
    const root = mkdtempSync(join(tmpdir(), "codemap-pointer-"));
    seedSkill(root, "x\n".repeat(100));
    const [status] = checkConsumerPointers(root);
    expect(status?.presentVersion).toBeNull();
    expect(status?.looksLegacy).toBe(true);
  });

  it("does not flag a short unstamped user-managed file", () => {
    const root = mkdtempSync(join(tmpdir(), "codemap-pointer-"));
    seedSkill(root, "# My own skill\n\nNothing to see here.\n");
    const [status] = checkConsumerPointers(root);
    expect(status?.presentVersion).toBeNull();
    expect(status?.looksLegacy).toBe(false);
  });

  it("accepts the current bundled pointer as fresh", () => {
    const root = mkdtempSync(join(tmpdir(), "codemap-pointer-"));
    seedSkill(
      root,
      `<!-- codemap-pointer-version: ${EXPECTED_POINTER_VERSION} -->\n`,
    );
    const [status] = checkConsumerPointers(root);
    expect(status?.presentVersion).toBe(EXPECTED_POINTER_VERSION);
  });
});
