"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createWorkspaceMigrateCommand = createWorkspaceMigrateCommand;
const errors_1 = require("../../../lib/errors");
const output_1 = require("../../../lib/output");
const workspace_compatibility_1 = require("../../../lib/workspace-compatibility");
const workspace_migration_planning_1 = require("../../../lib/planning/workspace-migration-planning");
const workspace_migration_application_1 = require("../../../lib/planning/workspace-migration-application");
const operational_recovery_1 = require("../../../lib/filesystem/operational-recovery");
const runtime_contract_1 = require("../../../lib/runtime/runtime-contract");
const global_format_1 = require("../global-format");
async function createWorkspaceMigrateCommand() {
    const { Command } = await import('commander');
    const command = new Command('migrate');
    command
        .description('Check, plan, or apply explicit Day Shift workspace migrations.')
        .option('--target-schema-version <version>', 'Supported target schema version for workspace metadata.')
        .option('--dry-run', 'Report the migration plan without writing files.')
        .option('--check', 'Check structural migration actionability without writing files.')
        .option('--plan', 'Emit the complete structural migration plan without writing files.')
        .option('--apply', 'Apply an exact structural migration plan through the repository transaction boundary.')
        .option('--recover <action>', 'Recover an interrupted migration transaction: roll-forward or restore.')
        .option('--transaction-id <id>', 'Exact interrupted migration transaction id.')
        .option('--evidence-fingerprint <fingerprint>', 'Exact current operational inspection fingerprint.')
        .option('--actor <actor>', 'Bounded recovery audit actor.')
        .option('--reason <reason>', 'Bounded recovery audit reason.')
        .option('--recovered-at <timestamp>', 'Optional recovery ISO-8601 timestamp.')
        .option('--expected-plan-digest <digest>', 'Exact structural plan digest authorized for --apply.')
        .option('--mapping <work-unit-id=level>', 'Select Basic, Structured, or Governed for one reported legacy work unit. Repeat as needed.', collectOption, [])
        .option('--root-order <work-unit-id=order>', 'Resolve a selected Basic or Structured root order explicitly; repeat as needed.', collectOption, [])
        .option('--task-sequence <work-unit-id:task-id=sequence>', 'Resolve a selected Structured parent-local task sequence explicitly; repeat as needed.', collectOption, [])
        .option('--confirm <token>', 'Required write confirmation token: apply-workspace-migration.')
        .option('--family <family>', 'Migration family. Only full workspace migrations are currently supported.')
        .option('--field <field>', 'Rejected partial field migration request; use full workspace migration instead.')
        .action((_, commandInstance) => {
        const format = (0, output_1.resolveOutputFormat)((0, global_format_1.getGlobalFormat)(commandInstance));
        if (format === null) {
            throw (0, errors_1.createUsageError)('Output format must be one of: text, json.', [
                `Received: ${String((0, global_format_1.getGlobalFormat)(commandInstance))}`,
            ]);
        }
        const commandOptions = commandInstance.opts();
        const repositoryContext = (0, runtime_contract_1.createRepositoryContext)((0, runtime_contract_1.detectRepositoryRoot)(), false);
        if (commandOptions.recover !== undefined) {
            const recovery = recoverConfirmedWorkspaceMigration({
                repositoryRoot: repositoryContext.root,
                options: commandOptions,
            });
            (0, output_1.emitSuccess)({
                command: 'workspace migrate',
                format,
                text: renderWorkspaceMigrationRecoveryText(recovery),
                data: {
                    repositoryRoot: repositoryContext.root,
                    detectionSource: repositoryContext.detectionSource,
                    readOnly: false,
                    writeApplied: recovery.changedPaths.length > 0,
                    structuralMode: 'recover',
                    recovery,
                },
            });
            return;
        }
        rejectRecoveryOnlyOptions(commandOptions);
        const structuralMode = resolveStructuralMode(commandOptions);
        if (structuralMode !== null) {
            const selections = (0, workspace_migration_planning_1.applyWorkspaceMigrationOrderingSelections)((0, workspace_migration_planning_1.parseWorkspaceMigrationSelections)(commandOptions.mapping ?? []), commandOptions.rootOrder ?? [], commandOptions.taskSequence ?? []);
            const migration = structuralMode === 'apply'
                ? applyConfirmedStructuralMigration({
                    repositoryRoot: repositoryContext.root,
                    selections,
                    expectedPlanDigest: commandOptions.expectedPlanDigest,
                    confirm: commandOptions.confirm,
                })
                : (0, workspace_migration_planning_1.planWorkspaceStructureMigrationSync)({
                    repositoryRoot: repositoryContext.root,
                    selections,
                });
            (0, output_1.emitSuccess)({
                command: 'workspace migrate',
                format,
                text: structuralMode === 'apply'
                    ? renderWorkspaceStructureMigrationApplyText(migration)
                    : renderWorkspaceStructureMigrationText(structuralMode, migration),
                data: {
                    repositoryRoot: repositoryContext.root,
                    detectionSource: repositoryContext.detectionSource,
                    readOnly: structuralMode !== 'apply',
                    writeApplied: 'writeApplied' in migration && migration.writeApplied,
                    structuralMode,
                    migration,
                },
            });
            return;
        }
        const targetSchemaVersion = parseTargetSchemaVersion(commandOptions.targetSchemaVersion);
        rejectUnsafeMigrationRequest({
            targetSchemaVersion,
            family: commandOptions.family,
            field: commandOptions.field,
        });
        const migration = commandOptions.dryRun
            ? requireRunnableMigration((0, workspace_compatibility_1.planWorkspaceSchemaMigration)({
                repositoryRoot: repositoryContext.root,
                targetSchemaVersion,
            }))
            : applyConfirmedMigration({
                repositoryRoot: repositoryContext.root,
                targetSchemaVersion,
                confirm: commandOptions.confirm,
            });
        (0, output_1.emitSuccess)({
            command: 'workspace migrate',
            format,
            text: renderWorkspaceMigrationPlanText(migration),
            data: {
                repositoryRoot: repositoryContext.root,
                detectionSource: repositoryContext.detectionSource,
                migration,
            },
        });
    });
    return command;
}
function recoverConfirmedWorkspaceMigration(options) {
    const requested = options.options;
    if (requested.confirm !== 'recover-workspace-migration') {
        throw createWorkspaceMigrationRejectedError({
            category: 'missing-recovery-confirmation',
            detail: 'Migration recovery requires --confirm recover-workspace-migration.',
            requestedTarget: String(requested.confirm ?? '(missing)'),
        });
    }
    const incompatible = [
        requested.check ? '--check' : null,
        requested.plan ? '--plan' : null,
        requested.apply ? '--apply' : null,
        requested.dryRun ? '--dry-run' : null,
        requested.targetSchemaVersion !== undefined ? '--target-schema-version' : null,
        requested.family !== undefined ? '--family' : null,
        requested.field !== undefined ? '--field' : null,
        Array.isArray(requested.mapping) && requested.mapping.length > 0 ? '--mapping' : null,
        Array.isArray(requested.rootOrder) && requested.rootOrder.length > 0 ? '--root-order' : null,
        Array.isArray(requested.taskSequence) && requested.taskSequence.length > 0 ? '--task-sequence' : null,
        requested.expectedPlanDigest !== undefined ? '--expected-plan-digest' : null,
    ].filter((entry) => entry !== null);
    if (incompatible.length > 0) {
        throw (0, errors_1.createUsageError)('Migration recovery cannot be combined with planning or apply options.', [
            `Remove: ${incompatible.join(', ')}.`,
        ]);
    }
    const action = requested.recover;
    if (action !== 'roll-forward' && action !== 'restore') {
        throw (0, errors_1.createUsageError)('Migration recovery action must be roll-forward or restore.', [
            `Received: ${String(action)}`,
        ]);
    }
    const required = (name, value) => {
        if (typeof value !== 'string' || value.trim() === '') {
            throw (0, errors_1.createUsageError)(`${name} is required for migration recovery.`, []);
        }
        return value;
    };
    return (0, operational_recovery_1.recoverOperationalTransactionSync)(options.repositoryRoot, {
        transactionId: required('--transaction-id', requested.transactionId),
        evidenceFingerprint: required('--evidence-fingerprint', requested.evidenceFingerprint),
        action: action,
        expectedTransactionKind: 'migration',
        actor: required('--actor', requested.actor),
        reason: required('--reason', requested.reason),
        ...(requested.recoveredAt === undefined ? {} : { recoveredAt: required('--recovered-at', requested.recoveredAt) }),
    });
}
function rejectRecoveryOnlyOptions(options) {
    const recoveryOnly = [
        options.transactionId !== undefined ? '--transaction-id' : null,
        options.evidenceFingerprint !== undefined ? '--evidence-fingerprint' : null,
        options.actor !== undefined ? '--actor' : null,
        options.reason !== undefined ? '--reason' : null,
        options.recoveredAt !== undefined ? '--recovered-at' : null,
    ].filter((entry) => entry !== null);
    if (recoveryOnly.length > 0) {
        throw (0, errors_1.createUsageError)('Migration recovery evidence options require --recover.', [
            `Remove: ${recoveryOnly.join(', ')}, or add --recover.`,
        ]);
    }
}
function collectOption(value, prior) {
    return [...prior, value];
}
function resolveStructuralMode(options) {
    const selectedModes = [options.check, options.plan, options.apply].filter(Boolean).length;
    if (selectedModes > 1) {
        throw (0, errors_1.createUsageError)('Structural workspace migration modes are mutually exclusive.', [
            'Choose exactly one of --check, --plan, or --apply.',
        ]);
    }
    const structuralMode = options.check ? 'check' : options.plan ? 'plan' : options.apply ? 'apply' : null;
    const hasMappings = (options.mapping?.length ?? 0) > 0 ||
        (options.rootOrder?.length ?? 0) > 0 ||
        (options.taskSequence?.length ?? 0) > 0;
    if (structuralMode === null) {
        if (hasMappings || options.expectedPlanDigest !== undefined) {
            throw (0, errors_1.createUsageError)('Structural migration options require --check, --plan, or --apply.', [
                'Add --plan to review the selected mapping and digest without writes.',
            ]);
        }
        return null;
    }
    const incompatible = [
        options.targetSchemaVersion !== undefined ? '--target-schema-version' : null,
        options.dryRun ? '--dry-run' : null,
        options.family !== undefined ? '--family' : null,
        options.field !== undefined ? '--field' : null,
    ].filter((entry) => entry !== null);
    if (structuralMode !== 'apply') {
        if (options.confirm !== undefined)
            incompatible.push('--confirm');
        if (options.expectedPlanDigest !== undefined)
            incompatible.push('--expected-plan-digest');
    }
    if (incompatible.length > 0) {
        throw (0, errors_1.createUsageError)(`Structural ${structuralMode} cannot be combined with these options.`, [
            `Remove: ${incompatible.join(', ')}.`,
        ]);
    }
    return structuralMode;
}
function applyConfirmedStructuralMigration(options) {
    if (options.confirm !== 'apply-workspace-migration') {
        throw createWorkspaceMigrationRejectedError({
            category: 'missing-confirmation',
            detail: 'Structural workspace migration writes require --confirm apply-workspace-migration.',
            requestedTarget: String(options.confirm ?? '(missing)'),
        });
    }
    if (typeof options.expectedPlanDigest !== 'string' || !/^sha256:[a-f0-9]{64}$/.test(options.expectedPlanDigest)) {
        throw createWorkspaceMigrationRejectedError({
            category: 'missing-plan-digest',
            detail: 'Structural workspace migration writes require --expected-plan-digest sha256:<hex>.',
            requestedTarget: String(options.expectedPlanDigest ?? '(missing)'),
        });
    }
    try {
        return (0, workspace_migration_application_1.applyWorkspaceStructureMigrationSync)({
            repositoryRoot: options.repositoryRoot,
            expectedPlanDigest: options.expectedPlanDigest,
            selections: options.selections,
        });
    }
    catch (error) {
        if (!(error instanceof workspace_migration_application_1.WorkspaceMigrationApplicationError))
            throw error;
        throw createWorkspaceMigrationRejectedError({
            category: error.code,
            detail: error.message,
            requestedTarget: options.expectedPlanDigest,
        });
    }
}
function rejectUnsafeMigrationRequest(options) {
    if (options.targetSchemaVersion < 1) {
        throw createWorkspaceMigrationRejectedError({
            category: 'unsupported-target',
            detail: `Target schema version ${options.targetSchemaVersion} is not supported.`,
            requestedTarget: String(options.targetSchemaVersion),
        });
    }
    if (typeof options.family === 'string' && options.family !== 'workspace') {
        throw createWorkspaceMigrationRejectedError({
            category: 'cross-family',
            detail: `Migration family '${options.family}' is not supported by workspace migration.`,
            requestedTarget: options.family,
        });
    }
    if (typeof options.field === 'string' && options.field.trim().length > 0) {
        throw createWorkspaceMigrationRejectedError({
            category: 'partial-request',
            detail: `Partial field migration '${options.field}' is not supported.`,
            requestedTarget: options.field,
        });
    }
}
function requireRunnableMigration(migration) {
    if (migration.status !== 'blocked') {
        return migration;
    }
    const firstBlocked = migration.blockedArtifacts[0];
    throw createWorkspaceMigrationRejectedError({
        category: firstBlocked?.reason ?? 'blocked-plan',
        detail: firstBlocked?.message ?? 'Workspace migration plan is blocked.',
        requestedTarget: String(migration.targetSchemaVersion),
        path: firstBlocked?.path,
        evidenceDetails: {
            blockedArtifacts: migration.blockedArtifacts,
        },
    });
}
function createWorkspaceMigrationRejectedError(options) {
    return (0, errors_1.createBlockingInvalidConfigError)({
        message: 'Workspace migration request was rejected.',
        detail: options.detail,
        findingCode: 'WORKSPACE_MIGRATION_REJECTED',
        findingMessage: options.detail,
        path: options.path,
        blockingReason: 'invalid-command-input',
        recovery: 'Rerun a full workspace migration with a supported target schema version after reviewing a dry-run plan. No files, backups, or checkpoints were written for this rejected request.',
        evidenceDetails: {
            category: options.category,
            requestedTarget: options.requestedTarget,
            noWriteBoundary: 'No files, backups, or checkpoints were written.',
            ...(options.evidenceDetails ?? {}),
        },
    });
}
function applyConfirmedMigration(options) {
    if (options.confirm !== 'apply-workspace-migration') {
        throw createWorkspaceMigrationRejectedError({
            category: 'missing-confirmation',
            detail: 'Workspace migration writes require --confirm apply-workspace-migration.',
            requestedTarget: String(options.confirm ?? '(missing)'),
        });
    }
    return requireRunnableMigration((0, workspace_compatibility_1.applyWorkspaceSchemaMigration)({
        repositoryRoot: options.repositoryRoot,
        targetSchemaVersion: options.targetSchemaVersion,
        backupRoot: `.day-shift/backups/workspace-migrations/${new Date().toISOString().replace(/[:.]/g, '-')}`,
    }));
}
function parseTargetSchemaVersion(value) {
    const parsed = typeof value === 'string' && /^\d+$/.test(value) ? Number.parseInt(value, 10) : Number.NaN;
    if (!Number.isSafeInteger(parsed)) {
        throw (0, errors_1.createUsageError)('Target schema version must be a positive integer.', [`Received: ${String(value)}`]);
    }
    return parsed;
}
function renderWorkspaceStructureMigrationText(mode, migration) {
    return [
        (0, output_1.renderTerminalHeading)('Structural Workspace Migration Preview', 'title'),
        (0, output_1.renderTerminalSection)({
            heading: 'Summary',
            fields: [
                { label: 'Mode', value: mode },
                { label: 'Status', value: migration.status },
                { label: 'Read-only', value: 'yes' },
                { label: 'Write applied', value: 'no' },
                { label: 'Plan digest', value: migration.planDigest },
                { label: 'Work units', value: migration.workUnits.length },
                { label: 'Blocking findings', value: migration.findings.length },
                { label: 'Affected paths', value: migration.affectedPaths.length },
                { label: 'Metadata insertions', value: migration.metadataInsertions.length },
                { label: 'Revision rebindings', value: migration.revisionRebindings.length },
                { label: 'Recovery actions', value: migration.recoveryActions.length },
            ],
        }),
        '',
        (0, output_1.renderTerminalCompactList)({
            heading: 'Work Units',
            entries: migration.workUnits.map((unit) => ({
                label: unit.workUnitId,
                fields: [
                    { label: 'Actionable', value: unit.actionable ? 'yes' : 'no' },
                    { label: 'Proposed level', value: unit.proposedWorkflowLevel ?? '(ambiguous)' },
                    { label: 'Selected level', value: unit.selectedWorkflowLevel ?? '(missing)' },
                    { label: 'Selection source', value: unit.selectionSource ?? '(none)' },
                    { label: 'Work id', value: unit.workId },
                    { label: 'Implementation order', value: unit.implementationOrder ?? '(not applicable)' },
                    { label: 'Order source', value: unit.implementationOrderSource ?? '(not applicable)' },
                    { label: 'Ordered root', value: unit.orderedRootName ?? '(not applicable)' },
                    { label: 'Sources', value: unit.sourcePaths.length },
                    { label: 'Destinations', value: unit.destinationPaths.length },
                ],
            })),
            emptyMessage: 'No legacy work units were detected.',
        }),
        '',
        (0, output_1.renderTerminalCompactList)({
            heading: 'Blocking Findings',
            entries: migration.findings.map((entry) => ({
                label: entry.code,
                fields: [
                    { label: 'Paths', value: entry.paths.join(', ') || '(none)' },
                    { label: 'Message', value: entry.message },
                    { label: 'Recovery', value: entry.recovery },
                ],
            })),
            emptyMessage: 'No structural migration blockers were found.',
        }),
        '',
        (0, output_1.renderTerminalSection)({
            heading: 'Recovery',
            fields: [{ label: 'Guidance', value: migration.recoveryGuidance }],
        }),
        '',
        (0, output_1.renderTerminalBoundary)({ kind: 'read-only' }),
    ]
        .filter((entry) => entry !== '')
        .join('\n');
}
function renderWorkspaceStructureMigrationApplyText(migration) {
    return [
        (0, output_1.renderTerminalHeading)('Structural Workspace Migration Apply', 'title'),
        (0, output_1.renderTerminalSection)({
            heading: 'Summary',
            fields: [
                { label: 'Outcome', value: migration.outcome },
                { label: 'Committed', value: migration.committed ? 'yes' : 'no' },
                { label: 'Write applied', value: migration.writeApplied ? 'yes' : 'no' },
                { label: 'Plan digest', value: migration.planDigest },
                { label: 'Transaction', value: migration.transactionId ?? '(none)' },
                { label: 'Changed paths', value: migration.changedPaths.length },
                { label: 'Retired paths', value: migration.retiredPaths.length },
                { label: 'Recovery path', value: migration.recoveryPath ?? '(none)' },
            ],
        }),
        '',
        (0, output_1.renderTerminalCompactList)({
            heading: 'Changed Paths',
            entries: migration.changedPaths.map((entry) => ({ label: entry })),
            emptyMessage: 'No canonical paths were changed.',
        }),
        '',
        (0, output_1.renderTerminalSection)({
            heading: 'Next Action',
            fields: [{ label: 'Guidance', value: migration.nextAction }],
        }),
        '',
        (0, output_1.renderTerminalBoundary)(migration.writeApplied
            ? {
                kind: 'write-applied',
                target: 'structural workspace migration',
                targetLabel: 'Write target',
            }
            : {
                kind: 'mutating-no-op',
                reason: migration.failure ?? 'No structural migration write was committed.',
            }),
    ].join('\n');
}
function renderWorkspaceMigrationRecoveryText(recovery) {
    return [
        (0, output_1.renderTerminalHeading)('Structural Workspace Migration Recovery', 'title'),
        (0, output_1.renderTerminalSection)({
            heading: 'Summary',
            fields: [
                { label: 'Outcome', value: recovery.outcome },
                { label: 'Action', value: recovery.action },
                { label: 'Transaction', value: recovery.transactionId },
                { label: 'Changed paths', value: recovery.changedPaths.length },
                { label: 'Recovery record', value: recovery.recoveryRecordPath ?? '(none)' },
                { label: 'Recovery path', value: recovery.recoveryPath ?? '(none)' },
            ],
        }),
        '',
        (0, output_1.renderTerminalBoundary)(recovery.outcome === 'recovered'
            ? {
                kind: 'write-applied',
                target: 'structural workspace migration recovery',
                targetLabel: 'Write target',
            }
            : {
                kind: 'mutating-no-op',
                reason: recovery.failure ?? 'Migration recovery remains incomplete.',
            }),
    ].join('\n');
}
function renderWorkspaceMigrationPlanText(plan) {
    return [
        (0, output_1.renderTerminalHeading)('Workspace Migration Plan', 'title'),
        (0, output_1.renderTerminalSection)({
            heading: 'Summary',
            fields: [
                { label: 'Status', value: plan.status },
                { label: 'Target schema version', value: plan.targetSchemaVersion },
                { label: 'Dry-run', value: plan.dryRun ? 'yes' : 'no' },
                { label: 'Write applied', value: 'writeApplied' in plan && plan.writeApplied ? 'yes' : 'no' },
                { label: 'Planned changes', value: plan.changes.length },
                { label: 'Skipped artifacts', value: plan.skippedArtifacts.length },
                { label: 'Blocked artifacts', value: plan.blockedArtifacts.length },
            ],
        }),
        '',
        (0, output_1.renderTerminalCompactList)({
            heading: 'Planned Artifact Changes',
            entries: plan.changes.map((change) => ({
                label: change.path,
                fields: [
                    { label: 'Family', value: change.family },
                    { label: 'Field', value: change.field },
                    { label: 'Version', value: `${change.fromVersion} -> ${change.toVersion}` },
                    { label: 'Backup path', value: change.backupPath },
                    { label: 'Coordinated fields', value: change.coordinatedFields.join(', ') },
                ],
            })),
            emptyMessage: 'No artifact changes are planned.',
        }),
        '',
        (0, output_1.renderTerminalCompactList)({
            heading: 'Skipped Artifacts',
            entries: plan.skippedArtifacts.map((artifact) => ({
                label: artifact.path,
                fields: [
                    { label: 'Family', value: artifact.family },
                    { label: 'Field', value: artifact.field },
                    { label: 'Reason', value: artifact.reason },
                ],
            })),
            emptyMessage: 'No artifacts were skipped.',
        }),
        '',
        (0, output_1.renderTerminalCompactList)({
            heading: 'Blocked Artifacts',
            entries: plan.blockedArtifacts.map((artifact) => ({
                label: artifact.path,
                fields: [
                    { label: 'Family', value: artifact.family },
                    { label: 'Field', value: artifact.field },
                    { label: 'Reason', value: artifact.reason },
                    { label: 'Message', value: artifact.message },
                ],
            })),
            emptyMessage: 'No artifacts are blocked.',
        }),
        '',
        'updatedArtifacts' in plan
            ? (0, output_1.renderTerminalCompactList)({
                heading: 'Updated Artifacts',
                entries: plan.updatedArtifacts.map((artifactPath) => ({ label: artifactPath })),
                emptyMessage: 'No artifacts were updated.',
            })
            : '',
        'updatedArtifacts' in plan ? '' : '',
        'backupArtifacts' in plan
            ? (0, output_1.renderTerminalCompactList)({
                heading: 'Backup Artifacts',
                entries: plan.backupArtifacts.map((artifactPath) => ({ label: artifactPath })),
                emptyMessage: 'No backup artifacts were created.',
            })
            : '',
        'backupArtifacts' in plan ? '' : '',
        (0, output_1.renderTerminalSection)({
            heading: 'Write Boundary',
            lines: [
                plan.dryRun
                    ? (0, output_1.renderTerminalBoundary)({ kind: 'dry-run' })
                    : plan.status === 'no-op'
                        ? (0, output_1.renderTerminalBoundary)({
                            kind: 'mutating-no-op',
                            reason: 'Workspace already matched the requested schema migration target.',
                        })
                        : (0, output_1.renderTerminalBoundary)({
                            kind: 'write-applied',
                            target: 'workspace migration',
                            targetLabel: 'Write target',
                        }),
            ],
        }),
    ].join('\n');
}
