// engine/handoff-packager.ts — Assembles handoff context package for AI agents (SPEC-039)
import { readFile } from 'node:fs/promises';
import { stripFrontmatter } from './frontmatter-parser.js';
// ── Parsing helpers ──────────────────────────────────────────────────────────
async function safeReadFile(path) {
    if (!path) {
        return '';
    }
    try {
        const raw = await readFile(path, 'utf-8');
        return stripFrontmatter(raw);
    }
    catch {
        return '';
    }
}
function extractObjective(huContent, spec) {
    if (!huContent) {
        return spec.title;
    }
    // Look for "quiero" pattern (Spanish HU format)
    const quieroMatch = /quiero[^,.\n]+/i.exec(huContent);
    if (quieroMatch?.[0]) {
        return quieroMatch[0].trim().replace(/^quiero\s+/i, '');
    }
    // Fallback: first non-empty line after "Historia de Usuario" heading
    const lines = huContent.split('\n');
    const huIdx = lines.findIndex((l) => /historia de usuario/i.test(l));
    if (huIdx !== -1) {
        for (let i = huIdx + 1; i < lines.length && i < huIdx + 10; i++) {
            /* v8 ignore next 1 */
            const line = lines[i]?.trim() ?? '';
            if (line.length > 20 && !line.startsWith('#') && !line.startsWith('|')) {
                return line.replace(/\*\*/g, '').slice(0, 200);
            }
        }
    }
    return spec.title;
}
function extractCriteria(huContent) {
    if (!huContent) {
        return [];
    }
    return huContent
        .split('\n')
        .filter((line) => /^- \[[ x]\]/.test(line.trim()))
        .map((line) => line.trim().replace(/^- \[[ x]\]\s*/, ''));
}
function extractFilesFromFicha(fichaContent) {
    if (!fichaContent) {
        return { toModify: [], toCreate: [] };
    }
    const toModify = [];
    const toCreate = [];
    const lines = fichaContent.split('\n');
    let inFilesSection = false;
    for (const line of lines) {
        const trimmed = line.trim();
        if (/archivos?\s*(clave|key|impactados?|modificar|crear)/i.test(trimmed)) {
            inFilesSection = true;
            continue;
        }
        if (inFilesSection && trimmed.startsWith('##')) {
            inFilesSection = false;
        }
        if (!inFilesSection) {
            continue;
        }
        // Parse table rows: | path/to/file.ts | role |
        const tableMatch = /\|\s*(`?)([./\w-]+\.[a-z]+)\1\s*\|(.+)\|/.exec(trimmed);
        if (!tableMatch) {
            continue;
        }
        /* v8 ignore next 2 */
        const filePath = tableMatch[2] ?? '';
        const role = (tableMatch[3] ?? '').toLowerCase();
        /* v8 ignore start */
        if (!filePath) {
            continue;
        }
        /* v8 ignore stop */
        if (role.includes('nuevo') || role.includes('new') || role.includes('crear')) {
            toCreate.push(filePath);
        }
        else {
            toModify.push(filePath);
        }
    }
    return { toModify, toCreate };
}
function extractOutOfScope(huContent, spec) {
    if (!huContent) {
        return [
            `Anything beyond the ${spec.target} layer`,
            'Performance optimizations not specified in criteria',
        ];
    }
    const lines = huContent.split('\n');
    const outOfScopeItems = [];
    let inOosSection = false;
    for (const line of lines) {
        const trimmed = line.trim();
        if (/out of scope|fuera del scope|no incluye/i.test(trimmed)) {
            inOosSection = true;
            continue;
        }
        if (inOosSection && trimmed.startsWith('##')) {
            inOosSection = false;
        }
        if (inOosSection && /^[-*]/.test(trimmed)) {
            const item = trimmed.replace(/^[-*]\s*/, '').trim();
            if (item.length > 0) {
                outOfScopeItems.push(item);
            }
        }
    }
    if (outOfScopeItems.length === 0) {
        return [
            `Changes beyond the ${spec.target} target layer`,
            'Refactors not explicitly requested in acceptance criteria',
            'New dependencies not listed in FICHA-TECNICA.md',
        ];
    }
    return outOfScopeItems;
}
function buildConstraints(knowledge) {
    const constraints = [];
    const conventions = knowledge.conventions;
    const conventionEntries = Object.entries(conventions);
    for (const [key, value] of conventionEntries.slice(0, 5)) {
        constraints.push({
            type: 'interface',
            description: `Convention — ${key}: ${value}`,
            affectedItems: [],
        });
    }
    // Architecture constraints
    constraints.push({
        type: 'interface',
        description: `Architecture pattern: ${knowledge.architecture.primary} — do not break layer boundaries`,
        affectedItems: knowledge.layers,
    });
    return constraints;
}
// ── Public API ───────────────────────────────────────────────────────────────
export async function packageHandoff(spec, knowledge) {
    const warnings = [];
    const huContent = await safeReadFile(spec.specPath);
    const fichaContent = await safeReadFile(spec.technicalPath);
    if (!huContent) {
        warnings.push('HU.md not found — using minimal package. Run create_spec_hu to generate it.');
    }
    if (!fichaContent) {
        warnings.push('FICHA-TECNICA.md not found — file lists may be incomplete. Run create_spec_tech to generate it.');
    }
    const objective = extractObjective(huContent, spec);
    const criteria = extractCriteria(huContent);
    const { toModify, toCreate } = extractFilesFromFicha(fichaContent);
    const outOfScope = extractOutOfScope(huContent, spec);
    const constraints = buildConstraints(knowledge);
    if (criteria.length === 0) {
        warnings.push('No acceptance criteria found in HU.md — criteria list will be empty');
    }
    return {
        specId: spec.id,
        generatedAt: new Date().toISOString(),
        readinessScore: 0, // caller can populate with checkSpecReadiness result
        objective,
        criteria,
        filesToModify: toModify,
        filesToCreate: toCreate,
        outOfScope,
        constraints,
        warnings,
    };
}
//# sourceMappingURL=handoff-packager.js.map