import { ti } from '../i18n/index.js';
import { specStore, knowledgeStore } from '../storage/index.js';
import { createVersion } from '../engine/spec-versioner.js';
import { readFile, writeFile } from 'node:fs/promises';
import { dirname } from 'node:path';
import { appendChangelogEntry } from '../engine/spec-changelog/index.js';
/**
 * Auto-detect changes by comparing spec metadata with current state.
 */
async function autoDetectChanges(spec, _knowledge) {
    const changes = [];
    // Check if estimation was significantly off (only if actuals exist)
    if (spec.actuals) {
        const estimatedTotal = spec.estimation.devHours + spec.estimation.reviewHours;
        const actualTotal = spec.actuals.devHours + spec.actuals.reviewHours;
        const ratio = actualTotal / estimatedTotal;
        if (ratio > 1.3 || ratio < 0.7) {
            changes.push({
                section: 'estimation',
                originalValue: `${estimatedTotal}h estimated`,
                newValue: `${actualTotal}h actual (${Math.round(ratio * 100)}% of estimate)`,
                reason: ratio > 1.3
                    ? 'Implementation took significantly longer than estimated'
                    : 'Implementation was completed faster than estimated',
                approved: true,
            });
        }
        // Cost discrepancy
        if (Math.abs(spec.estimation.totalCostUsd - spec.actuals.totalCostUsd) > 10) {
            changes.push({
                section: 'cost',
                originalValue: `$${spec.estimation.totalCostUsd}`,
                newValue: `$${spec.actuals.totalCostUsd}`,
                reason: 'Actual cost differs from estimate',
                approved: true,
            });
        }
    }
    // Check if status should be updated
    if (spec.actuals && spec.status !== 'done') {
        changes.push({
            section: 'status',
            originalValue: spec.status,
            newValue: 'done',
            reason: 'Actuals are recorded but status is not done',
            approved: false,
        });
    }
    // Detect if spec files have been manually modified
    try {
        const huContent = await readFile(spec.specPath, 'utf-8');
        if (huContent.includes('MODIFIED') || huContent.includes('UPDATED')) {
            changes.push({
                section: 'hu-manual-edits',
                originalValue: 'Original spec content',
                newValue: 'Manual edits detected in spec.md',
                reason: 'spec.md appears to have been manually modified after creation',
                approved: true,
            });
        }
    }
    catch {
        changes.push({
            section: 'hu-file',
            originalValue: spec.specPath,
            newValue: 'File not found',
            reason: 'spec.md file is missing or was moved',
            approved: false,
        });
    }
    try {
        await readFile(spec.technicalPath, 'utf-8');
    }
    catch {
        changes.push({
            section: 'ficha-file',
            originalValue: spec.technicalPath,
            newValue: 'File not found',
            reason: 'technical.md file is missing or was moved',
            approved: false,
        });
    }
    return changes;
}
/**
 * Apply approved changes to the spec files.
 */
