/**
 * M010/S02 test — DispatchLayer 4D API contract.
 *
 * Pins the documented API surface:
 *   - Constructor requires basePath (string)
 *   - dispatch(opts) validates the 4D config + unitType/unitId
 *   - The single implemented cell (full/managed/inline/single) routes through
 *     runUnitInline; ineligible unit types within that cell surface
 *     runUnitInline's structured rejection.
 *   - All other cells return ok:false with exitCode:99 and a named owner.
 *   - implementedConfigKey() is exposed for callers.
 */
import { describe, expect, it } from "vitest";

import { DispatchLayer } from "../dispatch/dispatch-layer.js";

const BASE = "/tmp/sf-dispatch-test";

describe("M010/S02 — DispatchLayer 4D API", () => {
	it("constructor requires basePath", () => {
		expect(() => new DispatchLayer()).toThrow(/basePath required/);
		expect(() => new DispatchLayer(123)).toThrow(/basePath required/);
		expect(() => new DispatchLayer("")).toThrow(/basePath required/);
	});

	it("constructor succeeds with a string basePath", () => {
		const layer = new DispatchLayer(BASE);
		expect(layer.getDispatchCount()).toBe(0);
	});

	it("dispatch rejects bad isolation/coordination/scope/mode", async () => {
		const layer = new DispatchLayer(BASE);
		const bad = [
			{ isolation: "bogus", coordination: "managed", scope: "inline", mode: "single", unitType: "validate-milestone", unitId: "M006" },
			{ isolation: "full", coordination: "bogus", scope: "inline", mode: "single", unitType: "validate-milestone", unitId: "M006" },
			{ isolation: "full", coordination: "managed", scope: "bogus", mode: "single", unitType: "validate-milestone", unitId: "M006" },
			{ isolation: "full", coordination: "managed", scope: "inline", mode: "bogus", unitType: "validate-milestone", unitId: "M006" },
		];
		for (const opts of bad) {
			const r = await layer.dispatch(opts);
			expect(r.ok).toBe(false);
			expect(r.exitCode).toBe(1);
			expect(r.stderr).toMatch(/must be one of/);
		}
	});

	it("dispatch rejects missing unitType/unitId", async () => {
		const layer = new DispatchLayer(BASE);
		const a = await layer.dispatch({
			isolation: "full",
			coordination: "managed",
			scope: "inline",
			mode: "single",
		});
		expect(a.ok).toBe(false);
		expect(a.stderr).toMatch(/unitType required/);
		const b = await layer.dispatch({
			isolation: "full",
			coordination: "managed",
			scope: "inline",
			mode: "single",
			unitType: "validate-milestone",
		});
		expect(b.ok).toBe(false);
		expect(b.stderr).toMatch(/unitId required/);
	});

	it("dispatch with implemented config routes to runUnitInline (and surfaces its ineligibility error)", async () => {
		const layer = new DispatchLayer(BASE);
		// Use an ineligible unit type to keep the test offline (runUnitInline rejects without calling LLM).
		const r = await layer.dispatch({
			isolation: "full",
			coordination: "managed",
			scope: "inline",
			mode: "single",
			unitType: "execute-task",
			unitId: "M010/S02/T01",
		});
		expect(r.ok).toBe(false);
		// runUnitInline returns exitCode=2 for ineligible unit types — this proves
		// we're routing through it (the layer's own exitCode for not-implemented is 99).
		expect(r.exitCode).toBe(2);
		expect(r.stderr).toMatch(/not yet eligible for inline dispatch/i);
		expect(layer.getDispatchCount()).toBe(1);
	});

	it("dispatch with non-implemented config returns structured not-implemented error", async () => {
		const layer = new DispatchLayer(BASE);
		const r = await layer.dispatch({
			isolation: "full",
			coordination: "managed",
			scope: "milestone",
			mode: "parallel",
			unitType: "anything",
			unitId: "M001",
		});
		expect(r.ok).toBe(false);
		expect(r.exitCode).toBe(99);
		expect(r.stderr).toMatch(/not implemented/i);
		expect(r.stderr).toMatch(/parallel-orchestrator/i);
	});

	it("implementedConfigKey is exposed", () => {
		const key = DispatchLayer.implementedConfigKey();
		expect(key).toBe("full|managed|inline|single");
	});

	it("getDispatchCount increments per call", async () => {
		const layer = new DispatchLayer(BASE);
		await layer.dispatch({
			isolation: "full",
			coordination: "managed",
			scope: "inline",
			mode: "single",
			unitType: "execute-task",
			unitId: "M010/S02/T01",
		});
		await layer.dispatch({
			isolation: "constrained",
			coordination: "standalone",
			scope: "inline",
			mode: "single",
			unitType: "execute-task",
			unitId: "M010/S02/T01",
		});
		expect(layer.getDispatchCount()).toBe(2);
	});
});
