/**
 * Migration system - schema version detection and migration runner.
 * @task T4468
 * @epic T4454
 */
// Re-export agent-outputs migration utility (@task T4700)
export { detectLegacyAgentOutputs, migrateAgentOutputs, } from './agent-outputs.js';
// Re-export checksum utilities for backup verification (@task T4728)
export { compareChecksums, computeChecksum, verifyBackup, } from './checksum.js';
// Re-export migration logger (@task T4727)
export { createMigrationLogger, getLatestMigrationLog, logFileExists, MigrationLogger, readMigrationLog, } from './logger.js';
// Re-export pre-flight check for CLI/MCP consumers (@task T4699)
export { checkStorageMigration } from './preflight.js';
// Re-export migration state tracking (@task T4726)
export { addMigrationError, addMigrationWarning, canResumeMigration, clearMigrationState, completeMigration, createMigrationState, failMigration, getMigrationSummary, isMigrationInProgress, loadMigrationState, updateMigrationPhase, updateMigrationProgress, updateMigrationState, verifySourceIntegrity, } from './state.js';
// Re-export pre-migration validation (@task T4725)
export { checkTaskCountMismatch, formatValidationResult, validateSourceFiles, } from './validate.js';
import { join } from 'node:path';
import { ExitCode } from '@cleocode/contracts';
import { CleoError } from '../errors.js';
import { getBackupDir, getCleoDirAbsolute, getConfigPath, getTaskPath } from '../paths.js';
import { readJson, saveJson } from '../store/json.js';
// Current target schema versions
const TARGET_VERSIONS = {
    todo: '2.10.0',
    config: '2.0.0',
    archive: '2.6.0',
    log: '1.0.0',
};
// Migration registry
const MIGRATIONS = {
    todo: [
        {
            fromVersion: '2.6.0',
            toVersion: '2.7.0',
            description: 'Add epicLifecycle and origin fields',
            migrate: (data) => {
                const d = data;
                const tasks = d['tasks'];
                for (const task of tasks) {
                    if (task['type'] === 'epic' && !task['epicLifecycle']) {
                        task['epicLifecycle'] = null;
                    }
                    if (!task['origin']) {
                        task['origin'] = null;
                    }
                }
                return d;
            },
        },
        {
            fromVersion: '2.7.0',
            toVersion: '2.8.0',
            description: 'Add verification gates',
            migrate: (data) => {
                const d = data;
                const tasks = d['tasks'];
                for (const task of tasks) {
                    if (!task['verification']) {
                        task['verification'] = null;
                    }
                }
                return d;
            },
        },
        {
            fromVersion: '2.8.0',
            toVersion: '2.9.0',
            description: 'Add provenance tracking',
            migrate: (data) => {
                const d = data;
                const tasks = d['tasks'];
                for (const task of tasks) {
                    if (!task['provenance']) {
                        task['provenance'] = null;
                    }
                }
                return d;
            },
        },
        {
            fromVersion: '2.9.0',
            toVersion: '2.10.0',
            description: 'Add multi-session support fields',
            migrate: (data) => {
                const d = data;
                const meta = d['_meta'];
                if (!meta['multiSessionEnabled']) {
                    meta['multiSessionEnabled'] = false;
                }
                if (!meta['activeSessionCount']) {
                    meta['activeSessionCount'] = 0;
                }
                return d;
            },
        },
    ],
};
/**
 * Detect schema version from a data file.
 * @task T4468
 */
export function detectVersion(data) {
    const d = data;
    // Check _meta.schemaVersion (canonical)
    const meta = d['_meta'];
    if (meta?.['schemaVersion']) {
        return meta['schemaVersion'];
    }
    // Fallback to .version
    if (d['version'] && typeof d['version'] === 'string') {
        return d['version'];
    }
    return '0.0.0';
}
/**
 * Compare two version strings (X.Y.Z format, works for both semver and CalVer).
 * Returns -1 if a < b, 0 if equal, 1 if a > b.
 * @task T4468
 */
export function compareSemver(a, b) {
    const pa = a.split('.').map(Number);
    const pb = b.split('.').map(Number);
    for (let i = 0; i < 3; i++) {
        const na = pa[i] ?? 0;
        const nb = pb[i] ?? 0;
        if (na < nb)
            return -1;
        if (na > nb)
            return 1;
    }
    return 0;
}
/**
 * Get migration status for all data files.
 * @task T4468
 */
