import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { copySkills, copyWorkflows, setupSymlink } from "./distribute";

describe("copySkills", () => {
  let tmpDir: string;

  beforeEach(() => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "distribute-test-"));
  });

  afterEach(() => {
    fs.rmSync(tmpDir, { recursive: true, force: true });
  });

  it("copies erp-kit-* skills to destination", () => {
    const result = copySkills(tmpDir);
    const skillPath = path.join(
      tmpDir,
      ".agents",
      "skills",
      "erp-kit-module-1-requirements",
      "SKILL.md",
    );
    expect(fs.existsSync(skillPath)).toBe(true);
    expect(result.copied).toBeGreaterThan(0);
  });

  it("removes stale erp-kit-* skills before copying", () => {
    const staleDir = path.join(tmpDir, ".agents", "skills", "erp-kit-old-skill");
    fs.mkdirSync(staleDir, { recursive: true });
    fs.writeFileSync(path.join(staleDir, "SKILL.md"), "# Stale");
    const result = copySkills(tmpDir);
    expect(fs.existsSync(staleDir)).toBe(false);
    expect(result.removed).toBe(1);
  });

  it("preserves non-erp-kit skills", () => {
    const customDir = path.join(tmpDir, ".agents", "skills", "my-custom-skill");
    fs.mkdirSync(customDir, { recursive: true });
    fs.writeFileSync(path.join(customDir, "SKILL.md"), "# Custom");
    copySkills(tmpDir);
    expect(fs.readFileSync(path.join(customDir, "SKILL.md"), "utf-8")).toBe("# Custom");
  });
});

describe("copyWorkflows", () => {
  let tmpDir: string;

  beforeEach(() => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "distribute-test-"));
  });

  afterEach(() => {
    fs.rmSync(tmpDir, { recursive: true, force: true });
  });

  it("copies workflow templates to .github/workflows/", () => {
    const result = copyWorkflows(tmpDir);
    const wfPath = path.join(tmpDir, ".github", "workflows", "erp-kit-check.yml");
    expect(fs.existsSync(wfPath)).toBe(true);
    expect(result.copied).toBeGreaterThan(0);
  });

  it("overwrites existing workflows", () => {
    const wfDir = path.join(tmpDir, ".github", "workflows");
    fs.mkdirSync(wfDir, { recursive: true });
    fs.writeFileSync(path.join(wfDir, "erp-kit-check.yml"), "# Old");
    copyWorkflows(tmpDir);
    const content = fs.readFileSync(path.join(wfDir, "erp-kit-check.yml"), "utf-8");
    expect(content).not.toBe("# Old");
  });
});

describe("setupSymlink", () => {
  let tmpDir: string;

  beforeEach(() => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "distribute-test-"));
  });

  afterEach(() => {
    fs.rmSync(tmpDir, { recursive: true, force: true });
  });

  it("creates .claude/skills symlink", () => {
    setupSymlink(tmpDir);
    const claudeSkills = path.join(tmpDir, ".claude", "skills");
    expect(fs.lstatSync(claudeSkills).isSymbolicLink()).toBe(true);
    expect(fs.readlinkSync(claudeSkills)).toBe(path.join("..", ".agents", "skills"));
  });

  it("skips when correct symlink already exists", () => {
    setupSymlink(tmpDir);
    const result = setupSymlink(tmpDir);
    expect(result).toBe("already_linked");
  });

  it("skips when .claude/skills is a real directory", () => {
    const claudeSkills = path.join(tmpDir, ".claude", "skills");
    fs.mkdirSync(claudeSkills, { recursive: true });
    const result = setupSymlink(tmpDir);
    expect(result).toBe("skipped_directory");
  });

  it("falls back to copy when symlink creation fails", () => {
    // Populate .agents/skills with a test file so copy has something to work with
    const agentsSkills = path.join(tmpDir, ".agents", "skills", "test-skill");
    fs.mkdirSync(agentsSkills, { recursive: true });
    fs.writeFileSync(path.join(agentsSkills, "SKILL.md"), "# Test");

    // Simulate Windows EPERM — symlink creation fails
    vi.spyOn(fs, "symlinkSync").mockImplementation(() => {
      const err = new Error("EPERM: operation not permitted") as NodeJS.ErrnoException;
      err.code = "EPERM";
      throw err;
    });

    try {
      const result = setupSymlink(tmpDir);
      expect(result).toBe("copied");

      const claudeSkills = path.join(tmpDir, ".claude", "skills");
      expect(fs.existsSync(claudeSkills)).toBe(true);
      expect(fs.statSync(claudeSkills).isDirectory()).toBe(true);
      // Not a symlink — it's a real directory copy
      expect(fs.lstatSync(claudeSkills).isSymbolicLink()).toBe(false);
      // Content was copied
      expect(fs.readFileSync(path.join(claudeSkills, "test-skill", "SKILL.md"), "utf-8")).toBe(
        "# Test",
      );
    } finally {
      vi.restoreAllMocks();
    }
  });
});
