// tools/create-spec/auto-pipeline.ts — Auto post-creation quality pipeline (SPEC-445)
// Runs challenge + readiness automatically after spec creation.
// Never throws — all errors are captured in the result.
import { specStore, knowledgeStore } from '../../storage/index.js';
import { checkSpecReadiness } from '../../engine/readiness-checker.js';
import { scoreCriterion } from '../../engine/ears/criterion-scorer.js';
import { generateFailureScenarios, generateSecurityScenarios, generateScaleScenarios, generateDataConsistencyScenarios, } from '../challenge-spec-scenarios.js';
import { readSpecContent, calculateOverallRisk } from '../challenge-spec-helpers.js';
import { prioritizeScenarios, buildPrioritizedSummary, } from '../../engine/challenge-prioritizer.js';
const AUTO_PIPELINE_TIMEOUT_MS = 10_000;
async function runChallengePhase(spec, knowledge) {
    try {
        const specContent = knowledge ? await readSpecContent(spec) : '';
        const failureScenarios = [
            ...(knowledge ? generateFailureScenarios(spec, specContent, knowledge) : []),
            ...(knowledge ? generateSecurityScenarios(spec, specContent, knowledge) : []),
            ...(knowledge ? generateScaleScenarios(spec, specContent, knowledge) : []),
            ...(knowledge ? generateDataConsistencyScenarios(spec, specContent, knowledge) : []),
        ];
        const overallRisk = calculateOverallRisk(failureScenarios, {
            hotPaths: [],
            raceConditions: [],
            sharedState: [],
            recommendations: [],
        });
        const prioritized = prioritizeScenarios(failureScenarios, spec);
        const summary = buildPrioritizedSummary(prioritized);
        return summary.trim().length > 0
            ? summary
            : `${String(failureScenarios.length)} escenarios detectados (riesgo: ${overallRisk})`;
    }
    catch {
        // Challenge failure is non-blocking
        return null;
    }
}
async function runPipelineInternal(specId, projectId) {
    // Load spec and knowledge (both required for challenge)
    const [spec, knowledge] = await Promise.all([
        specStore.getSpec(projectId, specId),
        knowledgeStore.getKnowledge(projectId),
    ]);
    if (!spec) {
        return {
            challengeSummary: null,
            readinessScore: null,
            readinessSuggestion: null,
            error: `Spec ${specId} not found`,
        };
    }
    // --- Challenge phase ---
    const challengeSummary = await runChallengePhase(spec, knowledge);
    // --- Readiness phase ---
    let readinessScore = null;
    let readinessSuggestion = null;
    try {
        const report = await checkSpecReadiness(spec, 'lenient');
        readinessScore = report.score;
        if (report.score >= 80) {
            readinessSuggestion = 'Listo para review. Usa update_status(review) cuando quieras proceder.';
        }
        else {
            const missing = report.recommendations.length > 0
                ? report.recommendations.join('; ')
                : report.issues.blockers.join('; ');
            readinessSuggestion = missing.length > 0 ? missing : 'Completa los criterios faltantes.';
        }
    }
    catch {
        // Readiness failure is non-blocking — return partial result
    }
    // SPEC-501: AC testability gate — flag vague criteria, suggest EARS rewrites
    let acTestabilityWarning = null;
    try {
        acTestabilityWarning = await scoreSpecCriteria(spec);
    }
    catch {
        /* best-effort */
    }
    // SPEC-506: Surface top 3 relevant lessons from project history matching spec tags
    let relevantLessons = null;
    try {
        if (knowledge?.projectPath && spec.tags.length > 0) {
            const { getLessons } = await import('../../storage/lessons-store.js');
            const lessons = await getLessons(knowledge.projectPath, { tags: spec.tags, limit: 3 });
            if (lessons.length > 0) {
                relevantLessons = lessons.map((l) => `- ${l.title}: ${l.prevention}`);
            }
        }
    }
    catch {
        /* best-effort */
    }
    // SPEC-664: Resolve npm package versions mentioned in spec text
    let resolvedVersions = null;
    try {
        const specBody = await readSpecContent(spec);
        if (specBody) {
            const { resolveSpecVersions } = await import('../../engine/version-resolver.js');
            resolvedVersions = await resolveSpecVersions(specBody, projectId);
        }
    }
    catch {
        /* best-effort */
    }
    return {
        challengeSummary,
        readinessScore,
        readinessSuggestion,
        acTestabilityWarning,
        relevantLessons,
        resolvedVersions,
    };
}
/** SPEC-501: Score each AC for testability. Reads criteria from spec.md via readSpecContent. */
async function scoreSpecCriteria(spec) {
    const content = await readSpecContent(spec);
    if (!content) {
        return null;
    }
    // Extract criteria texts from YAML frontmatter (lines matching `  - text: "..."` or `    text: "..."`)
    const criteriaTexts = [];
    for (const line of content.split('\n')) {
        const match = /^\s+-?\s*(?:text:\s*)?["']?(.+?)["']?\s*$/.exec(line.trim());
        const textMatch = /^\s*-\s*text:\s*["']?(.+?)["']?\s*$/.exec(line);
        if (textMatch?.[1]) {
            criteriaTexts.push(textMatch[1]);
        }
        else if (line.trim().startsWith('- ') && !line.includes(':') && match?.[1]) {
            criteriaTexts.push(match[1]);
        }
    }
    if (criteriaTexts.length === 0) {
        return null;
    }
    const vague = criteriaTexts
        .map((text) => ({ text, scored: scoreCriterion(text) }))
        .filter(({ scored }) => scored.overallScore < 7);
    if (vague.length === 0) {
        return null;
    }
    const lines = vague.slice(0, 3).map(({ text, scored }) => {
        const rewrites = scored.rewriteSuggestions.slice(0, 1);
        const hint = rewrites.length > 0 ? ` → ${rewrites[0]}` : '';
        const short = text.length > 60 ? text.slice(0, 57) + '...' : text;
        return `  - "${short}"${hint}`;
    });
    const more = vague.length > 3 ? ` (+${String(vague.length - 3)} more)` : '';
    return `${String(vague.length)} AC(s) son poco testables${more}:\n${lines.join('\n')}`;
}
/**
 * Formats the auto-pipeline result into markdown lines to append to the spec creation response.
 * Returns an array of lines (with empty-string separators where needed).
 */
export function formatPipelineLines(result) {
    if (result.error) {
        return ['', `⚠️ Auto-pipeline parcial: ${result.error}`];
    }
    const lines = [];
    const challengeLine = result.challengeSummary ?? 'sin gaps críticos detectados';
    lines.push('', `🔥 Challenge (auto): ${challengeLine}`);
    if (result.readinessScore !== null) {
        lines.push('', `📊 Readiness (auto): ${String(result.readinessScore)}/100`);
        if (result.readinessSuggestion) {
            lines.push(result.readinessSuggestion);
        }
    }
    if (result.acTestabilityWarning) {
        lines.push('', `⚠️ Testabilidad (SPEC-501): ${result.acTestabilityWarning}`);
    }
    if (result.relevantLessons && result.relevantLessons.length > 0) {
        lines.push('', '📚 Lecciones relevantes (SPEC-506):');
        lines.push(...result.relevantLessons);
    }
    if (result.resolvedVersions && result.resolvedVersions.length > 0) {
        lines.push('', `📦 Versiones resueltas: ${result.resolvedVersions.join(', ')}`);
    }
    return lines;
}
/**
 * Runs challenge + readiness checks after spec creation.
 * Has a 10s total timeout. Never throws — errors are captured in the result.
 */
export async function runAutoPostCreatePipeline(specId, projectId, _projectPath) {
    const timeoutPromise = new Promise((resolve) => {
        /* c8 ignore next */
        setTimeout(() => {
            /* c8 ignore next */
            resolve({
                challengeSummary: null,
                readinessScore: null,
                readinessSuggestion: null,
                error: 'Auto-pipeline timeout (10s)',
            });
        }, AUTO_PIPELINE_TIMEOUT_MS);
    });
    try {
        return await Promise.race([runPipelineInternal(specId, projectId), timeoutPromise]);
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        return {
            challengeSummary: null,
            readinessScore: null,
            readinessSuggestion: null,
            error: message,
        };
    }
}
//# sourceMappingURL=auto-pipeline.js.map