/**
 * detector-same-unit-loop.test.mjs — same-unit dispatch-loop detector contracts.
 *
 * Purpose: prove R051 catches repeated low-progress redispatches while allowing
 * old, complete, or productive dispatch history to proceed.
 */
import assert from "node:assert/strict";
import { afterEach, test, vi } from "vitest";
import {
	MIN_MARGINAL_PROGRESS,
	WINDOW_MS,
	detectSameUnitLoop,
} from "../detectors/same-unit-loop.js";

const NOW = Date.parse("2026-05-17T12:00:00.000Z");
const UNIT_ID = "M010/S05/T01";

afterEach(() => {
	vi.useRealTimers();
});

function dispatch(overrides = {}) {
	return {
		unitId: UNIT_ID,
		startedAt: NOW - 5 * 60 * 1000,
		toolCallCount: 1,
		outcome: "pending",
		...overrides,
	};
}

test("detectSameUnitLoop_when_three_low_progress_dispatches_in_window_returns_stuck", () => {
	vi.setSystemTime(NOW);

	const result = detectSameUnitLoop(UNIT_ID, [
		dispatch({ toolCallCount: 1 }),
		dispatch({ startedAt: NOW - 10 * 60 * 1000, toolCallCount: 2 }),
		dispatch({ startedAt: NOW - 20 * 60 * 1000, toolCallCount: 3 }),
	]);

	assert.equal(result.stuck, true);
	assert.equal(result.reason, "same-unit-dispatch-loop");
	assert.deepEqual(result.signature, {
		unitId: UNIT_ID,
		dispatchCount: 3,
		windowMs: WINDOW_MS,
		averageToolCallsPerCycle: 2,
	});
});

test("detectSameUnitLoop_when_only_one_dispatch_is_in_window_returns_not_stuck", () => {
	vi.setSystemTime(NOW);

	const result = detectSameUnitLoop(UNIT_ID, [
		dispatch({ startedAt: NOW - 5 * 60 * 1000 }),
		dispatch({ startedAt: NOW - 31 * 60 * 1000 }),
		dispatch({ startedAt: NOW - 32 * 60 * 1000 }),
		dispatch({ startedAt: NOW - 33 * 60 * 1000 }),
		dispatch({ startedAt: NOW - 34 * 60 * 1000 }),
	]);

	assert.deepEqual(result, { stuck: false });
});

test("detectSameUnitLoop_when_average_tool_calls_exceeds_minimum_returns_not_stuck", () => {
	vi.setSystemTime(NOW);

	const result = detectSameUnitLoop(UNIT_ID, [
		dispatch({ toolCallCount: MIN_MARGINAL_PROGRESS + 1 }),
		dispatch({ startedAt: NOW - 10 * 60 * 1000, toolCallCount: 6 }),
		dispatch({ startedAt: NOW - 20 * 60 * 1000, toolCallCount: 7 }),
	]);

	assert.deepEqual(result, { stuck: false });
});

test("detectSameUnitLoop_when_all_dispatches_completed_returns_not_stuck", () => {
	vi.setSystemTime(NOW);

	const result = detectSameUnitLoop(UNIT_ID, [
		dispatch({ outcome: "complete" }),
		dispatch({ startedAt: NOW - 10 * 60 * 1000, outcome: "complete" }),
		dispatch({ startedAt: NOW - 20 * 60 * 1000, outcome: "complete" }),
	]);

	assert.deepEqual(result, { stuck: false });
});
