/**
 * detector-status-completion-drift.test.mjs — R072 detector contracts.
 *
 * Purpose: prove SF catches rows where prose says complete but canonical
 * status columns still keep the unit dispatchable.
 */
import assert from "node:assert/strict";
import { test } from "vitest";

import { detectStatusCompletionDrift } from "../detectors/status-completion-drift.js";

function driftTask(overrides = {}) {
	return {
		milestoneId: "M010",
		sliceId: "S05",
		id: "T02",
		status: "pending",
		completed_at: null,
		narrative: "Implementation complete. Verification passed.",
		...overrides,
	};
}

function dispatches(count, overrides = {}) {
	return Array.from({ length: count }, () => ({
		unitId: "M010/S05/T02",
		outcome: "error",
		toolCallCount: 1,
		...overrides,
	}));
}

test("detectStatusCompletionDrift_when_prose_complete_status_pending_and_repeated_low_tool_dispatches_fires", () => {
	const result = detectStatusCompletionDrift({
		tasks: [driftTask()],
		recentDispatches: dispatches(2),
	});

	assert.equal(result.stuck, true);
	assert.equal(result.reason, "status-completion-drift");
	assert.equal(result.signature.unitId, "M010/S05/T02");
	assert.deepEqual(result.signature.proseFields, ["narrative"]);
});

test("detectStatusCompletionDrift_when_status_complete_does_not_fire", () => {
	const result = detectStatusCompletionDrift({
		tasks: [
			driftTask({
				status: "complete",
				completed_at: "2026-05-17T00:00:00.000Z",
			}),
		],
		recentDispatches: dispatches(3),
	});

	assert.equal(result.stuck, false);
});

test("detectStatusCompletionDrift_when_prose_lacks_completion_claim_does_not_fire", () => {
	const result = detectStatusCompletionDrift({
		tasks: [driftTask({ narrative: "Still investigating." })],
		recentDispatches: dispatches(3),
	});

	assert.equal(result.stuck, false);
});

test("detectStatusCompletionDrift_when_dispatch_count_below_threshold_does_not_fire", () => {
	const result = detectStatusCompletionDrift({
		tasks: [driftTask()],
		recentDispatches: dispatches(1),
	});

	assert.equal(result.stuck, false);
});

test("detectStatusCompletionDrift_when_dispatches_are_productive_does_not_fire", () => {
	const result = detectStatusCompletionDrift({
		tasks: [driftTask()],
		recentDispatches: dispatches(3, { toolCallCount: 4 }),
	});

	assert.equal(result.stuck, false);
});

test("detectStatusCompletionDrift_throttles_duplicate_unit_reports", () => {
	const throttleState = new Map();
	const ctx = {
		tasks: [driftTask()],
		recentDispatches: dispatches(3),
	};
	const options = {
		now: Date.parse("2026-05-17T12:00:00.000Z"),
		throttleState,
		throttleMs: 60_000,
	};

	const first = detectStatusCompletionDrift(ctx, options);
	const second = detectStatusCompletionDrift(ctx, {
		...options,
		now: options.now + 10_000,
	});

	assert.equal(first.stuck, true);
	assert.equal(second.stuck, false);
});
