import { ti } from '../i18n/index.js';
import { specStore, metricsStore, knowledgeStore } from '../storage/index.js';
import { calculateAccuracy } from '../engine/estimator.js';
import { validateDoR } from '../engine/dor-dod.js';
import { generateDoD } from '../engine/validator/dor-dod.js';
import { cascadeCheck } from '../engine/spec-versioner.js';
import { dispatchFeedbackEvent } from './learn.js';
import { generateProgressUpdate, generateProgressContent } from '../engine/progress-writer.js';
import { runImplementingActions, runDoneActions, checkConstitutionOnTransition, } from './update-status-actions.js';
import { readFile, writeFile, mkdir } from 'node:fs/promises';
import { dirname } from 'node:path';
import { appendChangelogEntry } from '../engine/spec-changelog/index.js';
import { dispatchHookEvent } from './manage-hooks.js';
import { buildStatusChangePayload } from '../engine/hooks/triggers.js';
import { regenerateSpecSummaryHtml } from '../engine/spec-summary-html.js';
/**
 * Valid state transitions for spec lifecycle.
 * draft -> review -> approved -> implementing -> done
 * Also allow: review -> draft (send back), approved -> review (reconsider)
 */
const VALID_TRANSITIONS = {
    draft: ['review'],
    review: ['draft', 'approved'],
    approved: ['review', 'implementing'],
    implementing: ['approved', 'done'],
    done: [], // terminal state
};
export async function handleUpdateStatus(params) {
    const { specId, projectId, status: newStatus, actuals, reviewNotes } = params;
    try {
        // Get the current spec
        const spec = await specStore.getSpec(projectId, specId);
        if (!spec) {
            return {
                content: [{ type: 'text', text: ti('spec.notFound', { id: specId }) }],
                isError: true,
            };
        }
        const currentStatus = spec.status;
        // Validate state transition
        const validNextStates = VALID_TRANSITIONS[currentStatus];
        if (!validNextStates.includes(newStatus)) {
            return {
                content: [
                    {
                        type: 'text',
                        text: ti('spec.invalidTransition', {
                            from: currentStatus,
                            to: newStatus,
                        }),
                    },
                ],
                isError: true,
            };
        }
        // Gate: DoR must pass before transitioning to 'implementing'
        if (newStatus === 'implementing') {
            const dorResult = validateDoR(spec);
            if (!dorResult.isValid) {
                void dispatchFeedbackEvent(projectId, {
                    type: 'checklist',
                    specId,
                    input: { gate: 'dor', specType: spec.type },
                    outcome: { passed: false, failedItems: dorResult.blockingItems },
                    delta: { failedCount: dorResult.blockingItems.length },
                    timestamp: new Date().toISOString(),
                });
                return {
                    content: [
                        {
                            type: 'text',
                            text: JSON.stringify({
                                error: 'DoR gate failed',
                                message: dorResult.humanMessage,
                                expertSummary: dorResult.expertSummary,
                                blockingItems: dorResult.blockingItems,
                                warningItems: dorResult.warningItems,
                            }, null, 2),
                        },
                    ],
                    isError: true,
                };
            }
        }
        // Gate: DoD must pass before transitioning to 'done' (unless force=true)
        if (newStatus === 'done' && !params.force) {
            const dod = await generateDoD(spec);
            const requiredItems = dod.items.filter((i) => i.required);
            const blockingItems = requiredItems
                .filter((i) => i.status === 'failed')
                .map((i) => i.description);
            const warningItems = dod.items
                .filter((i) => !i.required && i.status !== 'passed')
                .map((i) => i.description);
            if (blockingItems.length > 0) {
                const humanMessage = `Spec "${spec.title}" has ${blockingItems.length} blocking DoD item(s): ${blockingItems.slice(0, 3).join('; ')}${blockingItems.length > 3 ? ` (and ${blockingItems.length - 3} more)` : ''}`;
                const expertSummary = `DoD: ${dod.passedCount}/${dod.items.length} passed | Blocking: ${blockingItems.length} | Warnings: ${warningItems.length}`;
                void dispatchFeedbackEvent(projectId, {
                    type: 'checklist',
                    specId,
                    input: { gate: 'dod', specType: spec.type },
                    outcome: { passed: false, failedItems: blockingItems },
                    delta: { failedCount: blockingItems.length },
                    timestamp: new Date().toISOString(),
                });
                return {
                    content: [
                        {
                            type: 'text',
                            text: JSON.stringify({
                                error: 'DoD gate failed',
                                message: humanMessage,
                                expertSummary,
                                blockingItems,
                                warningItems,
                                hint: 'Use force: true to bypass DoD gates if validate already confirmed readiness.',
                            }, null, 2),
                        },
                    ],
                    isError: true,
                };
            }
        }
        // Constitution compliance check (best-effort, non-blocking)
        const constitutionWarnings = newStatus === 'approved' || newStatus === 'done'
            ? await checkConstitutionOnTransition(projectId, spec.title, spec.tags)
            : [];
        // Run transition-specific actions
        const implActions = newStatus === 'implementing' ? await runImplementingActions(projectId, specId) : null;
        const doneActions = newStatus === 'done' ? await runDoneActions(projectId, specId, spec.gitBranch) : null;
        // If transitioning to 'done', actuals are required
        if (newStatus === 'done' && !actuals) {
            return {
                content: [{ type: 'text', text: ti('spec.actualsRequired', {}) }],
                isError: true,
            };
        }
        // Build the update
        const updates = {
            status: newStatus,
        };
        // Store review notes when sending back (review→draft or approved→review)
        if (reviewNotes) {
            const existing = spec.reviewNotes ?? [];
            updates.reviewNotes = [
                ...existing,
                `[${new Date().toISOString()}] ${currentStatus}→${newStatus}: ${reviewNotes}`,
            ];
        }
        // If actuals are provided, save them
        if (actuals) {
            updates.actuals = actuals;
        }
        // Update the spec
        const updatedSpec = await specStore.updateSpec(projectId, specId, updates);
        // Update progress.md (best-effort — never blocks status update)
        if (updatedSpec.progressPath) {
            try {
                const existing = await readFile(updatedSpec.progressPath, 'utf-8');
                const updatedProgress = generateProgressUpdate(updatedSpec, currentStatus, existing);
                await writeFile(updatedSpec.progressPath, updatedProgress, 'utf-8');
            }
            catch {
                try {
                    const progressContent = generateProgressContent(updatedSpec);
                    await mkdir(dirname(updatedSpec.progressPath), { recursive: true });
                    await writeFile(updatedSpec.progressPath, progressContent, 'utf-8');
                }
                catch {
                    /* best-effort */
                }
            }
        }
        // If spec is done and has actuals, record metrics
        if (newStatus === 'done' && actuals) {
            const accuracyScore = calculateAccuracy(spec.estimation, actuals.devHours + actuals.reviewHours);
            const metricEntry = {
                specId,
                difficulty: spec.difficulty,
                estimated: spec.estimation,
                actual: actuals,
                accuracyScore,
            };
            await metricsStore.addMetricEntry(projectId, metricEntry);
            // Auto-learn from estimation accuracy (best-effort)
            if (accuracyScore < 0.7 || accuracyScore > 1.3) {
                try {
                    void dispatchFeedbackEvent(projectId, {
                        type: 'validate',
                        specId,
                        input: { specType: spec.type, scope: spec.scope, difficulty: spec.difficulty },
                        outcome: {
                            estimationAccuracy: accuracyScore,
                            estimatedHours: spec.estimation.devHours + spec.estimation.reviewHours,
                            actualHours: actuals.devHours + actuals.reviewHours,
                        },
                        delta: { accuracyDrift: Math.abs(1 - accuracyScore) },
                        timestamp: new Date().toISOString(),
                    });
                }
                catch {
                    /* best-effort */
                }
            }
        }
        // Generate changelog entry for status change (AC-03, AC-08)
        let changelogUpdated = false;
        if (updatedSpec.specPath) {
            const specDir = dirname(updatedSpec.specPath);
            changelogUpdated = await appendChangelogEntry({
                specDir,
                specId,
                entry: {
                    timestamp: new Date().toISOString(),
                    type: 'status_changed',
                    description: `Estado cambio de \`${currentStatus}\` a \`${newStatus}\`.`,
                    triggeredBy: 'update_status',
                    before: { criteriaCount: 0, completedCount: 0, status: currentStatus },
                    after: { criteriaCount: 0, completedCount: 0, status: newStatus },
                },
            });
        }
        // Cascade check: notify downstream specs of the status change
        const allSpecs = await specStore.listSpecs(projectId);
        const cascadeResult = cascadeCheck(specId, allSpecs, newStatus);
        const result = {
            specId,
            projectId,
            previousStatus: currentStatus,
            newStatus,
            actuals: actuals ?? null,
            autoBranch: implActions?.autoBranch ?? null,
            protectedBranchWarning: implActions?.protectedBranchWarning ?? null,
            mergeWarning: doneActions?.mergeWarning ?? null,
            validationWarning: doneActions?.validationWarning ?? null,
            prSuggestion: doneActions?.prSuggestion ?? null,
            changelogUpdated,
            implementingSuggestions: implActions?.suggestions ?? null,
            doneSuggestions: newStatus === 'done'
                ? [
                    'Run reconcile_spec to sync spec with actual implementation',
                    'Run generate_docs to update project documentation',
                ]
                : null,
            constitutionWarnings: constitutionWarnings.length > 0 ? constitutionWarnings : null,
            cascade: cascadeResult,
            metrics: newStatus === 'done' && actuals
                ? {
                    accuracyScore: calculateAccuracy(spec.estimation, actuals.devHours + actuals.reviewHours),
                    estimatedHours: spec.estimation.devHours + spec.estimation.reviewHours,
                    actualHours: actuals.devHours + actuals.reviewHours,
                    estimatedCost: spec.estimation.totalCostUsd,
                    actualCost: actuals.totalCostUsd,
                }
                : null,
            updatedAt: updatedSpec.updatedAt,
            message: ti('tools.update_status.success', { specId, status: newStatus }),
        };
        // Auto-regenerate planu/index.html (fire-and-forget, never blocks result)
        void knowledgeStore
            .getKnowledge(projectId)
            .then((knowledge) => {
            if (knowledge?.projectPath) {
                void regenerateSpecSummaryHtml(knowledge.projectPath, allSpecs);
            }
        })
            .catch(() => {
            /* best-effort */
        });
        // Dispatch on_spec_status_change hook event (fire-and-forget)
        void dispatchHookEvent(projectId, buildStatusChangePayload(projectId, specId, spec.title, currentStatus, newStatus));
        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=update-status.js.map