/**
 * artifact-flap.js — detect artifacts oscillating between two hashes.
 *
 * Purpose: stop autonomous mode from repeatedly rewriting a generated artifact
 * back and forth while satisfying no stable predicate.
 *
 * Consumer: periodic detector sweeps over caller-provided artifact hash history.
 */
export const MIN_FLAPS = 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;
}

/**
 * Detect whether one artifact path is flapping between two content hashes.
 *
 * Purpose: distinguish real artifact evolution from two-state oscillation so
 * SF can stop retrying a predicate that is not converging.
 *
 * Consumer: periodic detector runner.
 */
export function detectArtifactFlap(artifactHistory, options = {}) {
	const minFlaps = options.minFlaps ?? MIN_FLAPS;
	const windowMs = options.windowMs ?? WINDOW_MS;
	const now = options.now ?? Date.now();
	const cutoff = now - windowMs;
	const rows = Array.isArray(artifactHistory) ? artifactHistory : [];
	const byPath = new Map();

	for (const row of rows) {
		if (!row?.path || !row.hash) continue;
		const occurredAt = timestampMs(row.timestamp);
		if (occurredAt < cutoff) continue;
		const entries = byPath.get(row.path) ?? [];
		entries.push({ hash: row.hash, timestamp: occurredAt });
		byPath.set(row.path, entries);
	}

	for (const [path, entries] of byPath) {
		entries.sort((left, right) => left.timestamp - right.timestamp);
		const hashes = entries.map((entry) => entry.hash);
		const uniqueHashes = new Set(hashes);
		let flaps = 0;
		for (let index = 1; index < hashes.length; index += 1) {
			if (hashes[index] !== hashes[index - 1]) flaps += 1;
		}
		if (flaps >= minFlaps && uniqueHashes.size <= 2) {
			return {
				stuck: true,
				reason: "artifact-flap",
				signature: {
					path,
					flaps,
					uniqueHashes: uniqueHashes.size,
					windowMs,
				},
			};
		}
	}

	return { stuck: false };
}

/**
 * Run the artifact-flap detector as a UOK verification gate.
 *
 * Purpose: expose artifact oscillation detection to verification gates while
 * preserving the periodic detector function contract.
 *
 * Consumer: UOK gate registry alignment for detector-based verification gates.
 */
export const artifactFlapGate = {
	id: "artifact-flap",
	type: "verification",
	async execute(ctx = {}) {
		const result = detectArtifactFlap(ctx.artifactHistory, ctx.options);
		if (result.stuck) {
			return {
				outcome: "fail",
				failureClass: "verification",
				rationale: result.reason ?? "artifact-flap signal",
				findings: result.signature,
			};
		}
		return {
			outcome: "pass",
			failureClass: null,
			rationale: "no artifact-flap signal",
		};
	},
};
