/**
 * same-unit-loop.js — detect repeated low-progress dispatches of one unit.
 *
 * Purpose: stop autonomous mode from burning cycles on the same selected unit
 * when dispatch history shows repeated non-completing, low-progress attempts.
 *
 * Consumer: autoLoop before handing the selected unit to guard and execution
 * phases.
 */
export const DISPATCH_COUNT_THRESHOLD = 3;
export const WINDOW_MS = 30 * 60 * 1000;
export const MIN_MARGINAL_PROGRESS = 5;

function startedAtMs(value) {
	if (typeof value === "number" && Number.isFinite(value)) return value;
	if (typeof value === "string") {
		const parsed = Date.parse(value);
		if (Number.isFinite(parsed)) return parsed;
	}
	return 0;
}

/**
 * Detect whether recent dispatches indicate a same-unit dispatch loop.
 *
 * Purpose: distinguish repeated low-progress redispatch from legitimate
 * productive retries before SF spends another executor turn on the same unit.
 *
 * Consumer: autoLoop dispatch-selection guard.
 */
export function detectSameUnitLoop(unitId, recentDispatches, options = {}) {
	const dispatchCountThreshold =
		options.dispatchCountThreshold ?? DISPATCH_COUNT_THRESHOLD;
	const windowMs = options.windowMs ?? WINDOW_MS;
	const minMarginalProgress =
		options.minMarginalProgress ?? MIN_MARGINAL_PROGRESS;
	const cutoff = Date.now() - windowMs;
	const rows = Array.isArray(recentDispatches) ? recentDispatches : [];
	const filtered = rows.filter(
		(row) =>
			row?.unitId === unitId &&
			startedAtMs(row.startedAt) >= cutoff &&
			row.outcome !== "complete",
	);
	if (filtered.length < dispatchCountThreshold) {
		return { stuck: false };
	}
	const totalToolCalls = filtered.reduce(
		(sum, row) => sum + Number(row.toolCallCount ?? 0),
		0,
	);
	const averageToolCallsPerCycle = totalToolCalls / filtered.length;
	if (averageToolCallsPerCycle >= minMarginalProgress) {
		return { stuck: false };
	}
	return {
		stuck: true,
		reason: "same-unit-dispatch-loop",
		signature: {
			unitId,
			dispatchCount: filtered.length,
			windowMs,
			averageToolCallsPerCycle,
		},
	};
}

/**
 * Run the same-unit loop detector as a UOK verification gate.
 *
 * Purpose: expose the dispatch-loop detector to the gate runner without
 * changing the detector function contract used by autonomous dispatch.
 *
 * Consumer: UOK gate registry alignment for detector-based verification gates.
 */
export const sameUnitLoopGate = {
	id: "same-unit-loop",
	type: "verification",
	async execute(ctx = {}) {
		const result = detectSameUnitLoop(
			ctx.unitId,
			ctx.recentDispatches,
			ctx.options,
		);
		if (result.stuck) {
			return {
				outcome: "fail",
				failureClass: "verification",
				rationale: result.reason ?? "same-unit-loop signal",
				findings: result.signature,
			};
		}
		return {
			outcome: "pass",
			failureClass: null,
			rationale: "no same-unit-loop signal",
		};
	},
};
