import assert from "node:assert/strict";
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, it } from "vitest";
import {
	checkCrossSliceConsistency,
	checkMilestoneIntegrity,
	runPostExecutionChecks,
} from "../post-execution-checks.js";

function makeTempDir(prefix) {
	return mkdtempSync(join(tmpdir(), prefix));
}

function cleanup(dir) {
	try {
		rmSync(dir, { recursive: true, force: true });
	} catch {}
}

function writeMilestoneFile(base, milestoneId, fileName, content) {
	const dir = join(base, ".sf", "milestones", milestoneId);
	mkdirSync(dir, { recursive: true });
	writeFileSync(join(dir, fileName), content);
}

function writeSliceFile(base, milestoneId, sliceId, fileName, content) {
	const dir = join(base, ".sf", "milestones", milestoneId, "slices", sliceId);
	mkdirSync(dir, { recursive: true });
	writeFileSync(join(dir, fileName), content);
}

// ─── checkCrossSliceConsistency ────────────────────────────────────────────

describe("checkCrossSliceConsistency", () => {
	it("passes_when_no_other_slice_touches_same_files", () => {
		const taskRow = {
			milestone_id: "M001",
			slice_id: "S03",
			id: "T01",
			key_files: ["src/a.ts", "src/b.ts"],
		};
		const allSliceTasks = [
			{
				milestone_id: "M001",
				slice_id: "S01",
				id: "T01",
				key_files: ["src/c.ts"],
			},
			{
				milestone_id: "M001",
				slice_id: "S02",
				id: "T01",
				key_files: ["src/d.ts"],
			},
		];
		const results = checkCrossSliceConsistency(taskRow, allSliceTasks, "/tmp");
		assert.equal(results.length, 1);
		assert.equal(results[0].passed, true);
		assert.equal(results[0].category, "cross-slice");
	});

	it("warns_when_another_slice_has_same_key_file", () => {
		const taskRow = {
			milestone_id: "M001",
			slice_id: "S03",
			id: "T01",
			key_files: ["src/shared.ts", "src/b.ts"],
		};
		const allSliceTasks = [
			{
				milestone_id: "M001",
				slice_id: "S01",
				id: "T01",
				key_files: ["src/shared.ts"],
			},
			{
				milestone_id: "M001",
				slice_id: "S02",
				id: "T01",
				key_files: ["src/d.ts"],
			},
		];
		const results = checkCrossSliceConsistency(taskRow, allSliceTasks, "/tmp");
		assert.equal(results.length, 1);
		assert.equal(results[0].passed, false);
		assert.equal(results[0].blocking, false);
		assert.equal(results[0].category, "cross-slice");
		assert.ok(results[0].message.includes("src/shared.ts"));
		assert.ok(results[0].message.includes("S01"));
	});

	it("ignores_tasks_in_same_slice", () => {
		const taskRow = {
			milestone_id: "M001",
			slice_id: "S03",
			id: "T02",
			key_files: ["src/shared.ts"],
		};
		const allSliceTasks = [
			{
				milestone_id: "M001",
				slice_id: "S03",
				id: "T01",
				key_files: ["src/shared.ts"],
			},
		];
		const results = checkCrossSliceConsistency(taskRow, allSliceTasks, "/tmp");
		assert.equal(results.length, 1);
		assert.equal(results[0].passed, true);
	});

	it("handles_multiple_conflicts", () => {
		const taskRow = {
			milestone_id: "M001",
			slice_id: "S03",
			id: "T01",
			key_files: ["src/a.ts", "src/b.ts"],
		};
		const allSliceTasks = [
			{
				milestone_id: "M001",
				slice_id: "S01",
				id: "T01",
				key_files: ["src/a.ts"],
			},
			{
				milestone_id: "M001",
				slice_id: "S02",
				id: "T01",
				key_files: ["src/b.ts"],
			},
		];
		const results = checkCrossSliceConsistency(taskRow, allSliceTasks, "/tmp");
		assert.equal(results.length, 2);
		assert.equal(results[0].passed, false);
		assert.equal(results[1].passed, false);
	});
});

// ─── checkMilestoneIntegrity ───────────────────────────────────────────────

