import { logger } from "../utils/logger.js";
/**
 * Creates the _migrations table if it doesn't exist.
 */
function ensureMigrationsTable(db) {
    db.exec(`
    CREATE TABLE IF NOT EXISTS _migrations (
      version INTEGER PRIMARY KEY,
      name TEXT NOT NULL,
      applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
    );
  `);
}
/**
 * Returns a set of version numbers that have already been applied.
 */
function getAppliedVersions(db) {
    const rows = db.prepare("SELECT version FROM _migrations ORDER BY version").all();
    return new Set(rows.map((r) => r.version));
}
/**
 * Records a migration as applied in the _migrations table.
 */
function recordMigration(db, version, name) {
    db.prepare("INSERT INTO _migrations (version, name) VALUES (?, ?)").run(version, name);
}
/**
 * Runs all pending migrations in order.
 * Each migration runs inside a transaction for atomicity.
 * Logs progress and handles failures gracefully.
 */
export function runMigrations(db, migrations) {
    ensureMigrationsTable(db);
    // Backfill from user_version if _migrations table is empty but schema exists
    const appliedVersions = getAppliedVersions(db);
    const userVersion = db.pragma("user_version", { simple: true });
    if (appliedVersions.size === 0 && userVersion > 0) {
        logger.info(`Backfilling _migrations table from user_version=${userVersion}`);
        for (const migration of migrations) {
            if (migration.version <= userVersion) {
                recordMigration(db, migration.version, migration.name);
            }
        }
        // Refresh after backfill
        const refreshed = getAppliedVersions(db);
        return runPendingMigrations(db, migrations, refreshed);
    }
    runPendingMigrations(db, migrations, appliedVersions);
}
function runPendingMigrations(db, migrations, appliedVersions) {
    const sorted = [...migrations].sort((a, b) => a.version - b.version);
    const pending = sorted.filter((m) => !appliedVersions.has(m.version));
    if (pending.length === 0) {
        logger.debug("No pending migrations");
        return;
    }
    logger.info(`Running ${pending.length} pending migration(s)`);
    for (const migration of pending) {
        logger.info(`Applying migration v${migration.version}: ${migration.name}`);
        try {
            db.transaction(() => {
                migration.up(db);
                recordMigration(db, migration.version, migration.name);
                db.pragma(`user_version = ${migration.version}`);
            })();
            logger.info(`Migration v${migration.version} applied successfully`);
        }
        catch (error) {
            const message = error instanceof Error ? error.message : String(error);
            logger.error(`Migration v${migration.version} failed: ${message}`);
            throw new Error(`Migration v${migration.version} (${migration.name}) failed: ${message}`);
        }
    }
    logger.info("All migrations applied successfully");
}
/**
 * Returns the current schema version from the _migrations table,
 * or falls back to user_version pragma.
 */
export function getSchemaVersion(db) {
    // Check if _migrations table exists
    const tableExists = db
        .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='_migrations'")
        .get();
    if (tableExists) {
        const row = db.prepare("SELECT MAX(version) as version FROM _migrations").get();
        if (row?.version != null) {
            return row.version;
        }
    }
    // Fallback to user_version pragma
    return db.pragma("user_version", { simple: true });
}
/**
 * Returns all applied migrations.
 */
export function getAppliedMigrations(db) {
    const tableExists = db
        .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='_migrations'")
        .get();
    if (!tableExists)
        return [];
    return db.prepare("SELECT version, name, applied_at FROM _migrations ORDER BY version").all();
}
//# sourceMappingURL=migrator.js.map