/**
 * Tests for UOK gate type enum validation (ADR-0075 §Gate Contract).
 *
 * Purpose: ensure that UOK_GATE_TYPES and isValidGateType are canonical and
 * that UokGateRunner.register() rejects gates with types outside the 5-value
 * enum at registration time, so slippage like type:"artifact" cannot recur.
 *
 * Consumer: CI gate and anyone registering UOK gates.
 */
import assert from "node:assert/strict";
import { test } from "vitest";
import { isValidGateType, UOK_GATE_TYPES } from "../engine-types.js";
import { UokGateRunner } from "../uok/gate-runner.js";

// ─── UOK_GATE_TYPES constant ──────────────────────────────────────────────

test("UOK_GATE_TYPES contains exactly the 5 ADR-0075 values", () => {
	assert.deepEqual([...UOK_GATE_TYPES].sort(), [
		"chaos",
		"learning",
		"policy",
		"security",
		"verification",
	]);
});

test("UOK_GATE_TYPES is frozen — mutation throws in strict mode", () => {
	assert.throws(() => {
		UOK_GATE_TYPES.push("artifact");
	}, TypeError);
});

// ─── isValidGateType unit tests ───────────────────────────────────────────

test("isValidGateType: each of the 5 valid types returns true", () => {
	for (const t of ["security", "policy", "verification", "learning", "chaos"]) {
		assert.equal(isValidGateType(t), true, `expected true for "${t}"`);
	}
});

test('isValidGateType: "artifact" returns false', () => {
	assert.equal(isValidGateType("artifact"), false);
});

test("isValidGateType: undefined returns false", () => {
	assert.equal(isValidGateType(undefined), false);
});

test("isValidGateType: empty string returns false", () => {
	assert.equal(isValidGateType(""), false);
});

test("isValidGateType: non-string types return false", () => {
	assert.equal(isValidGateType(null), false);
	assert.equal(isValidGateType(42), false);
	assert.equal(isValidGateType({}), false);
});

// ─── UokGateRunner.register() integration ────────────────────────────────

function makeGate(overrides = {}) {
	return {
		id: "test-gate",
		type: "verification",
		execute: async () => ({ outcome: "pass", rationale: "ok" }),
		...overrides,
	};
}

test('register: gate with type "verification" succeeds', () => {
	const runner = new UokGateRunner();
	assert.doesNotThrow(() =>
		runner.register(makeGate({ type: "verification" })),
	);
	assert.equal(runner.list().length, 1);
});

test("register: each of the 5 valid gate types is accepted", () => {
	for (const type of [
		"security",
		"policy",
		"verification",
		"learning",
		"chaos",
	]) {
		const runner = new UokGateRunner();
		assert.doesNotThrow(
			() => runner.register(makeGate({ id: `gate-${type}`, type })),
			`type "${type}" should be accepted`,
		);
	}
});

test('register: gate with type "artifact" throws TypeError naming gate id, bad type, and ADR-0075', () => {
	const runner = new UokGateRunner();
	assert.throws(
		() => runner.register(makeGate({ id: "my-gate", type: "artifact" })),
		(err) => {
			assert.ok(err instanceof TypeError, "must be TypeError");
			assert.ok(
				err.message.includes("my-gate"),
				`message must include gate id — got: ${err.message}`,
			);
			assert.ok(
				err.message.includes("artifact"),
				`message must include bad type — got: ${err.message}`,
			);
			assert.ok(
				err.message.includes("ADR-0075"),
				`message must reference ADR-0075 — got: ${err.message}`,
			);
			return true;
		},
	);
});

test("register: gate with type undefined throws (validateGate catches it)", () => {
	const runner = new UokGateRunner();
	// validateGate rejects undefined type before isValidGateType runs — any Error
	assert.throws(() => runner.register(makeGate({ type: undefined })));
});

test('register: gate with type "" (empty string) throws', () => {
	const runner = new UokGateRunner();
	// validateGate catches empty string before isValidGateType, so any Error
	assert.throws(() => runner.register(makeGate({ type: "" })));
});
