import Database from 'better-sqlite3';
import fs from 'fs';
import path from 'path';

export class MigrationRunner {
  private db: Database.Database;
  private migrationsDir: string;

  constructor(db: Database.Database, migrationsDir: string) {
    this.db = db;
    this.migrationsDir = migrationsDir;
  }

  /**
   * Initialize the migrations table if it doesn't exist
   */
  init() {
    this.db.exec(`
      CREATE TABLE IF NOT EXISTS migrations (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL UNIQUE,
        applied_at TEXT DEFAULT CURRENT_TIMESTAMP
      );
    `);
  }

  /**
   * Get list of applied migrations
   */
  getAppliedMigrations(): string[] {
    const rows = this.db
      .prepare('SELECT name FROM migrations ORDER BY id')
      .all() as { name: string }[];
    return rows.map((row) => row.name);
  }

  /**
   * Get list of available migration files
   */
  getAvailableMigrations(): string[] {
    if (!fs.existsSync(this.migrationsDir)) {
      return [];
    }

    return fs
      .readdirSync(this.migrationsDir)
      .filter((file) => file.endsWith('.sql'))
      .sort();
  }

  /**
   * Apply a single migration
   */
  applyMigration(migrationName: string): void {
    const migrationPath = path.join(this.migrationsDir, migrationName);

    if (!fs.existsSync(migrationPath)) {
      throw new Error(`Migration file not found: ${migrationPath}`);
    }

    const sql = fs.readFileSync(migrationPath, 'utf8');

    console.log(`Applying migration: ${migrationName}`);

    // Execute the migration in a transaction
    const transaction = this.db.transaction(() => {
      this.db.exec(sql);
      this.db
        .prepare('INSERT INTO migrations (name) VALUES (?)')
        .run(migrationName);
    });

    transaction();
    console.log(`Migration applied: ${migrationName}`);
  }

  /**
   * Apply all pending migrations
   */
  applyPendingMigrations(): void {
    this.init();

    const applied = this.getAppliedMigrations();
    const available = this.getAvailableMigrations();

    const pending = available.filter(
      (migration) => !applied.includes(migration),
    );

    if (pending.length === 0) {
      console.log('No pending migrations');
      return;
    }

    console.log(`Applying ${pending.length} pending migrations...`);

    for (const migration of pending) {
      this.applyMigration(migration);
    }

    console.log('All migrations applied');
  }

  /**
   * Create a new migration file
   */
  createMigration(name: string): string {
    const timestamp = new Date()
      .toISOString()
      .replace(/[:.]/g, '-')
      .slice(0, -5);
    const filename = `${timestamp}_${name}.sql`;
    const filepath = path.join(this.migrationsDir, filename);

    const template = `-- Migration: ${name}
-- Created at: ${new Date().toISOString()}

-- Add your SQL migration here

`;

    fs.writeFileSync(filepath, template);
    console.log(`Created migration: ${filepath}`);
    return filepath;
  }
}
