// tools/spec-coverage.ts — MCP tool handler for spec_coverage.
// Analyzes how many acceptance criteria in a spec are backed by real tests.
import { join } from 'node:path';
import { specStore, knowledgeStore } from '../storage/index.js';
import { analyzeSpecCoverage } from '../engine/spec-coverage/index.js';
// ---------------------------------------------------------------------------
// Handler
// ---------------------------------------------------------------------------
export async function handleSpecCoverage(args) {
    const { projectId, specId } = args;
    // 1. Load project knowledge for project path
    const knowledge = await knowledgeStore.getKnowledge(projectId);
    if (!knowledge) {
        return errorResult(`Project "${projectId}" not found. Run init_project first.`);
    }
    const projectPath = knowledge.projectPath;
    // 2. Determine which specs to analyze
    if (specId === 'all') {
        return analyzeAllSpecs(projectId, projectPath);
    }
    return analyzeSingleSpec(projectId, specId, projectPath);
}
// ---------------------------------------------------------------------------
// Single-spec analysis
// ---------------------------------------------------------------------------
async function analyzeSingleSpec(projectId, specId, projectPath) {
    const spec = await specStore.getSpec(projectId, specId);
    if (!spec) {
        return errorResult(`Spec "${specId}" not found in project "${projectId}".`);
    }
    if (!spec.specPath) {
        return errorResult(`Spec "${specId}" has no HU.md path recorded. ` +
            `The spec may have been created before this feature was available.`);
    }
    const huPath = join(projectPath, spec.specPath);
    let result;
    try {
        result = await analyzeSpecCoverage(projectId, specId, huPath, projectPath);
    }
    catch (err) {
        return errorResult(err instanceof Error ? err.message : String(err));
    }
    const markdown = formatSingleResult(result);
    return { content: [{ type: 'text', text: markdown }] };
}
// ---------------------------------------------------------------------------
// All-specs analysis
// ---------------------------------------------------------------------------
async function analyzeAllSpecs(projectId, projectPath) {
    const allSpecs = await specStore.listSpecs(projectId);
    if (allSpecs.length === 0) {
        return errorResult(`No specs found in project "${projectId}".`);
    }
    const results = [];
    const errors = [];
    for (const spec of allSpecs) {
        if (!spec.specPath) {
            errors.push(`${spec.id}: no HU.md path`);
            continue;
        }
        const huPath = join(projectPath, spec.specPath);
        try {
            const result = await analyzeSpecCoverage(projectId, spec.id, huPath, projectPath);
            results.push(result);
        }
        catch (err) {
            errors.push(`${spec.id}: ${err instanceof Error ? err.message : String(err)}`);
        }
    }
    const markdown = formatAllResults(results, errors);
    return { content: [{ type: 'text', text: markdown }] };
}
// ---------------------------------------------------------------------------
// Markdown formatting
// ---------------------------------------------------------------------------
function formatSingleResult(result) {
    const lines = [];
    lines.push(`## Test Coverage — ${result.specId}`);
    lines.push('');
    lines.push(`**Score:** ${result.score}%  |  **Grade:** ${result.grade}  |  ` +
        `**Analyzed:** ${formatDate(result.analyzedAt)}`);
    lines.push('');
    // Summary row
    lines.push(`**Total:** ${result.totalCriteria}  |  ` +
        `✅ Covered: ${result.coveredCriteria}  |  ` +
        `⚠️ Superficial: ${result.superficialCriteria}  |  ` +
        `❌ Uncovered: ${result.uncoveredCriteria}`);
    lines.push('');
    // Runners
    if (result.detectedRunners.length > 0) {
        const runnerNames = result.detectedRunners.map((r) => r.name).join(', ');
        lines.push(`**Test runners detected:** ${runnerNames}`);
        lines.push('');
    }
    // Criteria table
    lines.push('### Acceptance Criteria Coverage');
    lines.push('');
    lines.push('| AC-ID | Description | Tests | Files | Coverage |');
    lines.push('|-------|-------------|------:|-------|----------|');
    for (const c of result.criteria) {
        const icon = criteriaIcon(c);
        const files = c.tests.map((t) => `\`${truncate(t.file, 40)}\``).join(', ');
        const testCount = c.tests.length;
        lines.push(`| ${c.acId} | ${truncate(c.description, 60)} | ${testCount} | ${files || '—'} | ${icon} |`);
    }
    lines.push('');
    // Uncovered section
    const uncovered = result.criteria.filter((c) => c.status === 'uncovered');
    if (uncovered.length > 0) {
        lines.push('### Criteria Without Test Coverage');
        lines.push('');
        for (const c of uncovered) {
            lines.push(`- ❌ **${c.acId}**: ${c.description}`);
        }
        lines.push('');
        lines.push(`> **Tip:** Run \`generate_tests\` with specId=\`${result.specId}\` to generate ` +
            `test stubs for the ${uncovered.length} uncovered criteria.`);
        lines.push('');
    }
    return lines.join('\n');
}
function formatAllResults(results, errors) {
    const lines = [];
    lines.push('## Test Coverage — All Specs');
    lines.push('');
    if (results.length > 0) {
        lines.push('| Spec ID | Score | Grade | Covered | Superficial | Uncovered | Total |');
        lines.push('|---------|------:|-------|--------:|------------:|----------:|------:|');
        for (const r of results) {
            lines.push(`| ${r.specId} | ${r.score}% | ${r.grade} | ${r.coveredCriteria} | ` +
                `${r.superficialCriteria} | ${r.uncoveredCriteria} | ${r.totalCriteria} |`);
        }
        lines.push('');
        // Overall stats
        const totalCriteria = results.reduce((s, r) => s + r.totalCriteria, 0);
        const totalCovered = results.reduce((s, r) => s + r.coveredCriteria, 0);
        const overallScore = totalCriteria > 0 ? Math.round((totalCovered / totalCriteria) * 100) : 0;
        lines.push(`**Overall coverage:** ${overallScore}% (${totalCovered}/${totalCriteria} criteria)`);
        lines.push('');
    }
    if (errors.length > 0) {
        lines.push('### Analysis Errors');
        lines.push('');
        for (const e of errors) {
            lines.push(`- ⚠️ ${e}`);
        }
        lines.push('');
    }
    return lines.join('\n');
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function criteriaIcon(c) {
    switch (c.status) {
        case 'covered':
            return '✅';
        case 'superficial':
            return '⚠️';
        case 'uncovered':
            return '❌';
    }
}
function truncate(text, maxLen) {
    return text.length <= maxLen ? text : text.slice(0, maxLen - 1) + '…';
}
function formatDate(iso) {
    return iso.slice(0, 10);
}
function errorResult(message) {
    return {
        content: [{ type: 'text', text: `Error: ${message}` }],
        isError: true,
    };
}
//# sourceMappingURL=spec-coverage.js.map