import { readVerificationOutcomes, writeVerificationOutcomes, } from './verification-outcomes.js';
const NOOP_RESULT_TEMPLATE = {
    verifierInvocations: 0,
    promoted: 0,
    verifierFailures: 0,
    changed: false,
};
export async function promotePendingRules(rules, deps) {
    const pendingIndices = [];
    for (let i = 0; i < rules.length; i++) {
        if (rules[i]?.status === 'pending-verification')
            pendingIndices.push(i);
    }
    // Empty-pending fast path (Invariant #9): skip the outcomes-file read
    // when there's nothing to verify. The returned array is a shallow copy
    // of the input — `changed: false` is the no-op signal callers should use
    // to skip manifest writes, not array-identity.
    if (pendingIndices.length === 0) {
        return { mutatedRules: [...rules], ...NOOP_RESULT_TEMPLATE };
    }
    const onWarn = deps.onWarn ?? (() => { });
    const now = deps.now ?? (() => new Date());
    const existingOutcomes = readVerificationOutcomes(deps.outcomesPath, onWarn);
    const updatedOutcomes = { ...existingOutcomes };
    const mutatedRules = [...rules];
    let verifierInvocations = 0;
    let promoted = 0;
    let verifierFailures = 0;
    let outcomesChanged = false;
    for (const idx of pendingIndices) {
        const rule = mutatedRules[idx];
        const memoized = existingOutcomes[rule.lessonHash];
        let outcomeEntry = null;
        if (memoized && memoized.ruleHash === rule.lessonHash) {
            // Memoization hit: reuse the recorded outcome. The status was either
            // never written back to the manifest (interrupted prior pass) or the
            // rule was re-stamped pending by a reinstall — either way, the
            // recorded outcome is still authoritative.
            outcomeEntry = memoized;
        }
        else {
            // No memoized outcome (or hash mismatch on a stale entry). Invoke the
            // verifier. Per-rule try/catch isolates failures (Invariant #7).
            try {
                verifierInvocations += 1;
                const result = await deps.verifier(rule);
                outcomeEntry = {
                    ruleHash: rule.lessonHash,
                    verifiedAt: now().toISOString(),
                    outcome: result.outcome,
                    baselineMatches: [...result.baselineMatches],
                    inScopeMatches: [...result.inScopeMatches],
                    candidateDebtLines: [...result.candidateDebtLines],
                };
                updatedOutcomes[rule.lessonHash] = outcomeEntry;
                outcomesChanged = true; // totem-context: per-rule verifier-throw isolation per Invariant #7 of #1684 — one failing rule must not abort the lint pass; the rule stays 'pending-verification' and next lint retries
            }
            catch (err) {
                verifierFailures += 1;
                onWarn(`Stage 4 verifier failed for rule ${rule.lessonHash} (${rule.lessonHeading}); ` +
                    `leaving as 'pending-verification' for next lint retry. ` +
                    `Cause: ${err instanceof Error ? err.message : String(err)}`);
                continue;
            }
        }
        mutatedRules[idx] = applyOutcomeToRule(rule, outcomeEntry);
        promoted += 1;
    }
    if (outcomesChanged) {
        writeVerificationOutcomes(deps.outcomesPath, updatedOutcomes);
    }
    return {
        mutatedRules,
        verifierInvocations,
        promoted,
        verifierFailures,
        changed: promoted > 0,
    };
}
/**
 * Map a Stage 4 outcome to the rule's terminal lifecycle status per
 * Invariant #3. Pure function so the four-way mapping can be unit-tested
 * without staging the full interceptor flow. Returns a new rule object;
 * never mutates the input.
 */
export function applyOutcomeToRule(rule, entry) {
    switch (entry.outcome) {
        case 'no-matches':
            return { ...rule, status: 'untested-against-codebase' };
        case 'out-of-scope':
            return {
                ...rule,
                status: 'archived',
                archivedReason: 'stage4-out-of-scope-match',
                archivedAt: rule.archivedAt ?? entry.verifiedAt,
            };
        case 'in-scope-bad-example':
            return { ...rule, status: 'active', confidence: 'high' };
        case 'candidate-debt':
            return { ...rule, status: 'active', severity: 'warning' };
    }
}
//# sourceMappingURL=first-lint-promote.js.map