/**
 * @ai-summary Orchestrates all feature migrations in dependency order
 */
import { Pool } from 'pg';
import { readFileSync, readdirSync } from 'fs';
import { join, resolve } from 'path';
import { appConfig } from '../../core/config/config-loader';

const pool = new Pool({
  connectionString: appConfig.getDatabaseUrl(),
});

// Define migration order based on dependencies and packaging layout
// We package migrations under /app/migrations with two roots: features/ and core/
// The update_updated_at_column() function is defined in features/vehicles first,
// and user-preferences trigger depends on it; so run vehicles before core/user-preferences.
const MIGRATION_ORDER = [
  'features/vehicles',          // Primary entity, defines update_updated_at_column()
  'features/platform',          // Normalized make/model/trim schema for dropdowns
  'features/user-profile',      // User profile management; needed by documents migration
  'features/documents',         // Depends on vehicles, user-profile; provides documents table
  'core/user-preferences',      // Depends on update_updated_at_column()
  'features/fuel-logs',         // Depends on vehicles
  'features/maintenance',       // Depends on vehicles
  'features/stations',          // Independent
  'features/admin',             // Admin role management and oversight; depends on update_updated_at_column()
  'features/backup',            // Admin backup feature; depends on update_updated_at_column()
  'features/notifications',     // Depends on maintenance and documents
  'features/terms-agreement',   // Terms & Conditions acceptance audit trail
  'features/audit-log',         // Centralized audit logging; independent
  'features/ownership-costs',   // Depends on vehicles and documents; TCO recurring costs
  'features/subscriptions',     // Stripe subscriptions; depends on user-profile, vehicles
];

// Base directory where migrations are copied inside the image (set by Dockerfile)
const MIGRATIONS_DIR = resolve(process.env['MIGRATIONS_DIR'] || join(__dirname, '../../../migrations'));

async function getExecutedMigrations(): Promise<Record<string, Set<string>>> {
  const executed: Record<string, Set<string>> = {};
  // Ensure tracking table exists (retry across transient DB restarts)
  const retry = async <T>(op: () => Promise<T>, timeoutMs = 60000): Promise<T> => {
    const start = Date.now();
    while (true) {
      try { return await op(); } catch (e) {
        if (Date.now() - start > timeoutMs) throw e;
        await new Promise(res => setTimeout(res, 2000));
      }
    }
  };
  await retry(() => pool.query(`
    CREATE TABLE IF NOT EXISTS _migrations (
      id SERIAL PRIMARY KEY,
      feature VARCHAR(100) NOT NULL,
      file VARCHAR(255) NOT NULL,
      executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      UNIQUE(feature, file)
    );
  `));
  const res = await retry(() => pool.query('SELECT feature, file FROM _migrations'));
  for (const row of res.rows) {
    if (!executed[row.feature]) executed[row.feature] = new Set();
    executed[row.feature].add(row.file);
  }
  return executed;
}

async function runFeatureMigrations(featureName: string) {
  const migrationDir = join(MIGRATIONS_DIR, featureName, 'migrations');
  
  try {
    // Guard per-feature in case DB becomes available slightly later on cold start
    const ping = async (timeoutMs = 60000) => {
      const start = Date.now();
      while (true) {
        try { await pool.query('SELECT 1'); return; } catch (e) {
          if (Date.now() - start > timeoutMs) throw e; await new Promise(r => setTimeout(r, 2000));
        }
      }
    };
    await ping();
    const files = readdirSync(migrationDir)
      .filter(f => f.endsWith('.sql'))
      .sort();
    const executed = await getExecutedMigrations();
    const already = executed[featureName] || new Set<string>();
    
    for (const file of files) {
      if (already.has(file)) {
        console.log(`↷ Skipping already executed migration: ${featureName}/${file}`);
        continue;
      }
      const sql = readFileSync(join(migrationDir, file), 'utf-8');
      console.log(`Running migration: ${featureName}/${file}`);
      await pool.query(sql);
      await pool.query('INSERT INTO _migrations(feature, file) VALUES ($1, $2) ON CONFLICT DO NOTHING', [featureName, file]);
      console.log(`✅ Completed: ${featureName}/${file}`);
    }
  } catch (error) {
    console.error(`❌ Failed migration for ${featureName}:`, error);
    throw error;
  }
}

async function main() {
  try {
    console.log('Starting migration orchestration...');
    console.log(`Using migrations directory: ${MIGRATIONS_DIR}`);
    // Wait for database to be reachable (handles cold starts)
    const waitForDb = async (timeoutMs = 60000) => {
      const start = Date.now();
       
      while (true) {
        try {
          await pool.query('SELECT 1');
          return;
        } catch (e) {
          if (Date.now() - start > timeoutMs) throw e;
          await new Promise(res => setTimeout(res, 2000));
        }
      }
    };
    await waitForDb();
    
    // Run migrations in order
    for (const feature of MIGRATION_ORDER) {
      console.log(`\nMigrating feature: ${feature}`);
      await runFeatureMigrations(feature);
    }
    
    console.log('\n✅ All migrations completed successfully');
  } catch (error) {
    console.error('Migration failed:', error);
    process.exit(1);
  } finally {
    await pool.end();
  }
}

if (require.main === module) {
  main();
}
