import { afterEach, beforeEach, describe, expect, it } from "bun:test";
import { mkdir, mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";

import { SkillStore } from "../../src/skills/store.js";

describe("SkillStore", () => {
  let originalCwd: string;
  let tempDir: string;
  let alternateCwd: string;
  let skillsRoot: string;
  let backupRoot: string;
  let store: SkillStore;

  const skillDocument = (name: string, body: string) =>
    [
      "---",
      `name: ${name}`,
      `description: ${name} workflow.`,
      "---",
      "",
      body,
    ].join("\n");

  beforeEach(async () => {
    originalCwd = process.cwd();
    tempDir = await mkdtemp(join(tmpdir(), "tjwater-skills-"));
    alternateCwd = join(tempDir, "runtime-cwd");
    skillsRoot = join(tempDir, "project", ".opencode", "skills");
    backupRoot = join(tempDir, "backup", "skills");
    store = new SkillStore(skillsRoot, backupRoot);
  });

  afterEach(async () => {
    process.chdir(originalCwd);
    await rm(tempDir, { force: true, recursive: true });
  });

  it("writes scripts under the configured skills root regardless of process cwd", async () => {
    await mkdir(alternateCwd, { recursive: true });
    process.chdir(alternateCwd);

    const result = await store.writeScript(
      "workflow/hydraulic-bottleneck-analysis",
      "scripts/analyze.py",
      "print('ok')\n",
    );

    expect(result).toEqual({
      changed: true,
      detail: "script written",
      target: join(
        skillsRoot,
        "workflow",
        "hydraulic-bottleneck-analysis",
        "scripts",
        "analyze.py",
      ),
    });
    await expect(readFile(result.target, "utf8")).resolves.toBe("print('ok')\n");
  });

  it("rejects script paths outside scripts/*.py", async () => {
    const result = await store.writeScript(
      "workflow/hydraulic-bottleneck-analysis",
      "analyze.ts",
      "console.log('ok')\n",
    );

    expect(result).toEqual({
      changed: false,
      detail: "invalid script file_path",
      target: "",
    });
  });

  it("writes and overwrites the main skill file", async () => {
    const skillPath = "workflow/pressure-review";
    const writeResult = await store.writeSkill(
      skillPath,
      skillDocument("pressure-review", "# Pressure Review"),
    );

    expect(writeResult).toEqual({
      changed: true,
      detail: "skill written",
      target: join(skillsRoot, "workflow", "pressure-review", "SKILL.md"),
    });

    const overwriteResult = await store.writeSkill(
      skillPath,
      skillDocument("pressure-review", "# Updated Pressure Review"),
    );

    expect(overwriteResult).toEqual({
      changed: true,
      detail: "skill written",
      target: writeResult.target,
    });
    await expect(readFile(writeResult.target, "utf8")).resolves.toContain(
      "# Updated Pressure Review\n",
    );
  });

  it("writes the root skills index via the reserved alias", async () => {
    const result = await store.writeSkill(
      "__root__",
      [
        "---",
        "name: skills",
        "description: TJWater Skills root index.",
        "---",
        "",
        "# TJWater Skills",
      ].join("\n"),
    );

    expect(result).toEqual({
      changed: true,
      detail: "skill written",
      target: join(skillsRoot, "SKILL.md"),
    });
    await expect(readFile(result.target, "utf8")).resolves.toContain(
      "# TJWater Skills\n",
    );
  });

  it("removes the main skill file", async () => {
    const writeResult = await store.writeSkill(
      "workflow/remove-me",
      skillDocument("remove-me", "# Remove Me"),
    );
    const removeResult = await store.removeSkill("workflow/remove-me");

    expect(removeResult).toEqual({
      changed: true,
      detail: "skill removed",
      target: writeResult.target,
    });
    await expect(readFile(writeResult.target, "utf8")).rejects.toThrow();
  });

  it("rejects sensitive skill content", async () => {
    const result = await store.writeSkill(
      "workflow/unsafe",
      "access_token=secret-value",
    );

    expect(result).toEqual({
      changed: false,
      detail: "skill content rejected by persistence policy",
      target: "",
    });
  });

  it("rejects skill content without required frontmatter", async () => {
    const result = await store.writeSkill("workflow/incomplete", "# Incomplete");

    expect(result).toEqual({
      changed: false,
      detail: "skill content rejected: expected SKILL.md frontmatter with name and description",
      target: "",
    });
  });
});
