import assert from "node:assert/strict";
import {
	existsSync,
	mkdtempSync,
	readFileSync,
	rmSync,
	writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { test } from 'vitest';

import {
	approveProductionMutationWithLlmPolicy,
	assessLlmProductionMutationApproval,
	ensureProductionMutationApprovalTemplate,
	type ProductionMutationApproval,
	type ProductionMutationUnit,
	productionMutationApprovalPath,
	readProductionMutationApprovalStatus,
	validateProductionMutationApproval,
} from "../production-mutation-approval.ts";

function tempBase(): string {
	return mkdtempSync(join(tmpdir(), "sf-production-approval-"));
}

const unit: ProductionMutationUnit = {
	milestoneId: "M004",
	sliceId: "S03",
	taskId: "T01",
	taskTitle: "End-to-End R005 Smoke Test",
	taskText: [
		"Execute smoke test against production NixOS Hetzner infrastructure.",
		"POST to /action/unified-failover with test server_id and vm_names.",
		"Verify command row was created with status=pending.",
		"Verify gateway routing table is unchanged and no active_dr updates occur.",
		"NOTE: Do NOT trigger actual failover on real VMs.",
	].join("\n"),
};

test("production mutation approval writes a pending template once", () => {
	const basePath = tempBase();
	try {
		const first = ensureProductionMutationApprovalTemplate(basePath, unit);
		assert.equal(first.created, true);
		assert.equal(existsSync(first.path), true);
		assert.equal(first.path, productionMutationApprovalPath(basePath, unit));

		const parsed = JSON.parse(
			readFileSync(first.path, "utf-8"),
		) as ProductionMutationApproval;
		assert.equal(parsed.schemaVersion, 1);
		assert.equal(parsed.unitId, "M004/S03/T01");
		assert.equal(parsed.status, "pending");
		assert.equal(parsed.approval.approved, false);
		assert.equal(parsed.approval.approverType, "");
		assert.deepEqual(parsed.approval.safeVmNames, []);
		assert.deepEqual(parsed.approval.preMutationChecks, []);

		const second = ensureProductionMutationApprovalTemplate(basePath, unit);
		assert.equal(second.created, false);
		assert.equal(second.path, first.path);
	} finally {
		rmSync(basePath, { recursive: true, force: true });
	}
});

test("production mutation approval rejects incomplete approval files", () => {
	const result = validateProductionMutationApproval(
		{
			schemaVersion: 1,
			unitId: "M004/S03/T01",
			status: "pending",
			risk: "production-unified-failover-post",
			approval: {
				approved: true,
				approvedBy: "",
				approverType: "",
				approvedAt: "",
				safeServerId: "",
				safeVmNames: [],
				targetSelectionPlan: "",
				preMutationChecks: [],
				cleanupPlan: "",
				rollbackPlan: "",
			},
		},
		unit,
	);

	assert.equal(result.approved, false);
	assert.ok(result.reasons.includes("status must be approved"));
	assert.ok(result.reasons.includes("approval.approvedBy is required"));
	assert.ok(
		result.reasons.includes(
			"approval must include safeServerId and safeVmNames, or targetSelectionPlan",
		),
	);
});

test("production mutation approval accepts complete explicit approval", () => {
	const basePath = tempBase();
	try {
		const path = productionMutationApprovalPath(basePath, unit);
		const approval: ProductionMutationApproval = {
			schemaVersion: 1,
			unitId: "M004/S03/T01",
			status: "approved",
			risk: "production-unified-failover-post",
			task: {
				title: unit.taskTitle,
				text: unit.taskText,
			},
			approval: {
				approved: true,
				approvedBy: "operator@example.com",
				approverType: "human",
				approvedAt: "2026-04-30T10:00:00.000Z",
				safeServerId: "test-server-id",
				safeVmNames: ["pilot-smoke-vm"],
				targetSelectionPlan: "",
				preMutationChecks: ["Confirm target is non-customer-impacting."],
				cleanupPlan: "Delete the test pending command after verification.",
				rollbackPlan: "Abort the test failover and restore the previous route.",
				notes: "Smoke target is non-customer-impacting.",
			},
			instructions: [],
		};
		ensureProductionMutationApprovalTemplate(basePath, unit);
		writeFileSync(path, JSON.stringify(approval, null, 2) + "\n", "utf-8");

		const status = readProductionMutationApprovalStatus(basePath, unit);
		assert.equal(status.approved, true);
		assert.deepEqual(status.reasons, []);
	} finally {
		rmSync(basePath, { recursive: true, force: true });
	}
});

test("production mutation approval auto-fills LLM approval for pending-command-only smoke tests", () => {
	const basePath = tempBase();
	try {
		const approvedAt = new Date("2026-04-30T10:05:00.000Z");
		const result = approveProductionMutationWithLlmPolicy(
			basePath,
			unit,
			approvedAt,
		);
		assert.equal(result.approved, true);
		assert.equal(result.wrote, true);
		assert.deepEqual(result.reasons, []);

		const status = readProductionMutationApprovalStatus(basePath, unit);
		assert.equal(status.approved, true);

		const parsed = JSON.parse(
			readFileSync(result.path, "utf-8"),
		) as ProductionMutationApproval;
		assert.equal(parsed.status, "approved");
		assert.equal(parsed.approval.approverType, "llm");
		assert.equal(
			parsed.approval.approvedBy,
			"sf-llm:auto-production-mutation-policy",
		);
		assert.equal(parsed.approval.approvedAt, "2026-04-30T10:05:00.000Z");
		assert.ok(parsed.approval.targetSelectionPlan.length > 0);
		assert.ok(parsed.approval.preMutationChecks.length >= 3);
	} finally {
		rmSync(basePath, { recursive: true, force: true });
	}
});

test("production mutation approval does not auto-approve real failover tasks", () => {
	const realFailoverUnit: ProductionMutationUnit = {
		...unit,
		taskText:
			"POST to /action/unified-failover against production and verify the VM fails over.",
	};

	const assessment = assessLlmProductionMutationApproval(realFailoverUnit);
	assert.equal(assessment.approved, false);
	assert.ok(
		assessment.reasons.includes(
			"task is not constrained to pending command verification",
		),
	);
	assert.ok(
		assessment.reasons.includes(
			"task does not explicitly forbid actual failover",
		),
	);
});
