/**
 * Database Migration Script — destructive fresh sync.
 *
 * Drops every table and recreates the schema from Sequelize model definitions
 * in `src/database/models/`. After the fresh schema is in place, every
 * versioned migration file under `scripts/migrations/` is automatically
 * stamped into the `migrations` table as "already applied" so subsequent
 * `npm run migrate:up` runs on this DB will be no-ops until a newer
 * migration is added.
 *
 * For incremental schema changes on environments that already hold data,
 * use `npm run migrate:up` instead.
 *
 * Run: npx tsx scripts/migrate.ts
 * Flags:
 *   --no-baseline   Skip stamping migration files as applied (advanced).
 */

import 'dotenv/config';
import { promises as fs } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { createHash } from 'crypto';
import db from '../src/database/models/index.js';

const MIGRATIONS_DIR = path.resolve(
    path.dirname(fileURLToPath(import.meta.url)),
    'migrations'
);

async function discoverMigrations(): Promise<string[]> {
    try {
        const entries = await fs.readdir(MIGRATIONS_DIR);
        return entries
            .filter((name) => name.endsWith('.ts') && !name.startsWith('_'))
            .sort();
    } catch {
        return [];
    }
}

async function fileChecksum(file: string): Promise<string> {
    const buf = await fs.readFile(path.join(MIGRATIONS_DIR, file));
    return createHash('sha256').update(buf).digest('hex');
}

async function baselineMigrationsTable(): Promise<void> {
    const files = await discoverMigrations();
    if (files.length === 0) {
        console.log('No versioned migrations to baseline.');
        return;
    }
    console.log(`📌 Stamping ${files.length} migration(s) as already-applied:`);
    for (const file of files) {
        const name = file.replace(/\.ts$/, '');
        const checksum = await fileChecksum(file);
        await db.Migration.create({ name, checksum });
        console.log(`   + ${name}`);
    }
}

async function runMigrations() {
    console.log('🔄 Starting database synchronization (Fresh Startup)...\n');
    console.log('⚠️  WARNING: This will drop all existing tables in the database.\n');

    const skipBaseline = process.argv.includes('--no-baseline');

    try {
        await db.sequelize.authenticate();
        console.log('📡 Connected to the database successfully.');

        // force: true drops existing tables — schema is rebuilt exactly from
        // Sequelize models, so every enum / column / index matches code.
        await db.sequelize.sync({ force: true });

        console.log('\n✅ All tables created and synchronized successfully!');
        console.log('----------------------------------------------------');
        const modelNames = Object.keys(db).filter((k) => k !== 'sequelize' && k !== 'Sequelize');
        console.log(`Available Models (${modelNames.length}): ${modelNames.join(', ')}`);
        console.log('----------------------------------------------------\n');

        if (!skipBaseline) {
            await baselineMigrationsTable();
        } else {
            console.log('Skipping migration baseline (--no-baseline).');
        }

        process.exit(0);
    } catch (error: any) {
        console.error('\n❌ Migration failed:', error.message);
        if (error.stack) {
            console.error('\nStack Trace:\n', error.stack);
        }
        process.exit(1);
    }
}

runMigrations();
