/*
 * Copyright 2026, Jaime Terrats.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { execFile as execFileCb } from 'node:child_process';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { promisify } from 'node:util';
import { addEvidenceRecord } from './orchestration-records.js';
const execFile = promisify(execFileCb);
export function parseTestOutput(stdout) {
    let parsed;
    try {
        parsed = JSON.parse(stdout);
    }
    catch {
        return { total: 0, passed: 0, failed: 0, skipped: 0, duration: 0, results: [] };
    }
    const results = collectTestResults(parsed);
    const stats = parsed.stats ?? {};
    return {
        total: stats.tests ?? results.length,
        passed: stats.passes ?? results.filter((r) => r.status === 'pass').length,
        failed: stats.failures ?? results.filter((r) => r.status === 'fail').length,
        skipped: stats.pending ?? results.filter((r) => r.status === 'skip').length,
        duration: stats.duration ?? 0,
        results,
    };
}
function collectTestResults(parsed) {
    const results = [];
    for (const t of parsed.passes ?? []) {
        results.push({ name: t.fullTitle ?? t.title ?? 'unknown', status: 'pass', duration: t.duration });
    }
    for (const t of parsed.failures ?? []) {
        results.push({
            name: t.fullTitle ?? t.title ?? 'unknown',
            status: 'fail',
            duration: t.duration,
            error: t.err?.message,
        });
    }
    for (const t of parsed.pending ?? []) {
        results.push({ name: t.fullTitle ?? t.title ?? 'unknown', status: 'skip' });
    }
    return results;
}
export async function generateTestEvidence(cwd, taskId) {
    const command = 'npx mocha --reporter json';
    let stdout = '';
    let exitCode = 0;
    try {
        const result = await execFile('npx', ['mocha', '--reporter', 'json'], { cwd, timeout: 120_000 });
        stdout = result.stdout;
    }
    catch (err) {
        const e = err;
        stdout = e.stdout ?? '';
        exitCode = e.code ?? 1;
    }
    const summary = parseTestOutput(stdout);
    const summaryText = `Tests: ${summary.passed}/${summary.total} passed, ${summary.failed} failed, ${summary.skipped} skipped (${summary.duration}ms)`;
    const evidenceRecord = addEvidenceRecord(cwd, {
        summary: `test-run: ${summaryText}`,
        profile: 'qa',
        evidenceKind: 'command',
        command,
        exitCode,
        details: summaryText,
        taskIds: [taskId],
    });
    let status;
    if (summary.failed > 0) {
        status = 'partial';
    }
    else {
        status = exitCode !== 0 ? 'failed' : 'success';
    }
    return {
        status,
        evidenceRecord,
        summary: summaryText,
        error: status === 'failed' ? `Test runner exited with code ${exitCode}` : undefined,
    };
}
// ─── Coverage Evidence ────────────────────────────────────────────────────────
export function parseLcovInfo(content) {
    const counters = { LF: 0, LH: 0, FNF: 0, FNH: 0, BRF: 0, BRH: 0 };
    for (const line of content.split('\n')) {
        const trimmed = line.trim();
        accumulateLcovLine(trimmed, counters);
    }
    return buildCoverageSummary(counters);
}
function accumulateLcovLine(trimmed, counters) {
    const prefixes = [
        { prefix: 'LF:', key: 'LF' },
        { prefix: 'LH:', key: 'LH' },
        { prefix: 'FNF:', key: 'FNF' },
        { prefix: 'FNH:', key: 'FNH' },
        { prefix: 'BRF:', key: 'BRF' },
        { prefix: 'BRH:', key: 'BRH' },
    ];
    for (const { prefix, key } of prefixes) {
        if (trimmed.startsWith(prefix)) {
            counters[key] += parseInt(trimmed.slice(prefix.length), 10) || 0;
            return;
        }
    }
}
function buildCoverageSummary(counters) {
    const pctCalc = (hit, found) => (found > 0 ? Math.round((hit / found) * 1000) / 10 : 0);
    return {
        lines: { found: counters.LF, hit: counters.LH, pct: pctCalc(counters.LH, counters.LF) },
        functions: {
            found: counters.FNF,
            hit: counters.FNH,
            pct: pctCalc(counters.FNH, counters.FNF),
        },
        branches: {
            found: counters.BRF,
            hit: counters.BRH,
            pct: pctCalc(counters.BRH, counters.BRF),
        },
    };
}
export function generateCoverageEvidence(cwd, taskId) {
    const lcovPath = join(cwd, 'coverage', 'lcov.info');
    let content;
    try {
        content = readFileSync(lcovPath, 'utf-8');
    }
    catch {
        return {
            status: 'failed',
            summary: 'Coverage file not found at coverage/lcov.info',
            error: `File not found: ${lcovPath}`,
        };
    }
    const coverage = parseLcovInfo(content);
    const summaryText = `Coverage: lines ${coverage.lines.pct}%, functions ${coverage.functions.pct}%, branches ${coverage.branches.pct}%`;
    const evidenceRecord = addEvidenceRecord(cwd, {
        summary: `coverage: ${summaryText}`,
        profile: 'qa',
        evidenceKind: 'report',
        path: lcovPath,
        details: summaryText,
        taskIds: [taskId],
    });
    return {
        status: 'success',
        evidenceRecord,
        summary: summaryText,
    };
}
export function parseLintOutput(stdout) {
    let parsed;
    try {
        parsed = JSON.parse(stdout);
    }
    catch {
        return { errors: 0, warnings: 0, fixable: 0, issues: [] };
    }
    let errors = 0;
    let warnings = 0;
    let fixable = 0;
    const issues = [];
    for (const file of parsed) {
        errors += file.errorCount ?? 0;
        warnings += file.warningCount ?? 0;
        fixable += (file.fixableErrorCount ?? 0) + (file.fixableWarningCount ?? 0);
        collectLintIssues(file, issues);
    }
    return { errors, warnings, fixable, issues };
}
function collectLintIssues(file, issues) {
    for (const msg of file.messages ?? []) {
        issues.push({
            file: file.filePath ?? 'unknown',
            line: msg.line ?? 0,
            column: msg.column ?? 0,
            severity: (msg.severity === 2 ? 'error' : 'warning'),
            message: msg.message ?? '',
            ruleId: msg.ruleId ?? undefined,
        });
    }
}
export async function generateLintEvidence(cwd, taskId) {
    const command = 'npx eslint . --format json';
    let stdout = '';
    let exitCode = 0;
    try {
        const result = await execFile('npx', ['eslint', '.', '--format', 'json'], { cwd, timeout: 120_000 });
        stdout = result.stdout;
    }
    catch (err) {
        const e = err;
        stdout = e.stdout ?? '';
        exitCode = e.code ?? 1;
    }
    const summary = parseLintOutput(stdout);
    const summaryText = `Lint: ${summary.errors} errors, ${summary.warnings} warnings, ${summary.fixable} fixable`;
    const evidenceRecord = addEvidenceRecord(cwd, {
        summary: `lint: ${summaryText}`,
        profile: 'qa',
        evidenceKind: 'command',
        command,
        exitCode,
        details: summaryText,
        taskIds: [taskId],
    });
    const status = summary.errors > 0 ? 'partial' : 'success';
    return {
        status,
        evidenceRecord,
        summary: summaryText,
        error: summary.errors > 0 ? `${summary.errors} lint errors found` : undefined,
    };
}
// ─── Combined Evidence ────────────────────────────────────────────────────────
export async function generateAllEvidence(cwd, taskId) {
    const [test, coverage, lint] = await Promise.all([
        generateTestEvidence(cwd, taskId),
        generateCoverageEvidence(cwd, taskId),
        generateLintEvidence(cwd, taskId),
    ]);
    const statuses = [test.status, coverage.status, lint.status];
    let overallStatus;
    if (statuses.every((s) => s === 'success')) {
        overallStatus = 'success';
    }
    else if (statuses.every((s) => s === 'failed')) {
        overallStatus = 'failed';
    }
    else {
        overallStatus = 'partial';
    }
    return { test, coverage, lint, overallStatus };
}
//# sourceMappingURL=qa-evidence-generator.js.map