/**
 * task-completion-drift.test.mjs - task completion status/prose drift contracts.
 *
 * Purpose: prove task completion prose cannot make a pending DB row eligible for
 * autonomous redispatch, and the sanctioned completion writer keeps canonical
 * task state fields aligned.
 */
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 {
	closeDatabase,
	completeTaskAtomic,
	getTask,
	insertMilestone,
	insertSlice,
	insertTask,
	listSelfFeedbackEntries,
	openDatabase,
	setTaskSummaryMd,
} from "../sf-db.js";
import { deriveStateFromDb } from "../state-db.js";

const tmpDirs = [];

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

function makeProject(prefix = "sf-task-completion-drift-") {
	const project = mkdtempSync(join(tmpdir(), prefix));
	tmpDirs.push(project);
	mkdirSync(join(project, ".sf", "milestones", "M011", "slices", "S07"), {
		recursive: true,
	});
	assert.equal(openDatabase(join(project, ".sf", "sf.db")), true);
	insertMilestone({ id: "M011", title: "Atomic completion", status: "active" });
	insertSlice({
		milestoneId: "M011",
		id: "S07",
		title: "Completion writer",
		status: "pending",
		sequence: 1,
	});
	return project;
}

test("deriveStateFromDb_when_pending_task_has_completion_evidence_quarantines_drift", async () => {
	const project = makeProject();
	writeFileSync(
		join(project, ".sf", "milestones", "M011", "slices", "S07", "S07-PLAN.md"),
		"# S07 plan\n",
	);
	insertTask({
		milestoneId: "M011",
		sliceId: "S07",
		id: "T02",
		title: "Drifted task",
		status: "pending",
		taskStatus: "todo",
		verificationStatus: "all_pass",
		fullSummaryMd: "---\ncompleted_at: 2026-05-17T07:41:35.913Z\n---",
	});

	const state = await deriveStateFromDb(project);

	assert.notEqual(state.activeTask?.id, "T02");
	assert.equal(state.phase, "blocked");
	const driftEntry = listSelfFeedbackEntries().find(
		(entry) => entry.kind === "status-completion-drift",
	);
	assert.ok(driftEntry);
	assert.equal(driftEntry.occurredIn.task, "T02");
});

test("completeTaskAtomic_when_completing_task_aligns_canonical_completion_fields", () => {
	makeProject();
	insertTask({
		milestoneId: "M011",
		sliceId: "S07",
		id: "T01",
		title: "Atomic task",
		status: "pending",
	});

	completeTaskAtomic("M011", "S07", "T01", {
		status: "complete",
		completedAt: "2026-05-17T08:00:00.000Z",
		narrative: "The task is complete.",
		verificationResult: "passed",
		summaryMd: "---\ncompleted_at: 2026-05-17T08:00:00.000Z\n---",
		keyFiles: ["src/resources/extensions/sf/sf-db/sf-db-tasks.js"],
		keyDecisions: ["Use one transaction for completion prose and status."],
	});

	const task = getTask("M011", "S07", "T01");
	assert.equal(task.status, "complete");
	assert.equal(task.completed_at, "2026-05-17T08:00:00.000Z");
	assert.equal(task.task_status, "done");
	assert.equal(task.verification_status, "all_pass");
	assert.equal(task.verification_result, "passed");
	assert.equal(task.narrative, "The task is complete.");
	assert.match(task.full_summary_md, /completed_at: 2026-05-17T08:00:00\.000Z/);
	assert.deepEqual(task.key_files, [
		"src/resources/extensions/sf/sf-db/sf-db-tasks.js",
	]);
	assert.deepEqual(task.key_decisions, [
		"Use one transaction for completion prose and status.",
	]);
});

test("setTaskSummaryMd_when_called_by_legacy_code_only_updates_completed_rows", () => {
	makeProject();
	insertTask({
		milestoneId: "M011",
		sliceId: "S07",
		id: "T03",
		title: "Completed legacy row",
		status: "complete",
	});
	insertTask({
		milestoneId: "M011",
		sliceId: "S07",
		id: "T04",
		title: "Pending legacy row",
		status: "pending",
	});

	setTaskSummaryMd("M011", "S07", "T03", "# complete summary\n");
	setTaskSummaryMd("M011", "S07", "T04", "# unsafe pending summary\n");

	assert.equal(
		getTask("M011", "S07", "T03").full_summary_md,
		"# complete summary\n",
	);
	assert.equal(getTask("M011", "S07", "T04").full_summary_md, "");
});
