// engine/living-spec/test-coverage-correlator.ts — AC-8: Test coverage correlation
// Parses Vitest JSON and JUnit XML outputs, groups by spec/criteria annotations,
// determines criterion status, and builds coverage reports.
// ---------------------------------------------------------------------------
// Vitest JSON parsing
// ---------------------------------------------------------------------------
/**
 * Parse a Vitest JSON report string into structured results.
 */
export function parseVitestJson(json) {
    const report = JSON.parse(json);
    return report.testResults ?? [];
}
// ---------------------------------------------------------------------------
// JUnit XML parsing (simple, no external deps)
// ---------------------------------------------------------------------------
/**
 * Parse JUnit XML string into test cases (simple regex-based parser).
 */
export function parseJUnitXml(xml) {
    const cases = [];
    const testCaseRegex = /<testcase\s+([^>]*)>([\s\S]*?)<\/testcase>|<testcase\s+([^>]*)\/>/g;
    let match;
    while ((match = testCaseRegex.exec(xml)) !== null) {
        const attrs = match[1] ?? match[3] ?? '';
        const body = match[2] ?? '';
        const name = extractAttr(attrs, 'name') ?? 'unknown';
        const classname = extractAttr(attrs, 'classname') ?? '';
        const file = extractAttr(attrs, 'file') ?? classname;
        const hasFailure = body.includes('<failure') || body.includes('<error');
        cases.push({ name, classname, file, passed: !hasFailure });
    }
    return cases;
}
function extractAttr(attrs, name) {
    const regex = new RegExp(`${name}="([^"]*)"`, 'i');
    const match = regex.exec(attrs);
    return match?.[1] ?? null;
}
// ---------------------------------------------------------------------------
// Annotation grouping
// ---------------------------------------------------------------------------
/**
 * Group test results by spec annotation, matching test file paths to annotations.
 */
export function groupByAnnotation(tests, annotations) {
    const fileAnnotations = new Map();
    for (const ann of annotations) {
        const existing = fileAnnotations.get(ann.file) ?? [];
        existing.push(ann);
        fileAnnotations.set(ann.file, existing);
    }
    const groupMap = new Map();
    for (const test of tests) {
        const matchedAnnotations = findMatchingAnnotations(test.file, fileAnnotations);
        for (const ann of matchedAnnotations) {
            for (const criterion of ann.criteria) {
                const key = `${ann.specId}:AC-${criterion}`;
                const group = groupMap.get(key) ?? {
                    specId: ann.specId,
                    criterion,
                    status: 'untested',
                    testCount: 0,
                    passCount: 0,
                    failCount: 0,
                    files: [],
                };
                group.testCount++;
                if (test.passed) {
                    group.passCount++;
                }
                else {
                    group.failCount++;
                }
                if (!group.files.includes(test.file)) {
                    group.files.push(test.file);
                }
                groupMap.set(key, group);
            }
        }
    }
    for (const group of groupMap.values()) {
        group.status = determineCriterionStatus(group);
    }
    return Array.from(groupMap.values());
}
/**
 * Find annotations matching a test file path.
 */
function findMatchingAnnotations(testFile, fileAnnotations) {
    const results = [];
    for (const [annotatedFile, anns] of fileAnnotations) {
        if (testFile === annotatedFile ||
            testFile.includes(annotatedFile) ||
            annotatedFile.includes(testFile)) {
            results.push(...anns);
        }
    }
    return results;
}
/**
 * Determine criterion status based on test results.
 */
export function determineCriterionStatus(group) {
    if (group.testCount === 0) {
        return 'untested';
    }
    if (group.failCount > 0) {
        return 'regression';
    }
    return 'verified';
}
/**
 * Build a full test coverage report from grouped results.
 */
export function buildCoverageReport(projectId, groups, totalTests, passedTests, failedTests) {
    const coveredCriteria = groups.filter((g) => g.status !== 'untested').length;
    const totalCriteria = groups.length;
    const coveragePercentage = totalCriteria > 0 ? Math.round((coveredCriteria / totalCriteria) * 100) : 0;
    return {
        projectId,
        groups,
        totalTests,
        passedTests,
        failedTests,
        coveragePercentage,
        generatedAt: new Date().toISOString(),
    };
}
//# sourceMappingURL=test-coverage-correlator.js.map