import { test, describe } from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
import { validatePrdForDecompose, runCli } from "./decompose.mjs";

function writePrd(dir, id, status) {
  const file = path.join(dir, `${id}.prd.md`);
  fs.writeFileSync(
    file,
    `---
id: ${id}
title: Test PRD
type: prd
status: ${status}
author: tester
created: 2026-05-13
---

body content
`,
  );
  return file;
}

function setupRepo() {
  const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "decompose-"));
  fs.mkdirSync(path.join(tmp, "prds"), { recursive: true });
  return tmp;
}

describe("validatePrdForDecompose", () => {
  test("accepts an approved PRD", () => {
    const tmp = setupRepo();
    try {
      const file = writePrd(path.join(tmp, "prds"), "test-1", "approved");
      const result = validatePrdForDecompose(file);
      assert.equal(result.frontmatter.status, "approved");
      assert.ok(result.text.includes("body content"));
    } finally {
      fs.rmSync(tmp, { recursive: true, force: true });
    }
  });

  test("rejects draft (must go through human review)", () => {
    const tmp = setupRepo();
    try {
      const file = writePrd(path.join(tmp, "prds"), "test-2", "draft");
      assert.throws(
        () => validatePrdForDecompose(file),
        /status is "draft".*Flip status to "approved"/,
      );
    } finally {
      fs.rmSync(tmp, { recursive: true, force: true });
    }
  });

  test("rejects in-review", () => {
    const tmp = setupRepo();
    try {
      const file = writePrd(path.join(tmp, "prds"), "test-3", "in-review");
      assert.throws(
        () => validatePrdForDecompose(file),
        /status is "in-review"/,
      );
    } finally {
      fs.rmSync(tmp, { recursive: true, force: true });
    }
  });

  test("rejects shipped (epic already exists)", () => {
    const tmp = setupRepo();
    try {
      const file = writePrd(path.join(tmp, "prds"), "test-4", "shipped");
      assert.throws(() => validatePrdForDecompose(file), /status is "shipped"/);
    } finally {
      fs.rmSync(tmp, { recursive: true, force: true });
    }
  });

  test("rejects unknown status", () => {
    const tmp = setupRepo();
    try {
      const file = writePrd(path.join(tmp, "prds"), "test-5", "cancelled");
      assert.throws(
        () => validatePrdForDecompose(file),
        /Unexpected PRD status "cancelled"/,
      );
    } finally {
      fs.rmSync(tmp, { recursive: true, force: true });
    }
  });

  test("rejects missing file", () => {
    assert.throws(
      () => validatePrdForDecompose("/nonexistent/path.prd.md"),
      /PRD file not found/,
    );
  });
});

describe("runCli (print mode)", () => {
  test("returns 1 + writes error when PRD id not found", async () => {
    const tmp = setupRepo();
    const errors = [];
    const origError = console.error;
    console.error = (m) => errors.push(m);
    try {
      const code = await runCli(["nonexistent-id"], {
        repoRoot: tmp,
        workRoot: tmp,
      });
      assert.equal(code, 1);
      assert.ok(errors.some((e) => /not found under docs\/work\/prds/.test(e)));
    } finally {
      console.error = origError;
      fs.rmSync(tmp, { recursive: true, force: true });
    }
  });

  test("returns 1 + writes error when PRD is draft", async () => {
    const tmp = setupRepo();
    const errors = [];
    const origError = console.error;
    console.error = (m) => errors.push(m);
    try {
      writePrd(path.join(tmp, "prds"), "draft-prd", "draft");
      const code = await runCli(["draft-prd"], {
        repoRoot: tmp,
        workRoot: tmp,
      });
      assert.equal(code, 1);
      assert.ok(errors.some((e) => /Cannot decompose/.test(e)));
    } finally {
      console.error = origError;
      fs.rmSync(tmp, { recursive: true, force: true });
    }
  });

  test("prints plan when PRD is approved + returns 0", async () => {
    const tmp = setupRepo();
    const logs = [];
    const origLog = console.log;
    console.log = (m) => logs.push(m);
    try {
      writePrd(path.join(tmp, "prds"), "approved-prd", "approved");
      const code = await runCli(["approved-prd"], {
        repoRoot: tmp,
        workRoot: tmp,
      });
      assert.equal(code, 0);
      const all = logs.join("\n");
      assert.match(all, /Decompose plan/);
      assert.match(all, /approved-prd/);
      assert.match(all, /eligible/);
    } finally {
      console.log = origLog;
      fs.rmSync(tmp, { recursive: true, force: true });
    }
  });
});
