/**
 * Forward-only file-based migration runner using PRAGMA user_version.
 *
 * Applies `migrations/<dbLabel>/v0NN_*.sql` files in ascending numeric order.
 * Uses PRAGMA user_version as the version counter — independent of any
 * schema_version tables the caller may maintain inline.
 *
 * Designed to run inside better-sqlite3 DB constructors (sync path).
 * Sync fs calls here are legitimate: we run before any async code path
 * and the DB opener is already synchronous by nature of better-sqlite3.
 */
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
// Locate the migrations directory relative to this file.
// Works identically whether running from src/ (tsx) or dist/ (compiled):
//   src/engines/schema/migrate.ts  → ../../migrations → src/migrations
//   dist/engines/schema/migrate.js → ../../migrations → dist/migrations
const _dir = path.dirname(fileURLToPath(import.meta.url));
const MIGRATIONS_ROOT = path.resolve(_dir, '..', '..', 'migrations');
/**
 * Run pending file-based migrations for `dbLabel` against `db`.
 *
 * @param db       - A better-sqlite3 Database instance (any subtype).
 * @param dbLabel  - Subdirectory name under `migrations/` (e.g. 'memory').
 * @returns        - Migration result; `degraded: true` means a migration
 *                   failed and a backup was saved next to the DB file.
 */
export function runMigrations(db, dbLabel) {
    const fromVersion = db.pragma('user_version', { simple: true }) ?? 0;
    const migrationsDir = path.join(MIGRATIONS_ROOT, dbLabel);
    let allFiles;
    try {
        allFiles = fs.readdirSync(migrationsDir);
    }
    catch {
        // No migrations directory — nothing to apply.
        return { degraded: false, migrationsApplied: 0, fromVersion, toVersion: fromVersion };
    }
    // Filter to files matching vNNN_*.sql, extract version numbers.
    const pending = allFiles
        .map(f => {
        const m = f.match(/^v(\d+)_.*\.sql$/);
        return m ? { file: f, version: parseInt(m[1], 10) } : null;
    })
        .filter((e) => e !== null && e.version > fromVersion)
        .sort((a, b) => a.version - b.version);
    if (pending.length === 0) {
        return { degraded: false, migrationsApplied: 0, fromVersion, toVersion: fromVersion };
    }
    const dbPath = db.name; // better-sqlite3: .name is the DB filename
    const stamp = new Date().toISOString().replace(/[:.]/g, '-');
    const backupPath = dbPath !== ':memory:' ? `${dbPath}.pre-migration-${stamp}.bak` : null;
    let lastVersion = fromVersion;
    try {
        for (const { file, version } of pending) {
            const sql = fs.readFileSync(path.join(migrationsDir, file), 'utf8');
            db.exec(sql);
            // Bump user_version after each successful migration so a partial
            // run leaves the DB at the last-successfully-applied version.
            db.pragma(`user_version = ${version}`);
            lastVersion = version;
        }
    }
    catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        console.error(`[nexus-migrate] ${dbLabel}: migration v${lastVersion + 1} failed — ${msg}`);
        if (backupPath) {
            try {
                fs.copyFileSync(dbPath, backupPath);
                console.error(`[nexus-migrate] ${dbLabel}: backup saved → ${backupPath}`);
            }
            catch {
                // Backup is best-effort; continue with degraded flag.
            }
        }
        return {
            degraded: true,
            migrationsApplied: lastVersion - fromVersion,
            fromVersion,
            toVersion: lastVersion,
        };
    }
    return {
        degraded: false,
        migrationsApplied: pending.length,
        fromVersion,
        toVersion: lastVersion,
    };
}