describe("checkMilestoneIntegrity", () => {
	it("passes_when_all_complete_slices_have_summaries", () => {
		const base = makeTempDir("sf-pec-mi-");
		try {
			writeMilestoneFile(
				base,
				"M001",
				"M001-ROADMAP.md",
				[
					"# M001: Test",
					"",
					"## Slice Overview",
					"",
					"| ID | Slice | Risk | Depends | Done | After this |",
					"|----|-------|------|---------|------|------------|",
					"| S01 | S01 | low | — | ✅ | Done. |",
					"| S02 | S02 | low | — | ⬜ | Pending. |",
					"",
				].join("\n"),
			);
			writeSliceFile(base, "M001", "S01", "S01-SUMMARY.md", "# S01 Summary\n");
			writeFileSync(
				join(base, ".sf", "REQUIREMENTS.md"),
				[
					"## Active",
					"",
					"### R001 — Test",
					"- Class: functional",
					"- Description: test",
					"- Why: test",
					"- Source: test",
					"- Primary owner: M002",
					"",
				].join("\n"),
			);

			const results = checkMilestoneIntegrity("M001", base);
			assert.ok(
				results.every((r) => r.passed),
				`Expected all checks to pass, got: ${JSON.stringify(results)}`,
			);
		} finally {
			cleanup(base);
		}
	});

	it("warns_when_complete_slice_lacks_summary", () => {
		const base = makeTempDir("sf-pec-mi-");
		try {
			writeMilestoneFile(
				base,
				"M001",
				"M001-ROADMAP.md",
				[
					"# M001: Test",
					"",
					"## Slice Overview",
					"",
					"| ID | Slice | Risk | Depends | Done | After this |",
					"|----|-------|------|---------|------|------------|",
					"| S01 | S01 | low | — | ✅ | Done. |",
					"",
				].join("\n"),
			);
			// Intentionally NOT writing S01-SUMMARY.md
			writeFileSync(
				join(base, ".sf", "REQUIREMENTS.md"),
				["## Active", "", "### R001 — Test", "- Primary owner: M001", ""].join(
					"\n",
				),
			);

			const results = checkMilestoneIntegrity("M001", base);
			const missing = results.find((r) => r.message.includes("SUMMARY"));
			assert.ok(
				missing,
				`Expected missing summary warning, got: ${JSON.stringify(results)}`,
			);
			assert.equal(missing.passed, false);
			assert.equal(missing.blocking, false);
		} finally {
			cleanup(base);
		}
	});

	it("warns_when_active_requirement_belongs_to_milestone", () => {
		const base = makeTempDir("sf-pec-mi-");
		try {
			writeMilestoneFile(
				base,
				"M001",
				"M001-ROADMAP.md",
				[
					"# M001: Test",
					"",
					"## Slice Overview",
					"",
					"| ID | Slice | Risk | Depends | Done | After this |",
					"|----|-------|------|---------|------|------------|",
					"| S01 | S01 | low | — | ✅ | Done. |",
					"",
				].join("\n"),
			);
			writeSliceFile(base, "M001", "S01", "S01-SUMMARY.md", "# S01 Summary\n");
			writeFileSync(
				join(base, ".sf", "REQUIREMENTS.md"),
				[
					"## Active",
					"",
					"### R001 — Test",
					"- Class: functional",
					"- Description: test",
					"- Why: test",
					"- Source: test",
					"- Primary owner: M001",
					"",
				].join("\n"),
			);

			const results = checkMilestoneIntegrity("M001", base);
			const reqWarning = results.find((r) => r.message.includes("R001"));
			assert.ok(
				reqWarning,
				`Expected active requirement warning, got: ${JSON.stringify(results)}`,
			);
			assert.equal(reqWarning.passed, false);
			assert.equal(reqWarning.blocking, false);
		} finally {
			cleanup(base);
		}
	});

	it("warns_when_roadmap_missing", () => {
		const base = makeTempDir("sf-pec-mi-");
		try {
			mkdirSync(join(base, ".sf"), { recursive: true });
			writeFileSync(join(base, ".sf", "REQUIREMENTS.md"), "## Active\n\n");
			const results = checkMilestoneIntegrity("M001", base);
			const missing = results.find((r) => r.message.includes("ROADMAP"));
			assert.ok(
				missing,
				`Expected missing roadmap warning, got: ${JSON.stringify(results)}`,
			);
			assert.equal(missing.passed, false);
			assert.equal(missing.blocking, false);
		} finally {
			cleanup(base);
		}
	});
});

// ─── runPostExecutionChecks ────────────────────────────────────────────────

describe("runPostExecutionChecks", () => {
	it("includes_cross_slice_and_milestone_checks_when_args_provided", () => {
		const base = makeTempDir("sf-pec-rpec-");
		try {
			writeMilestoneFile(
				base,
				"M001",
				"M001-ROADMAP.md",
				[
					"# M001: Test",
					"",
					"## Slice Overview",
					"",
					"| ID | Slice | Risk | Depends | Done | After this |",
					"|----|-------|------|---------|------|------------|",
					"| S01 | S01 | low | — | ✅ | Done. |",
					"| S02 | S02 | low | — | ⬜ | Pending. |",
					"",
				].join("\n"),
			);
			writeSliceFile(base, "M001", "S01", "S01-SUMMARY.md", "# S01\n");
			writeFileSync(join(base, ".sf", "REQUIREMENTS.md"), "## Active\n\n");

			const taskRow = {
				milestone_id: "M001",
				slice_id: "S02",
				id: "T01",
				key_files: ["src/a.ts"],
			};
			const allSliceTasks = [
				{
					milestone_id: "M001",
					slice_id: "S01",
					id: "T01",
					key_files: ["src/a.ts"],
				},
			];

			const result = runPostExecutionChecks(
				taskRow,
				[],
				base,
				"M001",
				allSliceTasks,
			);
			const categories = result.checks.map((c) => c.category);
			assert.ok(
				categories.includes("cross-slice"),
				`Expected cross-slice check, got categories: ${categories}`,
			);
			assert.ok(
				categories.includes("milestone"),
				`Expected milestone check, got categories: ${categories}`,
			);
		} finally {
			cleanup(base);
		}
	});

	it("skips_new_checks_when_optional_args_omitted", () => {
		const taskRow = {
			milestone_id: "M001",
			slice_id: "S01",
			id: "T01",
			key_files: [],
		};
		const result = runPostExecutionChecks(taskRow, [], "/tmp");
		const categories = result.checks.map((c) => c.category);
		assert.ok(!categories.includes("cross-slice"));
		assert.ok(!categories.includes("milestone"));
	});
});
