/**
 * Run pending DB migrations. Used by server on startup and by npm run migrate.
 * Export runMigrations(sequelize) - does not load secrets or connect; caller must pass connected sequelize.
 */

import { QueryInterface, QueryTypes } from 'sequelize';
import type { Sequelize } from 'sequelize';
import * as m46 from '../migrations/20260216-add-qty-hsn-to-expenses';
import * as m47 from '../migrations/20260217-add-is-service-to-expenses';
import * as m48 from '../migrations/20260217-create-claim-invoice-items';
import * as m49 from '../migrations/20260220-create-form16-tables';
import * as m50 from '../migrations/20260220000001-add-form16-ocr-extracted-data';
import * as m51 from '../migrations/20260222000001-create-tds-26as-entries';
import * as m52 from '../migrations/20260223000001-create-form-16-debit-notes';
import * as m53 from '../migrations/20260224000001-create-form-16-26as-upload-log';
import * as m53a from '../migrations/20260225000001-add-form16-26as-upload-log-id-and-tables';
import * as m54 from '../migrations/20260225000001-create-form16-non-submitted-notifications';
import * as m54a from '../migrations/20260225100001-add-form16-archived-at';
import * as m55 from '../migrations/20260303100001-drop-form16a-number-unique';
import * as m56 from '../migrations/20260302-refine-dealer-claim-schema';

interface Migration {
  name: string;
  module: any;
}

const migrations: Migration[] = [
  { name: '20260216-add-qty-hsn-to-expenses', module: m46 },
  { name: '20260217-add-is-service-to-expenses', module: m47 },
  { name: '20260217-create-claim-invoice-items', module: m48 },
  { name: '20260220-create-form16-tables', module: m49 },
  { name: '20260220000001-add-form16-ocr-extracted-data', module: m50 },
  { name: '20260222000001-create-tds-26as-entries', module: m51 },
  { name: '20260223000001-create-form-16-debit-notes', module: m52 },
  { name: '20260224000001-create-form-16-26as-upload-log', module: m53 },
  { name: '20260225000001-add-form16-26as-upload-log-id-and-tables', module: m53a },
  { name: '20260225000001-create-form16-non-submitted-notifications', module: m54 },
  { name: '20260225100001-add-form16-archived-at', module: m54a },
  { name: '20260302-refine-dealer-claim-schema', module: m56 },
  { name: '20260303100001-drop-form16a-number-unique', module: m55 },
];

async function ensureMigrationsTable(queryInterface: QueryInterface): Promise<void> {
  const tables = await queryInterface.showAllTables();
  const tableName = (t: string) => (typeof t === 'string' ? t.toLowerCase() : (t as any));
  if (!tables.some((t) => tableName(t) === 'migrations')) {
    await queryInterface.sequelize.query(`
      CREATE TABLE IF NOT EXISTS migrations (
        id SERIAL PRIMARY KEY,
        name VARCHAR(255) NOT NULL UNIQUE,
        executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
      )
    `);
  }
}

async function getExecutedMigrations(sequelize: Sequelize): Promise<string[]> {
  try {
    const results = (await sequelize.query('SELECT name FROM migrations ORDER BY id', {
      type: QueryTypes.SELECT,
    })) as { name: string }[];
    return results.map((r) => r.name);
  } catch {
    return [];
  }
}

async function markMigrationExecuted(sequelize: Sequelize, name: string): Promise<void> {
  await sequelize.query(
    'INSERT INTO migrations (name) VALUES (:name) ON CONFLICT (name) DO NOTHING',
    { replacements: { name }, type: QueryTypes.RAW }
  );
}

/**
 * Run all pending migrations. Call after DB is connected (e.g. after sequelize.authenticate()).
 * @returns Number of migrations applied (0 if already up-to-date).
 */
export async function runMigrations(sequelize: Sequelize): Promise<number> {
  const queryInterface = sequelize.getQueryInterface();
  await ensureMigrationsTable(queryInterface);
  const executedMigrations = await getExecutedMigrations(sequelize);
  const pendingMigrations = migrations.filter((m) => !executedMigrations.includes(m.name));
  if (pendingMigrations.length === 0) return 0;
  for (const migration of pendingMigrations) {
    await migration.module.up(queryInterface);
    await markMigrationExecuted(sequelize, migration.name);
    console.log(`✅ Migration: ${migration.name}`);
  }
  return pendingMigrations.length;
}
