/**
 * Normalize a string for fuzzy keyword matching:
 * lowercase, collapse whitespace, remove punctuation.
 */
function normalize(text) {
    return text
        .toLowerCase()
        .replace(/[^\w\s]/g, ' ')
        .replace(/\s+/g, ' ')
        .trim();
}
/**
 * Extract significant keywords from a phrase (>3 chars, not stop-words).
 */
const STOP_WORDS = new Set([
    'the',
    'and',
    'for',
    'with',
    'that',
    'this',
    'from',
    'will',
    'when',
    'then',
    'given',
    'should',
    'must',
    'have',
    'been',
    'not',
    'are',
    'its',
    'into',
    'via',
    'any',
    'all',
    'can',
    'may',
]);
function keywords(phrase) {
    return normalize(phrase)
        .split(' ')
        .filter((w) => w.length > 3 && !STOP_WORDS.has(w));
}
/**
 * Determine whether a criterion text contradicts an out-of-scope item.
 * Uses substring match first, then keyword overlap as fuzzy fallback.
 */
function contradicts(criterionText, outOfScopeItem) {
    const normCriterion = normalize(criterionText);
    const normScope = normalize(outOfScopeItem);
    // 1. Direct substring match
    if (normCriterion.includes(normScope)) {
        return true;
    }
    // 2. Reverse — the out-of-scope item contains the whole criterion (unlikely but safe)
    if (normScope.includes(normCriterion)) {
        return true;
    }
    // 3. Keyword overlap: if 2+ significant keywords from outOfScope appear in criterion
    const scopeKws = keywords(outOfScopeItem);
    if (scopeKws.length === 0) {
        return false;
    }
    const matchCount = scopeKws.filter((kw) => normCriterion.includes(kw)).length;
    // For 1 keyword: require it to match; for 2 keywords: require both; for 3+: require 2
    const threshold = scopeKws.length === 1 ? 1 : scopeKws.length === 2 ? 2 : 2;
    return matchCount >= threshold;
}
/**
 * Check acceptance criteria against the spec's outOfScope declarations.
 * Returns all pairs where a criterion contradicts an out-of-scope item.
 */
export function checkContradictions(outOfScope, criteria) {
    const contradictions = [];
    for (const criterion of criteria) {
        for (const scopeItem of outOfScope) {
            if (contradicts(criterion, scopeItem)) {
                contradictions.push({ criterion, contradicts: scopeItem });
            }
        }
    }
    const isClean = contradictions.length === 0;
    const summary = isClean
        ? 'No contradictions found between acceptance criteria and out-of-scope declarations.'
        : `Found ${contradictions.length} contradiction(s) between acceptance criteria and out-of-scope declarations.`;
    return { contradictions, isClean, summary };
}
//# sourceMappingURL=contradiction-checker.js.map