// engine/ac-gap-detector/pattern-matcher.ts — Low-level keyword matching utilities (SPEC-156)
/**
 * Returns true if any keyword from the list appears in the text (case-insensitive).
 */
export function containsAny(text, keywords) {
    const lower = text.toLowerCase();
    return keywords.some((kw) => lower.includes(kw));
}
/**
 * Extracts acceptance criteria lines from markdown text.
 * Looks for lines starting with "- [ ]", "- [x]", "- [X]", numbered items, or "AC:" prefix.
 * Returns an array of cleaned criterion strings.
 */
export function extractAcLines(markdown) {
    const lines = markdown.split('\n');
    const results = [];
    // Track whether we are inside an acceptance criteria section.
    // Sections start at a heading that matches AC keywords and end at the next heading.
    const AC_HEADING = /^#{1,4}\s+(?:acceptance\s+criteria|ac\b)/i;
    const ANY_HEADING = /^#{1,4}\s+/;
    let insideAcSection = false;
    for (const raw of lines) {
        const line = raw.trim();
        // Update section tracking when we hit a heading
        if (ANY_HEADING.test(line)) {
            insideAcSection = AC_HEADING.test(line);
        }
        // BDD / checkbox style: "- [ ] Given...", "- [x] When..." — always captured regardless of section
        if (/^-\s*\[[ xX]\]/.test(line)) {
            results.push(line.replace(/^-\s*\[[ xX]\]\s*/, '').trim());
            continue;
        }
        // Numbered list: "1. When the user..." — always captured regardless of section
        if (/^\d+\.\s+/.test(line)) {
            results.push(line.replace(/^\d+\.\s+/, '').trim());
            continue;
        }
        // "AC:" prefix — always captured regardless of section
        if (/^ac:/i.test(line)) {
            results.push(line.replace(/^ac:\s*/i, '').trim());
            continue;
        }
        // Plain bullet (- or *) — only captured when inside an acceptance criteria section
        // to avoid pulling in unrelated list items from other sections of the spec.
        if (insideAcSection && /^[-*]\s+/.test(line) && line.length > 5) {
            results.push(line.replace(/^[-*]\s+/, '').trim());
        }
    }
    return results.filter((l) => l.length > 0);
}
/**
 * Computes a simple coverage score 0–100 based on how many gap categories
 * are already addressed vs total expected categories.
 */
export function computeCoverageScore(coveredCategories, totalCategories) {
    if (totalCategories === 0) {
        return 0;
    }
    const ratio = coveredCategories.size / totalCategories;
    return Math.round(ratio * 100);
}
//# sourceMappingURL=pattern-matcher.js.map