async function applyChangesToSpecFiles(spec, changes) {
    const approvedChanges = changes.filter((c) => c.approved);
    if (approvedChanges.length === 0) {
        return;
    }
    // Update FICHA-TECNICA.md with a reconciliation section
    try {
        let fichaContent = await readFile(spec.technicalPath, 'utf-8');
        // Add reconciliation log
        const reconcileSection = [
            '',
            '## Reconciliation Log',
            '',
            `> Reconciled at: ${new Date().toISOString()}`,
            '',
            '| Section | Original | Updated | Reason |',
            '|---------|----------|---------|--------|',
            ...approvedChanges.map((c) => `| ${c.section} | ${c.originalValue} | ${c.newValue} | ${c.reason} |`),
            '',
        ].join('\n');
        // Check if there's already a reconciliation section
        if (fichaContent.includes('## Reconciliation Log')) {
            // Append to existing section
            fichaContent = fichaContent.replace(/(## Reconciliation Log[\s\S]*?)(\n## |\n$|$)/, `$1\n${reconcileSection}\n$2`);
        }
        else {
            // Add new section at the end
            fichaContent += reconcileSection;
        }
        await writeFile(spec.technicalPath, fichaContent, 'utf-8');
    }
    catch {
        // File doesn't exist, skip
    }
    // Update post-implementation section if actuals are present
    if (spec.actuals) {
        try {
            let fichaContent = await readFile(spec.technicalPath, 'utf-8');
            // Replace the post-implementation table values
            fichaContent = fichaContent.replace(/(\| Dev Hours \| [^|]+ \|) — \|/, `$1 ${spec.actuals.devHours}h |`);
            fichaContent = fichaContent.replace(/(\| Review Hours \| [^|]+ \|) — \|/, `$1 ${spec.actuals.reviewHours}h |`);
            fichaContent = fichaContent.replace(/(\| API Cost \| [^|]+ \|) — \|/, `$1 $${spec.actuals.apiCostUsd} |`);
            fichaContent = fichaContent.replace(/(\| Total Cost \| [^|]+ \|) — \|/, `$1 $${spec.actuals.totalCostUsd} |`);
            await writeFile(spec.technicalPath, fichaContent, 'utf-8');
        }
        catch {
            // File doesn't exist, skip
        }
    }
}
/**
 * Append a reconciliation note to PLAN.md when changes are applied.
 */
async function appendPlanReconcileNote(planPath, changes) {
    try {
        const existing = await readFile(planPath, 'utf-8');
        const note = [
            '',
            '---',
            '',
            `## Reconciliation Note — ${new Date().toISOString()}`,
            '',
            `> ${changes.length} change(s) applied during reconciliation:`,
            '',
            ...changes.map((c) => `- **${c.section}**: ${c.reason}`),
            '',
        ].join('\n');
        await writeFile(planPath, existing + note, 'utf-8');
    }
    catch {
        // Non-fatal: if PLAN.md can't be updated, reconcile still succeeds
    }
}
export async function handleReconcileSpec(params) {
    const { specId, projectId, autoDetect = true, changes: manualChanges } = params;
    try {
        // Get the spec
        const spec = await specStore.getSpec(projectId, specId);
        if (!spec) {
            return {
                content: [{ type: 'text', text: ti('spec.notFound', { id: specId }) }],
                isError: true,
            };
        }
        // Load project knowledge
        const knowledge = await knowledgeStore.getKnowledge(projectId);
        // Collect all changes
        const allChanges = [];
        // Auto-detect changes
        if (autoDetect) {
            const autoChanges = await autoDetectChanges(spec, knowledge);
            allChanges.push(...autoChanges);
        }
        // Add manual changes
        if (manualChanges && manualChanges.length > 0) {
            allChanges.push(...manualChanges);
        }
        // Apply approved changes to spec files
        await applyChangesToSpecFiles(spec, allChanges);
        // Update spec metadata if needed
        const approvedChanges = allChanges.filter((c) => c.approved);
        const hasStatusChange = approvedChanges.find((c) => c.section === 'status');
        if (hasStatusChange) {
            await specStore.updateSpec(projectId, specId, {
                status: hasStatusChange.newValue,
            });
        }
        // Update PLAN.md if spec has one and there were approved changes
        if (spec.planPath && approvedChanges.length > 0) {
            await appendPlanReconcileNote(spec.planPath, approvedChanges);
        }
        // Create a new SpecVersion if there were approved changes
        let createdVersion = null;
        if (approvedChanges.length > 0) {
            const currentVersion = await specStore.getLatestVersion(projectId, specId);
            const version = createVersion(specId, currentVersion, approvedChanges, `Reconcile: ${approvedChanges.length} change(s) applied`);
            await specStore.saveVersion(projectId, specId, version);
            createdVersion = version.version;
        }
        // Mark spec as reconciled by updating the timestamp
        await specStore.updateSpec(projectId, specId, {
            updatedAt: new Date().toISOString(),
        });
        const report = {
            specId,
            changes: allChanges,
            specUpdated: approvedChanges.length > 0,
            reason: approvedChanges.length > 0
                ? `Applied ${approvedChanges.length} change(s) to spec`
                : 'No changes to apply',
            reconciledAt: new Date().toISOString(),
        };
        // Generate changelog entry if there were approved changes (AC-02, AC-08)
        let changelogUpdated = false;
        if (approvedChanges.length > 0 && spec.specPath) {
            const specDir = dirname(spec.specPath);
            changelogUpdated = await appendChangelogEntry({
                specDir,
                specId,
                entry: {
                    timestamp: new Date().toISOString(),
                    type: 'reconciled',
                    description: `Se reconcilio la spec. ${approvedChanges.length} cambio(s) aplicado(s): ${approvedChanges.map((c) => c.section).join(', ')}.`,
                    triggeredBy: 'reconcile_spec',
                    before: { criteriaCount: allChanges.length, completedCount: 0, status: spec.status },
                    after: {
                        criteriaCount: allChanges.length,
                        completedCount: 0,
                        status: hasStatusChange ? hasStatusChange.newValue : spec.status,
                    },
                },
            });
        }
        const result = {
            ...report,
            versionCreated: createdVersion,
            changelogUpdated,
            summary: {
                totalChanges: allChanges.length,
                approvedChanges: approvedChanges.length,
                pendingChanges: allChanges.filter((c) => !c.approved).length,
                autoDetected: autoDetect
                    ? allChanges.filter((c) => !manualChanges?.some((mc) => mc.section === c.section)).length
                    : 0,
                manual: manualChanges?.length ?? 0,
            },
            message: ti('tools.reconcile_spec.success', {
                specId,
                count: String(allChanges.length),
            }),
        };
        return {
            content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
        };
    }
    catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        return {
            content: [{ type: 'text', text: ti('errors.internalError', { message }) }],
            isError: true,
        };
    }
}
//# sourceMappingURL=reconcile-spec.js.map