/**
 * Test harness — ephemeral schema per test run on the shared Postgres instance.
 *
 * Each test suite calls `setupTestDb()` in `beforeAll` and `teardownTestDb()`
 * in `afterAll`. The harness:
 *   1. Opens a Pool pointed at the real Postgres (QUINN_MACSYNC_DB_URL).
 *   2. Creates a temporary schema named `icloud_test_<random>` so tests are
 *      isolated from the production `icloud` schema and from each other.
 *   3. Runs all migrations scoped to that schema via `search_path`.
 *   4. Injects the pool into the module singleton so repo functions pick it up.
 *   5. Drops the schema on teardown (unconditionally).
 */

import { Pool } from 'pg';
import { randomUUIDv7 } from 'bun';

import { injectDb, resetDb, runMigrations } from '@/shared/db';
import { deviceMigrations } from '@/entities/device';
import { contactMigrations } from '@/entities/contact';
import { conversationMigrations } from '@/entities/conversation';
import { messageMigrations } from '@/entities/message';
import { messageEmbeddingMigrations } from '@/entities/message-embedding';
import { attachmentMigrations } from '@/entities/attachment';
import { albumMigrations } from '@/entities/album';
import { photoMigrations } from '@/entities/photo';
import { calendarMigrations } from '@/entities/calendar';
import { eventMigrations } from '@/entities/event';
import { searchCacheMigrations } from '@/entities/search-cache';
import { sendQueueMigrations } from '@/entities/send-queue';
import { syncHistoryMigrations } from '@/entities/sync-history';

const ALL_MIGRATIONS = [
  ...deviceMigrations,
  ...contactMigrations,
  ...conversationMigrations,
  ...messageMigrations,
  ...messageEmbeddingMigrations,
  ...attachmentMigrations,
  ...albumMigrations,
  ...photoMigrations,
  ...calendarMigrations,
  ...eventMigrations,
  ...searchCacheMigrations,
  ...sendQueueMigrations,
  ...syncHistoryMigrations,
];

export interface TestDb {
  pool: Pool;
  schema: string;
}

function requireDbUrl(): string {
  const url = process.env['QUINN_MACSYNC_DB_URL'];
  if (!url) {
    throw new Error(
      'QUINN_MACSYNC_DB_URL is required for tests. ' +
        'Set it to a Postgres URL, e.g. postgres://user:pass@10.0.0.11:25432/mac_sync',
    );
  }
  return url;
}

export async function setupTestDb(): Promise<TestDb> {
  const connectionString = requireDbUrl();

  const suffix = randomUUIDv7().replace(/-/g, '').slice(0, 12);
  const schema = `icloud_test_${suffix}`;

  // Admin pool: create the test schema, then discard.
  const adminPool = new Pool({ connectionString });
  try {
    await adminPool.query(`CREATE SCHEMA "${schema}"`);
  } finally {
    await adminPool.end();
  }

  // Scoped pool: every connection lands in the test schema.
  // Include `public` so extension-provided types (e.g. `vector`) are resolvable.
  const pool = new Pool({
    connectionString,
    options: `-c search_path="${schema}",public`,
  });

  // Rewrite migrations: replace `icloud.` qualifier with the test schema.
  const scopedMigrations = ALL_MIGRATIONS.map((m) => ({
    id: m.id,
    sql: m.sql.replace(/\bicloud\./g, `"${schema}".`).replace(/'icloud'/g, `'${schema}'`),
  }));

  try {
    await runMigrations(pool, scopedMigrations);
  } catch (err) {
    await pool.end();
    const adminPool2 = new Pool({ connectionString });
    try {
      await adminPool2.query(`DROP SCHEMA IF EXISTS "${schema}" CASCADE`);
    } finally {
      await adminPool2.end();
    }
    throw new Error(
      `setupTestDb: migration failed for schema "${schema}": ${err instanceof Error ? err.message : String(err)}`,
      { cause: err },
    );
  }

  // Inject so getDb() returns this pool throughout repo/feature layer calls.
  injectDb(pool);

  return { pool, schema };
}

export async function teardownTestDb(db: TestDb): Promise<void> {
  resetDb();

  const connectionString = requireDbUrl();
  const adminPool = new Pool({ connectionString });
  try {
    await adminPool.query(`DROP SCHEMA IF EXISTS "${db.schema}" CASCADE`);
  } finally {
    await adminPool.end();
  }

  await db.pool.end();
}
