import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { sql } from 'drizzle-orm';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { createPgliteDb } from './client-pglite.js';
import { runPgliteMigrations } from './migrate.js';
import type { DbHandle } from './client.js';

interface PgliteExec {
  exec(query: string): Promise<unknown>;
}

describe('runPgliteMigrations', () => {
  let dataDir: string;
  let handle: DbHandle;

  beforeEach(() => {
    dataDir = mkdtempSync(join(tmpdir(), 'mosaic-db-migrate-test-'));
    handle = createPgliteDb(dataDir);
  });

  afterEach(async () => {
    await handle.close();
    rmSync(dataDir, { recursive: true, force: true });
  });

  it('creates the BetterAuth tables required by the gateway', async () => {
    await runPgliteMigrations(handle);

    const result = (await handle.db.execute(sql`
      SELECT table_name FROM information_schema.tables
      WHERE table_schema = 'public'
      ORDER BY table_name
    `)) as unknown as { rows: Array<{ table_name: string }> };

    const tables = result.rows.map((r) => r.table_name);

    // Auth tables — required for sign-in / bootstrap to function.
    expect(tables).toContain('users');
    expect(tables).toContain('sessions');
    expect(tables).toContain('accounts');
    expect(tables).toContain('verifications');

    // Schema sanity check — admin token table consumed by mosaic gateway config.
    expect(tables).toContain('admin_tokens');
  });

  it('is idempotent — running twice does not error', async () => {
    await runPgliteMigrations(handle);
    await expect(runPgliteMigrations(handle)).resolves.toBeUndefined();
  });

  it('surfaces statement-level error context on failure and leaves no ledger row', async () => {
    // Pre-create a `users` table that conflicts with migration 0000's CREATE TABLE,
    // forcing it to fail without IF NOT EXISTS.
    const client = (handle.db as unknown as { $client: PgliteExec }).$client;
    await client.exec('CREATE TABLE users (sentinel text)');

    await expect(runPgliteMigrations(handle)).rejects.toThrow(
      /migration hash=[a-f0-9]+ statement #\d+ failed/,
    );

    // Ledger should be empty — partial application must not pretend to be complete.
    const ledger = (await handle.db.execute(
      sql`SELECT count(*)::int AS count FROM drizzle.__drizzle_migrations`,
    )) as unknown as { rows: Array<{ count: number }> };
    expect(ledger.rows[0]?.count).toBe(0);
  });
});
