// engine/spec-templates/renderer.ts — Template variable substitution engine.
// Replaces {{VariableKey}} placeholders with user-provided values.
// Supports partial criteria selection (AC-07) and estimation injection (AC-09).
/**
 * Interpolates all {{Key}} placeholders in a template string with provided values.
 * Falls back to defaultValue if a variable is optional and not provided.
 */
export function interpolate(template, variables, definitions) {
    const valueMap = new Map(variables.map((v) => [v.key, v.value]));
    return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
        const provided = valueMap.get(key);
        if (provided !== undefined && provided.length > 0) {
            return provided;
        }
        const definition = definitions.find((d) => d.key === key);
        if (definition?.defaultValue !== undefined) {
            return definition.defaultValue;
        }
        // Leave the placeholder intact if no value was provided (will be flagged as missing)
        return match;
    });
}
/**
 * Returns the keys of required variables that are missing from the provided values.
 */
export function findMissingRequired(template, variables) {
    const providedKeys = new Set(variables
        .map((v) => v.key)
        .filter((k) => {
        const match = variables.find((v) => v.key === k);
        return match !== undefined && match.value.length > 0;
    }));
    return template.variables.filter((v) => v.required && !providedKeys.has(v.key)).map((v) => v.key);
}
/**
 * Resolves which criteria to include and generates warnings for forced-required criteria.
 * AC-07: Required criteria cannot be omitted.
 */
export function resolveCriteria(allCriteria, includeCriteria) {
    if (includeCriteria === undefined || includeCriteria.length === 0) {
        return { included: allCriteria, warnings: [] };
    }
    const requestedIds = new Set(includeCriteria);
    const warnings = [];
    const included = [];
    for (const criterion of allCriteria) {
        const isRequested = requestedIds.has(criterion.id);
        if (isRequested) {
            included.push(criterion);
        }
        else if (criterion.required) {
            // Required criterion was excluded — force-include with warning
            included.push(criterion);
            warnings.push(`Criterion "${criterion.id}: ${criterion.title}" is required and was included automatically.`);
        }
        // Optional, not requested → omit
    }
    return { included, warnings };
}
/**
 * Filters HU template content to include only the selected criteria sections.
 * Renumbers criteria sequentially (AC-01, AC-02, ...) to avoid gaps.
 */
export function filterHuByCriteria(huContent, allCriteria, includedCriteria) {
    if (includedCriteria.length === allCriteria.length) {
        return huContent;
    }
    const includedIds = new Set(includedCriteria.map((c) => c.id));
    const lines = huContent.split('\n');
    const outputLines = [];
    let currentCriterionId;
    let isIncluded = true;
    let renumberIndex = 0;
    // Map from old AC IDs to which ones are included
    const criterionIdPattern = /^### (AC-\d+)/;
    for (const line of lines) {
        const match = criterionIdPattern.exec(line);
        if (match?.[1] !== undefined) {
            currentCriterionId = match[1];
            isIncluded = includedIds.has(currentCriterionId);
            if (isIncluded) {
                renumberIndex++;
                const renumbered = line.replace(/^### AC-\d+/, `### AC-${renumberIndex}`);
                outputLines.push(renumbered);
            }
        }
        else if (isIncluded) {
            outputLines.push(line);
        }
    }
    return outputLines.join('\n');
}
/**
 * Filters PROGRESS template to include only selected criteria.
 */
export function filterProgressByCriteria(progressContent, allCriteria, includedCriteria) {
    if (includedCriteria.length === allCriteria.length) {
        return progressContent;
    }
    const includedIds = new Set(includedCriteria.map((c) => c.id));
    const lines = progressContent.split('\n');
    const outputLines = [];
    let renumberIndex = 0;
    for (const line of lines) {
        const criterionMatch = /^- \[ \] (AC-\d+):/.exec(line);
        if (criterionMatch?.[1] !== undefined) {
            if (includedIds.has(criterionMatch[1])) {
                renumberIndex++;
                const renumbered = line.replace(/AC-\d+/, `AC-${renumberIndex}`);
                outputLines.push(renumbered);
            }
        }
        else {
            outputLines.push(line);
        }
    }
    return outputLines.join('\n');
}
/**
 * Builds the estimation section to inject into the HU.md (AC-09).
 */
export function buildEstimationSection(template) {
    const { complexityScore, estimationBase } = template;
    return [
        '',
        '## Estimacion Inicial',
        '',
        `- **Complejidad:** ${complexityScore}`,
        `- **Horas estimadas:** ${estimationBase.hoursMin}–${estimationBase.hoursMax}h`,
        `- **Notas:** ${estimationBase.notes}`,
        '',
    ].join('\n');
}
/**
 * Renders all three template files (HU, FICHA-TECNICA, PROGRESS) with the provided values.
 * Supports partial criteria selection and estimation injection.
 */
export function renderTemplate(template, variables, includeCriteria) {
    const defs = template.variables;
    // Resolve criteria selection
    const { included, warnings } = resolveCriteria(template.criteria, includeCriteria);
    // Interpolate variables first
    let hu = interpolate(template.huTemplate, variables, defs);
    const fichaTecnica = interpolate(template.fichaTecnicaTemplate, variables, defs);
    let progress = interpolate(template.progressTemplate, variables, defs);
    // Filter criteria if partial selection
    hu = filterHuByCriteria(hu, template.criteria, included);
    progress = filterProgressByCriteria(progress, template.criteria, included);
    // Inject estimation section into HU (AC-09)
    hu += buildEstimationSection(template);
    return {
        hu,
        fichaTecnica,
        progress,
        criteriaIncluded: included.length,
        criteriaTotal: template.criteria.length,
        warnings,
    };
}
//# sourceMappingURL=renderer.js.map