// tools/domain-criteria-handler.ts — Handlers for suggest_criteria and inject_criteria (SPEC-290)
import { readFile } from 'node:fs/promises';
import { atomicWriteFile } from '../engine/safety/atomic-write-file.js';
import { specStore } from '../storage/index.js';
import { detectDomains } from '../engine/domain-detector.js';
import { computeSuggestions } from '../engine/best-practices-library.js';
// ---------------------------------------------------------------------------
// Formatting helpers
// ---------------------------------------------------------------------------
function formatSuggestResult(result) {
    const lines = [];
    lines.push(`# Domain Criteria Suggestions — ${result.specId}`);
    lines.push(`**Spec**: ${result.specTitle}`);
    lines.push('');
    if (result.suggestions.length === 0) {
        lines.push('No domain detected in this spec. No suggestions available.');
        return lines.join('\n');
    }
    for (const suggestion of result.suggestions) {
        lines.push(`## Domain: \`${suggestion.domain}\``);
        lines.push('');
        if (suggestion.suggestedCriteria.length > 0) {
            lines.push('### Suggested Criteria (not yet covered)');
            lines.push('');
            for (const c of suggestion.suggestedCriteria) {
                lines.push(`- [ ] ${c}`);
            }
            lines.push('');
        }
        if (suggestion.alreadyCovered.length > 0) {
            lines.push('### Already Covered');
            lines.push('');
            for (const c of suggestion.alreadyCovered) {
                lines.push(`- [x] ${c}`);
            }
            lines.push('');
        }
        if (suggestion.hasCriticalGaps) {
            lines.push('> **WARNING**: This domain has critical criteria missing. ' +
                'Readiness score will be penalized until resolved.');
            lines.push('');
        }
    }
    lines.push(`---`);
    lines.push(`Total suggested: **${result.totalSuggested}** criteria across ${result.suggestions.length} domain(s).`);
    if (result.hasCriticalGaps) {
        lines.push('');
        lines.push('**ACTION REQUIRED**: Use `inject_criteria` to add the missing critical criteria to the spec.');
    }
    return lines.join('\n');
}
// ---------------------------------------------------------------------------
// suggest_criteria handler
// ---------------------------------------------------------------------------
export async function handleSuggestCriteria(args) {
    const { projectId, specId, projectHash } = args;
    const spec = await specStore.getSpec(projectId, specId);
    if (!spec) {
        return {
            content: [
                {
                    type: 'text',
                    text: `Spec '${specId}' not found in project '${projectId}'`,
                },
            ],
            isError: true,
        };
    }
    // Read spec file content for coverage analysis
    let specContent = `${spec.title} ${spec.tags.join(' ')}`;
    if (spec.specPath) {
        try {
            const fileContent = await readFile(spec.specPath, 'utf-8');
            specContent += ' ' + fileContent;
        }
        catch {
            // File not readable — use metadata only
        }
    }
    const detection = detectDomains(spec.title, specContent);
    if (detection.domains.length === 0) {
        const result = {
            specId,
            specTitle: spec.title,
            suggestions: [],
            totalSuggested: 0,
            hasCriticalGaps: false,
        };
        return {
            content: [{ type: 'text', text: formatSuggestResult(result) }],
            structuredContent: result,
        };
    }
    const suggestions = computeSuggestions(detection.domains, specContent, projectHash);
    const totalSuggested = suggestions.reduce((sum, s) => sum + s.suggestedCriteria.length, 0);
    const hasCriticalGaps = suggestions.some((s) => s.hasCriticalGaps);
    const result = {
        specId,
        specTitle: spec.title,
        suggestions,
        totalSuggested,
        hasCriticalGaps,
    };
    return {
        content: [{ type: 'text', text: formatSuggestResult(result) }],
        structuredContent: result,
    };
}
// ---------------------------------------------------------------------------
// inject_criteria handler
// ---------------------------------------------------------------------------
export async function handleInjectCriteria(args) {
    const { projectId, specId, criteriaIds, projectHash } = args;
    const spec = await specStore.getSpec(projectId, specId);
    if (!spec) {
        return {
            content: [
                {
                    type: 'text',
                    text: `Spec '${specId}' not found in project '${projectId}'`,
                },
            ],
            isError: true,
        };
    }
    if (!spec.specPath) {
        return {
            content: [
                {
                    type: 'text',
                    text: `Spec '${specId}' has no specPath — cannot inject criteria`,
                },
            ],
            isError: true,
        };
    }
    // Read current spec content
    let specContent;
    try {
        specContent = await readFile(spec.specPath, 'utf-8');
    }
    catch (err) {
        return {
            content: [
                {
                    type: 'text',
                    text: `Cannot read spec file at '${spec.specPath}': ${String(err)}`,
                },
            ],
            isError: true,
        };
    }
    // Compute which criteria to inject
    const detection = detectDomains(spec.title, specContent);
    const suggestions = computeSuggestions(detection.domains, specContent, projectHash);
    // Collect all criteria to inject (filtered by criteriaIds if provided)
    const toInject = [];
    for (const suggestion of suggestions) {
        for (let i = 0; i < suggestion.suggestedCriteria.length; i++) {
            const id = suggestion.suggestedIds[i];
            const criterion = suggestion.suggestedCriteria[i];
            if (criterion === undefined) {
                continue;
            }
            if (criteriaIds && id !== undefined && !criteriaIds.includes(id)) {
                continue;
            }
            toInject.push(criterion);
        }
    }
    if (toInject.length === 0) {
        return {
            content: [
                {
                    type: 'text',
                    text: `No criteria to inject for spec '${specId}'. All domain criteria are already covered, or no domain was detected.`,
                },
            ],
        };
    }
    // SPEC-461: Detect lean format (criteria in YAML frontmatter) vs old format
    const isLean = /^---\n[\s\S]*?criteria:\n/m.test(specContent);
    let updatedContent;
    if (isLean) {
        // Lean format: inject into YAML frontmatter criteria section
        const yamlCriteria = toInject
            .map((c) => `  - text: "${c.replace(/"/g, '\\"')}"\n    done: false`)
            .join('\n');
        // Insert before "history:" line
        const historyIdx = specContent.indexOf('history:');
        if (historyIdx !== -1) {
            updatedContent =
                specContent.slice(0, historyIdx) + yamlCriteria + '\n' + specContent.slice(historyIdx);
        }
        else {
            // Insert before closing ---
            const closingIdx = specContent.indexOf('---', 4);
            updatedContent =
                specContent.slice(0, closingIdx) + yamlCriteria + '\n' + specContent.slice(closingIdx);
        }
    }
    else {
        // Old format: find ## Acceptance Criteria section
        const acHeader = '## Acceptance Criteria';
        const acIndex = specContent.indexOf(acHeader);
        const injectedLines = toInject
            .map((c) => `- [ ] ${c}\n  <!-- injected by inject_criteria (SPEC-290) -->`)
            .join('\n');
        if (acIndex !== -1) {
            const afterHeader = specContent.indexOf('\n', acIndex) + 1;
            updatedContent =
                specContent.slice(0, afterHeader) + injectedLines + '\n' + specContent.slice(afterHeader);
        }
        else {
            updatedContent = specContent.trimEnd() + '\n\n' + acHeader + '\n\n' + injectedLines + '\n';
        }
    }
    await atomicWriteFile(spec.specPath, updatedContent);
    const injResult = {
        specId,
        injectedCount: toInject.length,
        injectedCriteria: toInject,
        updatedFilePath: spec.specPath,
    };
    const text = [
        `# Criteria Injected — ${specId}`,
        '',
        `Injected **${toInject.length}** criteria into \`${spec.specPath}\`:`,
        '',
        ...toInject.map((c) => `- [ ] ${c}`),
    ].join('\n');
    return {
        content: [{ type: 'text', text }],
        structuredContent: injResult,
    };
}
//# sourceMappingURL=domain-criteria-handler.js.map