/**
 * SQLite Migrations Module
 *
 * Manages database schema migrations using umzug v3.
 * Legacy migrations (V1–V36) are registered programmatically from legacy-migrations.ts.
 * New migrations (36+) are individual .ts files in the migrations/ directory,
 * discovered at runtime via directory scan.
 *
 * Preserves the same export API (runSQLiteMigrations, LATEST_SCHEMA_VERSION) so
 * all 16+ importing files require zero changes.
 */
import { readdirSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { Umzug } from 'umzug';
import { SQLiteMigrationStorage } from './sqlite-migration-storage.js';
import { LEGACY_MIGRATIONS, LEGACY_MIGRATION_NAMES } from './legacy-migrations.js';
/**
 * The latest schema version (highest legacy migration version number).
 * Exported for test assertions so they don't hardcode version numbers.
 */
export const LATEST_SCHEMA_VERSION = 36;
/**
 * Resolves the migrations directory path.
 * Works both in source (src/) and compiled (dist/) contexts.
 */
function getMigrationsDir() {
    // import.meta.url is available in ESM; fallback to __dirname for CJS
    // The migrations/ directory is a sibling of this file
    return join(dirname(fileURLToPath(import.meta.url)), 'migrations');
}
/**
 * Discovers new migration files (035+) in the migrations/ directory.
 * Files must be .js (compiled from .ts) and follow the naming convention:
 *   035-description.js, 036-description.js, etc.
 *
 * Each file must export:
 *   - up(params: MigrationParams<Database.Database>): Promise<void>
 *   - down(params: MigrationParams<Database.Database>): Promise<void> (required for 35+)
 */
async function discoverNewMigrations(migrationsDir) {
    let files;
    try {
        const allFiles = readdirSync(migrationsDir);
        // Accept .js (compiled) and .ts (dev via tsx) but exclude .d.ts declaration files
        const candidates = allFiles.filter((f) => (f.endsWith('.js') || f.endsWith('.ts')) && !f.endsWith('.d.ts') && !f.endsWith('.d.js'));
        // Deduplicate: if both .ts and .js exist for the same migration, prefer .js
        const byName = new Map();
        for (const f of candidates) {
            const base = f.replace(/\.(js|ts)$/, '');
            if (!byName.has(base) || f.endsWith('.js')) {
                byName.set(base, f);
            }
        }
        files = [...byName.values()].sort();
    }
    catch {
        // Directory doesn't exist or is unreadable — no new migrations
        return [];
    }
    const migrations = [];
    for (const file of files) {
        const name = file.replace(/\.(js|ts)$/, '');
        const filePath = join(migrationsDir, file);
        const mod = (await import(pathToFileURL(filePath).href));
        migrations.push({
            name,
            path: filePath,
            up: async (params) => mod.up(params),
            ...(mod.down
                ? { down: async (params) => mod.down(params) }
                : {}),
        });
    }
    return migrations;
}
/**
 * Creates a configured Umzug instance for the given database.
 *
 * Combines legacy inline migrations with runtime-discovered new
 * migration files (035+) from the migrations/ directory.
 */
function createUmzug(db) {
    const storage = new SQLiteMigrationStorage(db, LEGACY_MIGRATION_NAMES);
    const migrationsDir = getMigrationsDir();
    return new Umzug({
        storage,
        context: db,
        migrations: async () => {
            const newMigrations = await discoverNewMigrations(migrationsDir);
            // Legacy migrations first (001–035), then new migrations (035+) sorted by name
            return [...LEGACY_MIGRATIONS, ...newMigrations];
        },
        /* eslint-disable no-console */
        logger: process.env.DEBUG_SQL
            ? {
                info: (msg) => console.log('[umzug:info]', msg),
                warn: (msg) => console.warn('[umzug:warn]', msg),
                error: (msg) => console.error('[umzug:error]', msg),
                debug: (msg) => console.debug('[umzug:debug]', msg),
            }
            : undefined,
        /* eslint-enable no-console */
    });
}
/**
 * Runs all pending database migrations.
 * Safe to call multiple times (idempotent).
 *
 * Internally creates an Umzug instance configured with:
 *   - SQLiteMigrationStorage (custom better-sqlite3 storage adapter)
 *   - 35 legacy inline migrations from legacy-migrations.ts
 *   - Runtime discovery for new migration files (35+)
 *   - Debug-level logger (when DEBUG_SQL is set)
 *
 * @param db - Database instance to run migrations on
 */
/**
 * Returns the total number of registered migrations (legacy + discovered).
 * Exported for test assertions so they don't hardcode migration counts.
 */
export async function getTotalMigrationCount() {
    const migrationsDir = getMigrationsDir();
    const newMigrations = await discoverNewMigrations(migrationsDir);
    return LEGACY_MIGRATIONS.length + newMigrations.length;
}
export async function runSQLiteMigrations(db) {
    try {
        const umzug = createUmzug(db);
        await umzug.up();
    }
    catch (error) {
        throw new Error(`Failed to run database migrations: ${error instanceof Error ? error.message : String(error)}`);
    }
}
