/**
 * repeated-feedback-kind.js — detect repeated self-feedback for one target.
 *
 * Purpose: stop autonomous mode from re-emitting the same self-feedback signal
 * against the same planning target without changing strategy.
 *
 * Consumer: periodic detector sweeps before the next autonomous run-control
 * boundary.
 */
export const MIN_OCCURRENCES = 3;
export const WINDOW_MS = 30 * 60 * 1000;

function timestampMs(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;
}

function canonicalTarget(occurredIn) {
	if (!occurredIn || typeof occurredIn !== "object") return "";
	if (occurredIn.task) return `task:${occurredIn.task}`;
	if (occurredIn.slice) return `slice:${occurredIn.slice}`;
	if (occurredIn.milestone) return `milestone:${occurredIn.milestone}`;
	return "";
}

/**
 * Detect repeated self-feedback of the same kind against the same target.
 *
 * Purpose: identify loops where SF diagnoses the same local failure repeatedly
 * and should escalate or change plan instead of appending another identical
 * feedback row.
 *
 * Consumer: periodic detector runner.
 */
export function detectRepeatedFeedbackKind(recentFeedback, options = {}) {
	const minOccurrences = options.minOccurrences ?? MIN_OCCURRENCES;
	const windowMs = options.windowMs ?? WINDOW_MS;
	const now = options.now ?? Date.now();
	const cutoff = now - windowMs;
	const rows = Array.isArray(recentFeedback) ? recentFeedback : [];
	const groups = new Map();

	for (const row of rows) {
		const kind = row?.kind;
		if (!kind) continue;
		const occurredAt = timestampMs(row.timestamp);
		if (occurredAt < cutoff) continue;
		const target = canonicalTarget(row.occurredIn);
		const key = `${kind}\0${target}`;
		const group = groups.get(key) ?? { kind, target, occurrences: 0 };
		group.occurrences += 1;
		groups.set(key, group);
		if (group.occurrences >= minOccurrences) {
			return {
				stuck: true,
				reason: "repeated-feedback-kind",
				signature: {
					kind,
					target,
					occurrences: group.occurrences,
					windowMs,
				},
			};
		}
	}

	return { stuck: false };
}

/**
 * Run the repeated-feedback detector as a UOK policy gate.
 *
 * Purpose: expose repeated self-feedback detection to run-control policy gates
 * without changing the detector function consumed by periodic sweeps.
 *
 * Consumer: UOK gate registry alignment for detector-based policy gates.
 */
export const repeatedFeedbackKindGate = {
	id: "repeated-feedback-kind",
	type: "policy",
	async execute(ctx = {}) {
		const result = detectRepeatedFeedbackKind(
			ctx.feedbackHistory ?? ctx.recentFeedback,
			ctx.options,
		);
		if (result.stuck) {
			return {
				outcome: "fail",
				failureClass: "policy",
				rationale: result.reason ?? "repeated-feedback-kind signal",
				findings: result.signature,
			};
		}
		return {
			outcome: "pass",
			failureClass: null,
			rationale: "no repeated-feedback-kind signal",
		};
	},
};
