/**
 * uok-quality-gates-writer.test.mjs — verify insertGateRow / saveGateResult
 * round-trip canonical uokContext into the quality_gates table after the
 * schema v66 migration (UOK control-plane plan, slice 2).
 *
 * Asserts the writer-side of the slice: a caller that supplies a valid
 * uokContext from buildUokRunContext gets schema-v2 columns populated
 * on the DB row, which is the prerequisite for the status uok classifier
 * to flip its verdict from "legacy" to "ok"/"stale"/"incomplete".
 */
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import {
	closeDatabase,
	getDatabase,
	insertGateRow,
	openDatabase,
	saveGateResult,
} from "../sf-db.js";
import { buildUokRunContext } from "../uok/run-context.js";

let projectDir;
let dbPath;

function seedMilestoneSlice(db, milestoneId, sliceId) {
	// quality_gates has a FOREIGN KEY into slices(milestone_id, id), so the
	// parent milestone+slice rows have to exist before we insert a gate.
	// All other columns have defaults; minimum required is the PK shape.
	db.prepare(
		`INSERT INTO milestones (id, title, created_at)
		 VALUES (?, ?, ?)`,
	).run(milestoneId, "Test milestone", "2026-05-14T12:00:00Z");
	db.prepare(
		`INSERT INTO slices (milestone_id, id, title, created_at)
		 VALUES (?, ?, ?, ?)`,
	).run(milestoneId, sliceId, "Test slice", "2026-05-14T12:00:00Z");
}

beforeEach(() => {
	projectDir = mkdtempSync(join(tmpdir(), "sf-uok-writer-test-"));
	mkdirSync(join(projectDir, ".sf"), { recursive: true });
	writeFileSync(
		join(projectDir, "package.json"),
		JSON.stringify({ name: "test-project" }),
	);
	dbPath = join(projectDir, ".sf", "sf.db");
	openDatabase(dbPath);
});

afterEach(() => {
	closeDatabase();
	rmSync(projectDir, { recursive: true, force: true });
});

describe("insertGateRow writes schema-v2 columns from uokContext", () => {
	test("populates surface / run_control / permission_profile / trace_id / parent_trace", () => {
		const db = getDatabase();
		seedMilestoneSlice(db, "M001", "S01");
		const ctx = buildUokRunContext({
			surface: "headless",
			runControl: "supervised",
			permissionProfile: "high",
			traceId: "flow-abc",
			parentTrace: "flow-root",
		});
		insertGateRow({
			milestoneId: "M001",
			sliceId: "S01",
			gateId: "test-gate",
			scope: "slice",
			taskId: "T01",
			status: "pending",
			uokContext: ctx,
		});
		const row = db
			.prepare(
				`SELECT surface, run_control, permission_profile,
				        trace_id, parent_trace
				 FROM quality_gates WHERE gate_id = 'test-gate'`,
			)
			.get();
		expect(row).toBeTruthy();
		expect(row.surface).toBe("headless");
		expect(row.run_control).toBe("supervised");
		expect(row.permission_profile).toBe("high");
		expect(row.trace_id).toBe("flow-abc");
		expect(row.parent_trace).toBe("flow-root");
	});

	test("leaves columns NULL when uokContext is absent (legacy path)", () => {
		const db = getDatabase();
		seedMilestoneSlice(db, "M002", "S01");
		insertGateRow({
			milestoneId: "M002",
			sliceId: "S01",
			gateId: "legacy-gate",
			scope: "slice",
			taskId: "T01",
			status: "pending",
		});
		const row = db
			.prepare(
				`SELECT surface, run_control, permission_profile,
				        trace_id, parent_trace
				 FROM quality_gates WHERE gate_id = 'legacy-gate'`,
			)
			.get();
		expect(row.surface).toBe(null);
		expect(row.run_control).toBe(null);
		expect(row.permission_profile).toBe(null);
		expect(row.trace_id).toBe(null);
		expect(row.parent_trace).toBe(null);
	});

	test("leaves columns NULL when uokContext is malformed (invalid surface)", () => {
		const db = getDatabase();
		seedMilestoneSlice(db, "M003", "S01");
		insertGateRow({
			milestoneId: "M003",
			sliceId: "S01",
			gateId: "bad-ctx-gate",
			scope: "slice",
			taskId: "T01",
			status: "pending",
			// Bypass buildUokRunContext on purpose to test the adapter's
			// last-line-of-defense rejection path.
			uokContext: { surface: "rogue", runControl: "supervised" },
		});
		const row = db
			.prepare(
				`SELECT surface, run_control FROM quality_gates
				 WHERE gate_id = 'bad-ctx-gate'`,
			)
			.get();
		expect(row.surface).toBe(null);
		expect(row.run_control).toBe(null);
	});
});

describe("saveGateResult merges schema-v2 columns via COALESCE", () => {
	test("upgrades a legacy row to schema-v2 when uokContext supplied", () => {
		const db = getDatabase();
		seedMilestoneSlice(db, "M004", "S01");
		insertGateRow({
			milestoneId: "M004",
			sliceId: "S01",
			gateId: "upgrade-gate",
			scope: "slice",
			taskId: "T01",
			status: "pending",
		});
		const ctx = buildUokRunContext({
			surface: "autonomous",
			runControl: "autonomous",
			permissionProfile: "high",
			traceId: "flow-upgrade",
		});
		saveGateResult({
			milestoneId: "M004",
			sliceId: "S01",
			gateId: "upgrade-gate",
			taskId: "T01",
			verdict: "pass",
			rationale: "ok",
			findings: "",
			uokContext: ctx,
		});
		const row = db
			.prepare(
				`SELECT surface, run_control, permission_profile, trace_id, verdict
				 FROM quality_gates WHERE gate_id = 'upgrade-gate'`,
			)
			.get();
		expect(row.verdict).toBe("pass");
		expect(row.surface).toBe("autonomous");
		expect(row.run_control).toBe("autonomous");
		expect(row.permission_profile).toBe("high");
		expect(row.trace_id).toBe("flow-upgrade");
	});

	test("does NOT overwrite existing schema-v2 metadata when ctx is absent", () => {
		const db = getDatabase();
		seedMilestoneSlice(db, "M005", "S01");
		insertGateRow({
			milestoneId: "M005",
			sliceId: "S01",
			gateId: "preserve-gate",
			scope: "slice",
			taskId: "T01",
			status: "pending",
			uokContext: buildUokRunContext({
				surface: "headless",
				runControl: "supervised",
				permissionProfile: "medium",
				traceId: "flow-first",
			}),
		});
		// Second write has no ctx — COALESCE should keep the original
		// values, not blank them.
		saveGateResult({
			milestoneId: "M005",
			sliceId: "S01",
			gateId: "preserve-gate",
			taskId: "T01",
			verdict: "pass",
			rationale: "",
			findings: "",
		});
		const row = db
			.prepare(
				`SELECT surface, run_control, permission_profile, trace_id
				 FROM quality_gates WHERE gate_id = 'preserve-gate'`,
			)
			.get();
		expect(row.surface).toBe("headless");
		expect(row.run_control).toBe("supervised");
		expect(row.permission_profile).toBe("medium");
		expect(row.trace_id).toBe("flow-first");
	});
});
