/**
 * detector-gates-contract.test.mjs — detector-backed UOK gate contracts.
 *
 * Purpose: prove detector retrofits expose the canonical gate shape without
 * breaking the plain detector functions used by existing consumers.
 */
import { describe, expect, it } from "vitest";

import {
	artifactFlapGate,
	crashLoopGate,
	periodicDetectorSweepGate,
	productionPlateauGate,
	repeatedFeedbackKindGate,
	sameUnitLoopGate,
	staleLockGate,
	statusCompletionDriftGate,
	zeroProgressGate,
} from "../detectors/index.js";

const GATE_TYPES = new Set([
	"security",
	"policy",
	"verification",
	"learning",
	"chaos",
]);
const OUTCOMES = new Set(["pass", "fail", "manual-attention", "skip"]);

const gateCases = [
	{
		gate: sameUnitLoopGate,
		ctx: { unitId: "M001-S01-T01", recentDispatches: [] },
	},
	{
		gate: zeroProgressGate,
		ctx: {
			unitMetrics: { tool_calls: 0, elapsedMs: 0, fingerprint: "clean" },
			lastSnapshot: { tool_calls: 0, elapsedMs: 0, fingerprint: "clean" },
		},
	},
	{
		gate: repeatedFeedbackKindGate,
		ctx: { feedbackHistory: [] },
	},
	{
		gate: artifactFlapGate,
		ctx: { artifactHistory: [] },
	},
	{
		gate: staleLockGate,
		ctx: { lockInfo: [] },
	},
	{
		gate: crashLoopGate,
		ctx: {
			basePath: "/tmp/sf-no-ledger",
			currentResult: { ok: true, exitCode: 0, sourceHash: "clean" },
		},
	},
	{
		gate: statusCompletionDriftGate,
		ctx: { tasks: [], recentDispatches: [] },
	},
	{
		gate: productionPlateauGate,
		ctx: { unitMetrics: { totalCycles: 0, unchangedCycles: 0 } },
	},
	{
		gate: periodicDetectorSweepGate,
		ctx: {
			unitId: "M001-S01-T01",
			recentDispatches: [],
			unitMetrics: { tool_calls: 0, elapsedMs: 0, fingerprint: "clean" },
			sessionFingerprint: "clean",
			recentFeedback: [],
			artifactHistory: [],
			lockPaths: [],
			tasks: [],
			currentResult: { ok: true, exitCode: 0, sourceHash: "clean" },
		},
	},
];

describe("detector UOK gate contracts", () => {
	for (const { gate, ctx } of gateCases) {
		it(`${gate.id}_when_called_with_minimal_ctx_returns_gate_result`, async () => {
			expect(typeof gate.id).toBe("string");
			expect(gate.id.length).toBeGreaterThan(0);
			expect(GATE_TYPES.has(gate.type)).toBe(true);
			expect(gate.execute.constructor.name).toBe("AsyncFunction");

			const result = await gate.execute(ctx);

			expect(result).toEqual(expect.any(Object));
			expect(OUTCOMES.has(result.outcome)).toBe(true);
			expect(
				result.failureClass === null || typeof result.failureClass === "string",
			).toBe(true);
		});
	}
});
