// engine/migration/rollback-planner.ts — Rollback planning engine (SPEC-028b)
// Generates rollback plans, checkpoints, and dependency-aware rollback order
// --- Reversibility classification ---
function classifyReversibility(comp, isDbOperation) {
    if (isDbOperation) {
        // DB schema changes that delete columns/tables are irreversible in practice
        return comp.temperature === 'hot' ? 'irreversible' : 'partially-reversible';
    }
    if (comp.temperature === 'hot') {
        return 'partially-reversible';
    }
    return 'reversible';
}
function buildIrreversibilityReason(comp, isDbOp) {
    if (isDbOp && comp.temperature === 'hot') {
        return 'Destructive schema change (column/table deletion) on a hot component — restoring from backup required if data was written to the new schema';
    }
    if (isDbOp) {
        return 'Schema migration may have received writes — point-in-time restore needed for full rollback';
    }
    return undefined;
}
// --- Checkpoint generation ---
function buildCheckpoints(plan) {
    const checkpoints = [];
    for (const phase of plan.phases) {
        checkpoints.push({
            checkpointId: `cp-before-phase-${phase.order}`,
            phase: phase.name,
            description: `Snapshot before starting ${phase.name} — safe rollback point before any phase-${phase.order} changes`,
            automatedBefore: true,
            conditions: [
                `All phase-${phase.order - 1} components validated`,
                'DB backup confirmed',
                'Service health checks green',
            ],
        });
    }
    // Point of no return: after last phase first component with destructive DB change
    checkpoints.push({
        checkpointId: 'cp-point-of-no-return',
        phase: 'Final cutover',
        description: 'Last safe rollback point before decommissioning source system and deleting old schema',
        automatedBefore: true,
        conditions: [
            'New system at 100% traffic for minimum 24h',
            'All parity checks passed',
            'Monitoring shows no regressions',
        ],
    });
    return checkpoints;
}
// --- Inverse DB migrations ---
function buildInverseDbMigrations(components) {
    return components
        .filter((c) => c.type === 'layer' ||
        c.name.toLowerCase().includes('db') ||
        c.name.toLowerCase().includes('schema'))
        .map((c) => ({
        originalMigration: `migrate-${c.id}.sql`,
        inverseScript: `-- Inverse of migrate-${c.id}.sql\n-- Restore previous schema state\n-- NOTE: Verify data integrity before executing\nALTER TABLE ... RENAME COLUMN ... ; -- placeholder: fill in from actual migration`,
        dataRestoreStrategy: `Restore from backup taken at checkpoint before ${c.name} migration`,
        practicalityNote: c.temperature === 'hot'
            ? `WARNING: High volume of writes expected on ${c.name} — rollback window shortens rapidly after migration starts`
            : undefined,
    }));
}
// --- Component rollback order (reverse topological) ---
function buildComponentRollbacks(components, sortedOrder) {
    // Rollback order is reverse of migration order
    const reverseOrder = [...sortedOrder].reverse();
    return reverseOrder.map((id, idx) => {
        const comp = components.find((c) => c.id === id);
        const name = comp?.name ?? id;
        const dependents = comp?.dependents ?? [];
        return {
            component: name,
            dependents,
            rollbackOrder: idx + 1,
            rollbackSteps: [
                `Verify ${dependents.length > 0 ? `dependents [${dependents.join(', ')}] have been rolled back first` : 'no dependents — safe to rollback'}`,
                `Stop traffic to migrated ${name}`,
                `Restore ${name} from last checkpoint`,
                `Redirect traffic back to source ${name}`,
                `Validate source ${name} health check passes`,
            ],
        };
    });
}
// --- Steps generation ---
function buildRollbackSteps(components, sortedOrder) {
    const reverseOrder = [...sortedOrder].reverse();
    const steps = [];
    reverseOrder.forEach((id, idx) => {
        const comp = components.find((c) => c.id === id);
        if (!comp) {
            return;
        }
        const isDbOp = comp.type === 'layer' && comp.name.toLowerCase().includes('db');
        const reversibility = classifyReversibility(comp, isDbOp);
        const irreversibilityReason = buildIrreversibilityReason(comp, isDbOp);
        steps.push({
            stepId: `rollback-${comp.id}`,
            description: `Rollback ${comp.name} (${comp.path})`,
            reversibility,
            irreversibilityReason,
            rollbackAction: isDbOp
                ? `Execute inverse migration script for ${comp.name} and restore data from backup`
                : `Redeploy previous version of ${comp.name} and update routing`,
            estimatedMinutes: Math.ceil(comp.linesOfCode / 200) * (reversibility === 'irreversible' ? 30 : 10),
            requiresConfirmation: reversibility !== 'reversible',
        });
        // Warn on irreversible steps
        if (reversibility === 'irreversible') {
            steps.push({
                stepId: `rollback-${comp.id}-warning`,
                description: `CRITICAL WARNING: ${comp.name} rollback is irreversible`,
                reversibility: 'irreversible',
                irreversibilityReason: 
                /* v8 ignore next 1 */ irreversibilityReason ?? 'Irreversible operation',
                rollbackAction: 'Ensure full backup exists and data restore plan is confirmed before proceeding',
                estimatedMinutes: 5,
                requiresConfirmation: true,
            });
        }
        // Insert as early as possible in the loop
        void idx;
    });
    return steps;
}
// --- Public API ---
export function generateRollbackPlan(params) {
    const { plan, components, sortedOrder } = params;
    const checkpoints = buildCheckpoints(plan);
    const steps = buildRollbackSteps(components, sortedOrder);
    const inverseDbMigrations = buildInverseDbMigrations(components);
    const componentRollbacks = buildComponentRollbacks(components, sortedOrder);
    const totalEstimatedMinutes = steps.reduce((sum, s) => sum + s.estimatedMinutes, 0);
    const pointOfNoReturn = 'After decommissioning source system OR after applying destructive DB schema changes that have received production writes';
    return {
        components: componentRollbacks,
        checkpoints,
        steps,
        inverseDbMigrations,
        pointOfNoReturn,
        totalEstimatedMinutes,
        generatedAt: new Date().toISOString(),
    };
}
export function createMigrationCheckpoint(params) {
    const checkpointId = `cp-${Date.now()}`;
    return {
        checkpointId,
        label: params.label,
        completedComponents: params.completedComponents,
        dbSchemaSnapshot: params.dbSchemaVersion ?? 'snapshot-pending',
        serviceConfiguration: params.serviceConfig ?? {},
        createdAt: new Date().toISOString(),
        projectId: params.projectId,
    };
}
export function buildRollbackExecutionPlan(params) {
    const { targetCheckpointId, rollbackPlan } = params;
    const orderedActions = rollbackPlan.steps.map((step, idx) => {
        const type = step.requiresConfirmation
            ? 'manual-confirmation-required'
            : 'automatic';
        return {
            order: idx + 1,
            description: step.description,
            type,
            reversibility: step.reversibility,
            command: step.reversibility === 'reversible' ? `./rollback.sh --step=${step.stepId}` : undefined,
        };
    });
    const requiresManualConfirmation = orderedActions.some((a) => a.type === 'manual-confirmation-required');
    return {
        targetCheckpointId,
        orderedActions,
        totalSteps: orderedActions.length,
        requiresManualConfirmation,
        generatedAt: new Date().toISOString(),
    };
}
//# sourceMappingURL=rollback-planner.js.map