/**
 * Test support for database integration tests.
 * Explicit helpers only: no magic fixtures, no hidden state.
 */
import pg from 'pg';
import { runner } from 'node-pg-migrate';

/** Run project migrations programmatically with explicit, boring defaults.
 * `count` bounds how many migrations run (down: `count: 0` reverts ALL). */
export async function runMigrations(
  databaseUrl: string,
  direction: 'up' | 'down',
  count?: number,
): Promise<void> {
  await runner({
    databaseUrl,
    dir: 'migrations',
    direction,
    ...(count === undefined ? {} : { count }),
    migrationsTable: 'pgmigrations',
    verbose: false,
  });
}

export function getTestDbUrl(): string {
  const url = process.env.TEST_DATABASE_URL;
  if (!url) {
    throw new Error('TEST_DATABASE_URL is required for integration tests');
  }
  return url;
}

export function dbNameFromUrl(url: string): string {
  const parsed = new URL(url);
  const name = parsed.pathname.replace(/^\//, '');
  if (!name) {
    throw new Error(`TEST_DATABASE_URL must include a database name: ${url}`);
  }
  // Strict identifier validation: the name is interpolated into DDL below.
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
    throw new Error(`TEST_DATABASE_URL database name is not a safe identifier: ${name}`);
  }
  return name;
}

export function adminUrlFrom(url: string): string {
  const parsed = new URL(url);
  parsed.pathname = '/postgres';
  return parsed.toString();
}

/** Drop and recreate the test database so every run starts fresh. */
export async function recreateDatabase(url: string): Promise<void> {
  const dbName = dbNameFromUrl(url);
  const admin = new pg.Client({ connectionString: adminUrlFrom(url) });
  await admin.connect();
  try {
    // dbName is validated as a strict identifier in dbNameFromUrl before any DDL use.
    await admin.query(`DROP DATABASE IF EXISTS "${dbName}" WITH (FORCE)`);
    await admin.query(`CREATE DATABASE "${dbName}"`);
  } finally {
    await admin.end();
  }
}

export async function tableExists(pool: pg.Pool, table: string): Promise<boolean> {
  const result = await pool.query(
    'SELECT 1 FROM information_schema.tables WHERE table_schema = $1 AND table_name = $2',
    ['public', table],
  );
  return result.rowCount !== null && result.rowCount > 0;
}
