/**
 * engine-types.test.mjs — leaf-node SF engine contract tests.
 *
 * Purpose: pin typed PDD, run-control, and gate result contracts before other
 * engine modules consume them.
 */
import { describe, expect, test } from "vitest";

import {
	buildGateResult,
	isGateResult,
	scoreRunControlPolicy,
	validatePDDFields,
	validateRunControlPolicy,
} from "../engine-types.js";

const validPDDFields = {
	purpose: "Keep autonomous work tied to a production purpose.",
	consumer: "autonomous dispatch",
	contract: "typed purpose fields",
	failureBoundary: "manual attention when fields are missing",
	evidence: "unit test",
	invariants: ["required fields are present"],
};

const validRunControlPolicy = {
	confidence: 0.8,
	risk: "low",
	reversibility: 0.9,
	blastRadius: "slice",
	cost: 12,
	legal: false,
	customerImpact: "internal",
};

describe("validatePDDFields", () => {
	for (const field of [
		"purpose",
		"consumer",
		"contract",
		"failureBoundary",
		"evidence",
		"invariants",
	]) {
		test(`validatePDDFields_when_${field}_missing_rejects_with_missing_field`, () => {
			const input = { ...validPDDFields };
			delete input[field];

			const result = validatePDDFields(input);

			expect(result.ok).toBe(false);
			expect(result.missing).toEqual([field]);
		});
	}

	test("validatePDDFields_when_full_valid_input_accepts", () => {
		const result = validatePDDFields({
			...validPDDFields,
			nonGoals: "avoid refactoring consumers",
			assumptions: ["leaf-node module stays import-free"],
		});

		expect(result).toEqual({ ok: true, missing: [], errors: [] });
	});
});

describe("validateRunControlPolicy", () => {
	test("validateRunControlPolicy_when_numbers_out_of_range_rejects", () => {
		const result = validateRunControlPolicy({
			...validRunControlPolicy,
			confidence: 1.5,
			reversibility: -0.1,
		});

		expect(result.ok).toBe(false);
		expect(result.errors).toContain(
			"confidence must be a number between 0 and 1",
		);
		expect(result.errors).toContain(
			"reversibility must be a number between 0 and 1",
		);
	});

	test("validateRunControlPolicy_when_enums_invalid_rejects", () => {
		const result = validateRunControlPolicy({
			...validRunControlPolicy,
			risk: "severe",
			blastRadius: "planet",
			customerImpact: "external",
		});

		expect(result.ok).toBe(false);
		expect(result.errors).toContain(
			"risk must be a number between 0 and 1 or low|medium|high|critical",
		);
		expect(result.errors).toContain(
			"blastRadius must be unit|slice|milestone|project|org",
		);
		expect(result.errors).toContain(
			"customerImpact must be none|internal|customer-visible|customer-critical",
		);
	});

	test("validateRunControlPolicy_when_full_valid_input_accepts", () => {
		const result = validateRunControlPolicy(validRunControlPolicy);

		expect(result).toEqual({ ok: true, errors: [] });
	});
});

describe("scoreRunControlPolicy", () => {
	test("scoreRunControlPolicy_when_fixed_input_is_deterministic_in_unit_interval", () => {
		const first = scoreRunControlPolicy(validRunControlPolicy);
		const second = scoreRunControlPolicy(validRunControlPolicy);

		expect(first).toBe(second);
		expect(first).toBeGreaterThanOrEqual(0);
		expect(first).toBeLessThanOrEqual(1);
	});
});

describe("GateResult contracts", () => {
	test("isGateResult_when_valid_gate_result_returns_true", () => {
		const result = buildGateResult("fail", {
			failureClass: "policy",
			rationale: "Risk exceeds autonomous threshold.",
			findings: "customer-visible blast radius",
			recommendation: "request manual attention",
		});

		expect(isGateResult(result)).toBe(true);
	});

	test("isGateResult_when_malformed_returns_false", () => {
		expect(isGateResult(null)).toBe(false);
		expect(isGateResult({ outcome: "pass" })).toBe(false);
		expect(isGateResult({ outcome: "done", rationale: "ok" })).toBe(false);
	});

	test("buildGateResult_when_valid_input_creates_correct_shape", () => {
		const result = buildGateResult("pass", {
			rationale: "Verification passed.",
		});

		expect(result).toEqual({
			outcome: "pass",
			failureClass: "unknown",
			rationale: "Verification passed.",
		});
	});

	test("buildGateResult_when_outcome_invalid_throws", () => {
		expect(() =>
			buildGateResult("blocked", { rationale: "Unsupported outcome." }),
		).toThrow(/invalid gate outcome/);
	});
});