export async function getMigrationStatus(cwd) {
    const result = {
        todoJson: null,
        configJson: null,
        archiveJson: null,
    };
    // Check todo.json
    try {
        const taskData = await readJson(getTaskPath(cwd));
        if (taskData) {
            const current = detectVersion(taskData);
            const target = TARGET_VERSIONS['todo'];
            result.todoJson = {
                current,
                target,
                needsMigration: compareSemver(current, target) < 0,
            };
        }
    }
    catch {
        // File may not exist
    }
    // Check config.json
    try {
        const configData = await readJson(getConfigPath(cwd));
        if (configData) {
            const current = detectVersion(configData);
            const target = TARGET_VERSIONS['config'];
            result.configJson = {
                current,
                target,
                needsMigration: compareSemver(current, target) < 0,
            };
        }
    }
    catch {
        // File may not exist
    }
    // Check archive
    try {
        const archivePath = join(getCleoDirAbsolute(cwd), 'todo-archive.json');
        const archiveData = await readJson(archivePath);
        if (archiveData) {
            const current = detectVersion(archiveData);
            const target = TARGET_VERSIONS['archive'];
            result.archiveJson = {
                current,
                target,
                needsMigration: compareSemver(current, target) < 0,
            };
        }
    }
    catch {
        // File may not exist
    }
    return result;
}
/**
 * Run migrations on a data file.
 * @task T4468
 */
export async function runMigration(fileType, options = {}, cwd) {
    const filePaths = {
        todo: getTaskPath(cwd),
        config: getConfigPath(cwd),
        archive: join(getCleoDirAbsolute(cwd), 'todo-archive.json'),
    };
    const filePath = filePaths[fileType];
    if (!filePath) {
        throw new CleoError(ExitCode.INVALID_INPUT, `Unknown file type: ${fileType}`);
    }
    const data = await readJson(filePath);
    if (!data) {
        throw new CleoError(ExitCode.NOT_FOUND, `File not found: ${filePath}`);
    }
    const currentVersion = detectVersion(data);
    const targetVersion = TARGET_VERSIONS[fileType] ?? '0.0.0';
    const migrations = MIGRATIONS[fileType] ?? [];
    // Find applicable migrations
    const applicable = migrations
        .filter((m) => compareSemver(m.fromVersion, currentVersion) >= 0 &&
        compareSemver(m.toVersion, targetVersion) <= 0)
        .sort((a, b) => compareSemver(a.fromVersion, b.fromVersion));
    if (applicable.length === 0) {
        return {
            file: filePath,
            fromVersion: currentVersion,
            toVersion: currentVersion,
            migrationsApplied: [],
            success: true,
            errors: [],
            dryRun: options.dryRun ?? false,
        };
    }
    // Apply migrations sequentially
    let migrated = data;
    const applied = [];
    const errors = [];
    for (const migration of applicable) {
        try {
            migrated = migration.migrate(migrated);
            applied.push(`${migration.fromVersion} -> ${migration.toVersion}: ${migration.description}`);
        }
        catch (err) {
            errors.push(`Migration ${migration.fromVersion} -> ${migration.toVersion} failed: ${err}`);
            break;
        }
    }
    // Update schema version in _meta
    const migratedObj = migrated;
    const meta = (migratedObj['_meta'] ?? {});
    meta['schemaVersion'] = errors.length === 0 ? targetVersion : currentVersion;
    migratedObj['_meta'] = meta;
    if (!options.dryRun && errors.length === 0) {
        await saveJson(filePath, migrated, { backupDir: getBackupDir(cwd) });
    }
    return {
        file: filePath,
        fromVersion: currentVersion,
        toVersion: errors.length === 0 ? targetVersion : currentVersion,
        migrationsApplied: applied,
        success: errors.length === 0,
        errors,
        dryRun: options.dryRun ?? false,
    };
}
/**
 * Run all pending migrations.
 * @task T4468
 */
export async function runAllMigrations(options = {}, cwd) {
    const status = await getMigrationStatus(cwd);
    const results = [];
    if (status.todoJson?.needsMigration) {
        results.push(await runMigration('todo', options, cwd));
    }
    if (status.configJson?.needsMigration) {
        results.push(await runMigration('config', options, cwd));
    }
    if (status.archiveJson?.needsMigration) {
        results.push(await runMigration('archive', options, cwd));
    }
    return results;
}
//# sourceMappingURL=index.js.map