// Planu — Validator: criteria extractors
// Parses HU.md and FICHA-TECNICA.md to derive acceptance criteria lists.
import { readFile } from 'node:fs/promises';
/**
 * Extract all acceptance criteria from a spec's source files.
 */
export async function extractCriteria(spec) {
    const criteria = [];
    // Extract from HU.md — look for acceptance criteria sections
    try {
        const huContent = await readFile(spec.specPath, 'utf-8');
        const acSection = extractSection(huContent, 'acceptance criteria', 'criterios de aceptaci');
        if (acSection) {
            const items = extractListItems(acSection);
            criteria.push(...items);
        }
        // Also check for "Given/When/Then" patterns
        const gwtMatches = huContent.matchAll(/(?:Given|Dado|When|Cuando|Then|Entonces)\s+(.+)/gi);
        for (const match of gwtMatches) {
            /* v8 ignore next 3 -- defensive regex capture group guard */
            if (match[1]) {
                criteria.push(match[1].trim());
            }
        }
    }
    catch {
        // HU file not available
    }
    // Extract from FICHA-TECNICA.md — look for technical requirements
    try {
        const fichaContent = await readFile(spec.technicalPath, 'utf-8');
        const reqSection = extractSection(fichaContent, 'requirements', 'requisitos', 'technical', 'implementation');
        if (reqSection) {
            const items = extractListItems(reqSection);
            criteria.push(...items);
        }
    }
    catch {
        // FICHA file not available
    }
    // If no criteria extracted from files, generate basic ones from spec metadata
    if (criteria.length === 0) {
        criteria.push(`Feature "${spec.title}" is implemented`);
        if (spec.target === 'frontend' || spec.target === 'fullstack') {
            criteria.push('UI components render correctly');
        }
        if (spec.target === 'backend' || spec.target === 'fullstack') {
            criteria.push('API endpoints respond correctly');
        }
        criteria.push('No regression in existing tests');
    }
    return [...new Set(criteria)]; // Deduplicate
}
/**
 * Extract the text content under a markdown heading that matches any of the given keywords.
 */
export function extractSection(content, ...headingKeywords) {
    const lines = content.split('\n');
    let capturing = false;
    let capturedLevel = 0;
    const captured = [];
    for (const line of lines) {
        const headingMatch = /^(#{1,6})\s+(.+)/.exec(line);
        if (headingMatch?.[1] !== undefined && headingMatch[2] !== undefined) {
            const level = headingMatch[1].length;
            const title = headingMatch[2].toLowerCase();
            if (capturing && level <= capturedLevel) {
                break; // End of section
            }
            if (!capturing && headingKeywords.some((kw) => title.includes(kw))) {
                capturing = true;
                capturedLevel = level;
                continue;
            }
        }
        if (capturing) {
            captured.push(line);
        }
    }
    return captured.length > 0 ? captured.join('\n') : null;
}
/**
 * Extract bullet/numbered list items from a markdown text block.
 */
export function extractListItems(content) {
    const items = [];
    const lines = content.split('\n');
    for (const line of lines) {
        const listMatch = /^\s*[-*+]\s+\[?\s*[xX ]?\s*\]?\s*(.+)/.exec(line);
        if (listMatch?.[1]) {
            const item = listMatch[1].trim();
            if (item.length > 5) {
                // Skip trivially short items
                items.push(item);
            }
        }
        // Also handle numbered lists
        const numberedMatch = /^\s*\d+[.)]\s+(.+)/.exec(line);
        if (numberedMatch?.[1]) {
            const item = numberedMatch[1].trim();
            if (item.length > 5) {
                items.push(item);
            }
        }
    }
    return items;
}
//# sourceMappingURL=extractors.js.map