/**
 * verification-defer-policy.js — classify verification failures as deferrable vs blocking.
 *
 * Purpose: prevent low-risk findings (style, format, deprecation-only warnings)
 * from blocking autonomous flow. When every failed check is deferrable and
 * confidence exceeds the threshold, the verification gate returns "deferred"
 * instead of "retry"/"pause", allowing the loop to continue without consuming
 * a retry attempt.
 *
 * Consumer: uok/auto-verification.js after the verification gate runs but before
 * retry/pause decision.
 */

const DEFERRABLE_WORDS =
	/\b(style|format|prettier|eslint.*fix|lint.*warning|deprecat|cosmetic|whitespace|trailing.*space|indent|semicolon|quote)\b/i;
const BLOCKING_WORDS =
	/\b(error|fail|crash|fatal|exception|throw|reject|unhandled|assert|timeout|broken|invalid|syntax)\b/i;
const STYLE_COMMANDS = /\b(prettier|eslint.*fix|stylelint|biome.*format)\b/i;

/**
 * Classify a single failed verification check.
 *
 * Returns:
 * - "deferrable" when stderr/command suggests style/format/deprecation-only issues.
 * - "blocking" when stderr contains blocking indicators.
 * - "unknown" when the failure is ambiguous.
 *
 * Purpose: separate advisory verification noise from failures that should stop
 * autonomous execution.
 *
 * Consumer: decideAutoDefer before runPostUnitVerification spends a retry.
 */
export function classifyCheck(check) {
	const stderr = check.stderr ?? "";
	const command = check.command ?? "";

	// Blocking words override everything — if the output contains "error", "fail",
	// "crash", "fatal", etc., it is never deferrable regardless of command name.
	if (BLOCKING_WORDS.test(stderr)) return "blocking";

	// Style/format-only commands with exit code 1 and short stderr are deferrable.
	if (
		STYLE_COMMANDS.test(command) &&
		check.exitCode === 1 &&
		stderr.length < 300
	) {
		return "deferrable";
	}

	// Deferrable words in stderr with no blocking words → deferrable.
	if (DEFERRABLE_WORDS.test(stderr)) return "deferrable";

	// Deprecation-only warnings (no blocking words already checked above).
	if (/\bdeprecat/i.test(stderr)) return "deferrable";

	return "unknown";
}

/**
 * Compute auto-defer confidence for a verification result.
 *
 * Returns a number from 0 to 1.
 *
 * Purpose: give autonomous verification an explicit confidence score before it
 * decides whether a failed check is safe to defer.
 *
 * Consumer: decideAutoDefer and focused policy tests.
 */
export function computeDeferConfidence(result) {
	const failedChecks = result.checks.filter((c) => c.exitCode !== 0);
	if (failedChecks.length === 0) return 1.0;

	const classifications = failedChecks.map(classifyCheck);
	const blockingCount = classifications.filter((c) => c === "blocking").length;
	const deferrableCount = classifications.filter(
		(c) => c === "deferrable",
	).length;
	const unknownCount = classifications.filter((c) => c === "unknown").length;

	if (blockingCount > 0) return 0.0;
	if (deferrableCount > 0 && unknownCount === 0) return 0.9;
	if (deferrableCount > 0 && unknownCount > 0) return 0.7;
	return 0.5;
}

/**
 * Decide whether a failed verification result should be auto-deferred.
 *
 * Purpose: allow low-risk verification failures to continue without consuming
 * a retry while keeping blocking failures on the normal retry/pause path.
 *
 * Consumer: runPostUnitVerification after evidence has been collected.
 *
 * @param result - verification gate result (must have .checks)
 * @param threshold - confidence threshold (default 0.75)
 * @returns { defer: boolean, confidence: number, reasons: string[] }
 */
export function decideAutoDefer(result, threshold = 0.75) {
	const confidence = computeDeferConfidence(result);
	const failedChecks = result.checks.filter((c) => c.exitCode !== 0);
	const reasons = [];

	for (const check of failedChecks) {
		const cls = classifyCheck(check);
		if (cls === "deferrable") {
			reasons.push(`${check.command}: ${cls}`);
		}
	}

	return {
		defer: confidence >= threshold && reasons.length > 0,
		confidence,
		reasons,
	};
}
