import { mkdtemp, mkdir, readdir, rm, access, writeFile } from "node:fs/promises";
import { constants as fsConstants } from "node:fs";
import { join, resolve } from "node:path";
import { spawnSync } from "node:child_process";
import { tmpdir } from "node:os";
import test from "node:test";
import assert from "node:assert/strict";

const cliPath = resolve(
  process.cwd(),
  "bin/antigravity-superpowers.js",
);

async function pathExists(path) {
  try {
    await access(path, fsConstants.F_OK);
    return true;
  } catch {
    return false;
  }
}

function runCli(args, cwd) {
  return spawnSync(process.execPath, [cliPath, ...args], {
    cwd,
    encoding: "utf8",
  });
}

async function createTempProject(prefix) {
  const baseTmp = tmpdir();
  await mkdir(baseTmp, { recursive: true });
  return mkdtemp(join(baseTmp, prefix));
}

test("init creates .agent in a fresh project", async () => {
  const projectDir = await createTempProject("agsp-fresh-");

  try {
    const result = runCli(["init"], projectDir);
    assert.equal(result.status, 0);

    const hasAgent = await pathExists(join(projectDir, ".agent", "AGENTS.md"));
    assert.equal(hasAgent, true);
  } finally {
    await rm(projectDir, { recursive: true, force: true });
  }
});

test("init fails when .agent exists without --force", async () => {
  const projectDir = await createTempProject("agsp-existing-");

  try {
    await mkdir(join(projectDir, ".agent"), { recursive: true });

    const result = runCli(["init"], projectDir);
    assert.equal(result.status, 1);
    assert.match(result.stderr, /already exists/i);
    assert.match(result.stderr, /--force/i);
  } finally {
    await rm(projectDir, { recursive: true, force: true });
  }
});

test("init replaces .agent with --force", async () => {
  const projectDir = await createTempProject("agsp-force-");

  try {
    await mkdir(join(projectDir, ".agent"), { recursive: true });

    const result = runCli(["init", "--force"], projectDir);
    assert.equal(result.status, 0);

    const hasTemplate = await pathExists(join(projectDir, ".agent", "AGENTS.md"));
    assert.equal(hasTemplate, true);
  } finally {
    await rm(projectDir, { recursive: true, force: true });
  }
});

test("init with -f short flag works", async () => {
  const projectDir = await createTempProject("agsp-short-f-");

  try {
    await mkdir(join(projectDir, ".agent"), { recursive: true });

    const result = runCli(["init", "-f"], projectDir);
    assert.equal(result.status, 0);
    assert.match(result.stdout, /Initialized/);
  } finally {
    await rm(projectDir, { recursive: true, force: true });
  }
});

test("init with target directory argument", async () => {
  const parentDir = await createTempProject("agsp-parent-");
  const targetDir = join(parentDir, "myproject");
  await mkdir(targetDir);

  try {
    const result = runCli(["init", targetDir], parentDir);
    assert.equal(result.status, 0);

    const hasAgent = await pathExists(join(targetDir, ".agent", "AGENTS.md"));
    assert.equal(hasAgent, true);
  } finally {
    await rm(parentDir, { recursive: true, force: true });
  }
});

test("init fails when target directory does not exist", async () => {
  const result = runCli(["init", "/tmp/nonexistent-dir-agsp-12345"]);
  assert.equal(result.status, 1);
  assert.match(result.stderr, /does not exist/i);
});

test("init with unknown option fails", async () => {
  const projectDir = await createTempProject("agsp-unknown-");

  try {
    const result = runCli(["init", "--bogus"], projectDir);
    assert.equal(result.status, 1);
    assert.match(result.stderr, /Unknown option/);
  } finally {
    await rm(projectDir, { recursive: true, force: true });
  }
});

test("init --dry-run previews files without copying", async () => {
  const projectDir = await createTempProject("agsp-dryrun-");

  try {
    const result = runCli(["init", "--dry-run"], projectDir);
    assert.equal(result.status, 0);
    assert.match(result.stdout, /Dry run/);
    assert.match(result.stdout, /AGENTS\.md/);

    const hasAgent = await pathExists(join(projectDir, ".agent"));
    assert.equal(hasAgent, false, ".agent should not be created during dry run");
  } finally {
    await rm(projectDir, { recursive: true, force: true });
  }
});

test("init --force --backup creates backup before replacing", async () => {
  const projectDir = await createTempProject("agsp-backup-");

  try {
    // Create initial .agent with a custom file
    await mkdir(join(projectDir, ".agent"), { recursive: true });
    await writeFile(join(projectDir, ".agent", "custom.txt"), "my custom config");

    const result = runCli(["init", "--force", "--backup"], projectDir);
    assert.equal(result.status, 0);
    assert.match(result.stdout, /Backed up/);

    // New .agent should exist
    const hasAgent = await pathExists(join(projectDir, ".agent", "AGENTS.md"));
    assert.equal(hasAgent, true);

    // Backup dir should exist
    const entries = await readdir(projectDir);
    const backupDirs = entries.filter((e) => e.startsWith(".agent-backup-"));
    assert.equal(backupDirs.length, 1, "Should have exactly one backup directory");

    // Backup should contain the custom file
    const hasCustom = await pathExists(join(projectDir, backupDirs[0], "custom.txt"));
    assert.equal(hasCustom, true, "Backup should preserve custom files");
  } finally {
    await rm(projectDir, { recursive: true, force: true });
  }
});
