/**
 * doctor-db-projection-drift.test.mjs - DB/projection drift diagnostics.
 *
 * Purpose: prove doctor reports generated-file drift without treating JSON or
 * Markdown projections as executable runtime truth.
 */
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 { afterEach, test } from "vitest";
import { checkEngineHealth } from "../doctor-engine-checks.js";
import {
	closeDatabase,
	getMilestone,
	getMilestoneSlices,
	insertAssessment,
	insertMilestone,
	insertSlice,
	openDatabase,
} from "../sf-db.js";

const tmpDirs = [];

afterEach(() => {
	closeDatabase();
	while (tmpDirs.length > 0) {
		rmSync(tmpDirs.pop(), { recursive: true, force: true });
	}
});

test("checkEngineHealth_fix_when_roadmap_has_slices_but_db_shell_is_empty_imports_into_matching_db_milestone", async () => {
	const project = mkdtempSync(join(tmpdir(), "sf-db-roadmap-upgrade-"));
	tmpDirs.push(project);
	const milestoneDir = join(project, ".sf", "milestones", "M001");
	mkdirSync(milestoneDir, { recursive: true });
	openDatabase(join(project, ".sf", "sf.db"));
	insertMilestone({
		id: "M001-6377a4",
		title: "",
		status: "active",
	});
	writeFileSync(
		join(milestoneDir, "M001-ROADMAP.md"),
		[
			"# M001: Roadmap upgrade",
			"",
			"## Slices",
			"",
			"- [ ] **S01: Stabilize doctrine** `risk:high`",
			"- [ ] **S02: Autonomous reliability** `risk:medium` `depends:[S01]`",
			"",
		].join("\n"),
	);
	const issues = [];
	const fixesApplied = [];

	await checkEngineHealth(
		project,
		issues,
		fixesApplied,
		(code) => code === "db_missing_planning_rows_from_projection",
	);

	assert.equal(
		issues.some(
			(issue) => issue.code === "db_missing_planning_rows_from_projection",
		),
		true,
	);
	assert.deepEqual(
		getMilestoneSlices("M001-6377a4").map((slice) => [
			slice.id,
			slice.title,
			slice.risk,
			slice.sequence,
		]),
		[
			["S01", "Stabilize doctrine", "high", 1],
			["S02", "Autonomous reliability", "medium", 2],
		],
	);
	assert.equal(getMilestone("M001"), null);
	assert.match(fixesApplied.join("\n"), /upgraded \.sf planning artifacts/);
});

function makeProject() {
	const dir = mkdtempSync(join(tmpdir(), "sf-db-projection-drift-"));
	tmpDirs.push(dir);
	mkdirSync(join(dir, ".sf", "milestones", "M901"), { recursive: true });
	openDatabase(join(dir, ".sf", "sf.db"));
	insertMilestone({
		id: "M901",
		title: "Projection drift",
		status: "active",
	});
	insertSlice({
		milestoneId: "M901",
		id: "S01",
		title: "DB slice",
		status: "pending",
		sequence: 1,
	});
	return dir;
}

test("checkEngineHealth_when_roadmap_json_disagrees_with_db_reports_roadmap_drift_but_not_validation_drift", async () => {
	// db_projection_validation_drift was removed (#db-first-validation):
	// VALIDATION.md is a generated projection; drift is structurally impossible
	// after handleValidateMilestone always regenerates from DB.
	// Only db_projection_roadmap_drift is still checked (ROADMAP.json projection).
	const project = makeProject();
	const milestoneDir = join(project, ".sf", "milestones", "M901");
	const validationPath = join(milestoneDir, "M901-VALIDATION.md");
	writeFileSync(
		join(milestoneDir, "M901-ROADMAP.json"),
		`${JSON.stringify(
			{
				origin: "db-projection",
				milestone: { id: "M901", title: "Projection drift" },
				slices: [{ id: "S99", status: "complete", sequence: 99 }],
			},
			null,
			2,
		)}\n`,
	);
	// Even with a stale VALIDATION.md file on disk, no drift warning fires.
	writeFileSync(
		validationPath,
		["---", "verdict: needs-remediation", "---", "", "# stale"].join("\n"),
	);
	insertAssessment({
		path: validationPath,
		milestoneId: "M901",
		scope: "milestone-validation",
		status: "pass",
		fullContent: ["---", "verdict: pass", "---", "", "# db"].join("\n"),
	});
	const issues = [];

	await checkEngineHealth(project, issues, [], () => false);

	assert.equal(
		issues.some((issue) => issue.code === "db_projection_roadmap_drift"),
		true,
		"roadmap drift check should still fire",
	);
	assert.equal(
		issues.some((issue) => issue.code === "db_projection_validation_drift"),
		false,
		"validation drift check is retired — VALIDATION.md is a generated projection",
	);
});
