import { ti } from '../i18n/index.js';
import { specStore, metricsStore } from '../storage/index.js';
import { calculateAccuracy } from '../engine/estimator.js';
import { validateDoR, validateDoD } from '../engine/dor-dod.js';
import { cascadeCheck } from '../engine/spec-versioner.js';
import { dispatchFeedbackEvent } from './learn.js';
import { handleCreateBranch } from './git/branch-ops.js';
import { handleGeneratePr } from './git/pr-ops.js';
import { generateProgressUpdate, generateProgressContent } from '../engine/progress-writer.js';
import { readFile, writeFile } 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 { handleValidate } from './validate.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) {
                // SPEC-012: Record failed DoR items for auto-improvement loop
                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'
        if (newStatus === 'done') {
            const dodResult = validateDoD(spec);
            if (!dodResult.isValid) {
                // SPEC-012: Record failed DoD items for auto-improvement loop
                void dispatchFeedbackEvent(projectId, {
                    type: 'checklist',
                    specId,
                    input: { gate: 'dod', specType: spec.type },
                    outcome: { passed: false, failedItems: dodResult.blockingItems },
                    delta: { failedCount: dodResult.blockingItems.length },
                    timestamp: new Date().toISOString(),
                });
                return {
                    content: [
                        {
                            type: 'text',
                            text: JSON.stringify({
                                error: 'DoD gate failed',
                                message: dodResult.humanMessage,
                                expertSummary: dodResult.expertSummary,
                                blockingItems: dodResult.blockingItems,
                                warningItems: dodResult.warningItems,
                            }, null, 2),
                        },
                    ],
                    isError: true,
                };
            }
        }
        // Auto-create branch when transitioning to 'implementing' (always, not opt-in)
        let autoBranchResult;
        let protectedBranchWarning = null;
        if (newStatus === 'implementing') {
            // Check if on a protected branch
            try {
                const { resolveProjectPath, git: gitCmd } = await import('./git/git-helpers.js');
                const projectPath = await resolveProjectPath(projectId);
                const { stdout } = await gitCmd(projectPath, ['rev-parse', '--abbrev-ref', 'HEAD']);
                const currentBranch = stdout.trim();
                const protectedBranches = ['main', 'master', 'develop', 'production'];
                if (protectedBranches.includes(currentBranch)) {
                    protectedBranchWarning = `Currently on protected branch '${currentBranch}'. Consider switching to a feature branch.`;
                }
            }
            catch {
                // Not a git repo — skip check
            }
            const foundSpec = await specStore.getSpec(projectId, specId);
            if (foundSpec && !foundSpec.gitBranch) {
                const branchResult = await handleCreateBranch(projectId, specId);
                if (!branchResult.isError) {
                    try {
                        const branchData = JSON.parse(branchResult.content[0]?.text ?? '{}');
                        autoBranchResult = branchData.branchName;
                    }
                    catch {
                        // ignore parse error
                    }
                }
            }
        }
        // Verify merge and generate PR info when transitioning to 'done'
        let mergeWarning = null;
        let prSuggestion = null;
        if (newStatus === 'done') {
            if (spec.gitBranch) {
                // Check if branch has been merged
                try {
                    const { resolveProjectPath, git: gitCmd } = await import('./git/git-helpers.js');
                    const projectPath = await resolveProjectPath(projectId);
                    const { stdout } = await gitCmd(projectPath, ['branch', '--merged', 'develop']);
                    const mergedBranches = stdout.split('\n').map((b) => b.trim().replace('* ', ''));
                    if (!mergedBranches.includes(spec.gitBranch)) {
                        mergeWarning = `Branch '${spec.gitBranch}' has not been merged to develop yet. Consider merging before marking as done.`;
                    }
                }
                catch {
                    // Not a git repo or branch check failed — skip
                }
                // Auto-generate PR description (best-effort)
                try {
                    const prResult = await handleGeneratePr(projectId, specId);
                    if (!prResult.isError) {
                        prSuggestion = JSON.parse(prResult.content[0]?.text ?? '{}');
                    }
                }
                catch {
                    // PR generation is best-effort
                }
            }
        }
        // Auto-validate coverage when transitioning to 'done' (non-blocking)
        let validationWarning = null;
        if (newStatus === 'done') {
            try {
                const valResult = await handleValidate({ specId, projectId });
                if (!valResult.isError && valResult.content[1]?.text) {
                    const parsed = JSON.parse(valResult.content[1].text);
                    const score = parsed.score ?? 100;
                    if (score < 80) {
                        validationWarning = `Coverage score is ${String(score)}%. Consider improving before marking done.`;
                    }
                }
            }
            catch {
                // Validation is best-effort — continue with transition
            }
        }
        // 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 if it exists
        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 {
                // If progress.md doesn't exist yet, create it
                const progressContent = generateProgressContent(updatedSpec);
                await writeFile(updatedSpec.progressPath, progressContent, 'utf-8');
            }
        }
        // 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);
        }
        // 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: autoBranchResult ?? null,
            protectedBranchWarning,
            mergeWarning,
            validationWarning,
            prSuggestion,
            changelogUpdated,
            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 }),
        };
        // 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