import type { Pool, PoolClient, QueryResult, QueryResultRow } from 'pg';

/**
 * The subset of pg's API the worker services actually need: a `query()` that
 * takes SQL plus positional params.
 *
 * Services are typed against this rather than against `Client` so they work
 * unchanged whether they are handed a `Pool` (the worker process — see
 * index.ts), a `PoolClient` checked out for a transaction, or a plain `Client`
 * (the one-off maintenance scripts in ./scripts, which are single-threaded and
 * own their connection).
 *
 * IMPORTANT: a `Queryable` gives NO transaction guarantees. When a `Pool` is
 * behind it, consecutive `query()` calls may land on different connections, so
 * bare `BEGIN`/`COMMIT` must never be issued through it — check out a dedicated
 * client with `withTransaction()` instead.
 */
export interface Queryable {
  query<R extends QueryResultRow = any>(
    sql: string,
    params?: any[]
  ): Promise<QueryResult<R>>;
}

/**
 * Run `fn` inside a transaction on a dedicated pooled connection, committing on
 * success and rolling back on any throw. The client is always released.
 *
 * This is the only correct way to run a transaction in the worker: the process
 * runs BullMQ with `concurrency: 10`, so issuing `BEGIN` on a shared connection
 * would enrol another job's unrelated queries in this transaction — and discard
 * them on `ROLLBACK`. (The backend documents the same hazard as its reason for
 * using a `Pool`; see backend/src/app.ts.)
 */
export async function withTransaction<T>(
  pool: Pool,
  fn: (client: PoolClient) => Promise<T>
): Promise<T> {
  const client = await pool.connect();
  try {
    await client.query('BEGIN');
    const result = await fn(client);
    await client.query('COMMIT');
    return result;
  } catch (err) {
    try {
      await client.query('ROLLBACK');
    } catch (rollbackErr) {
      // A failed ROLLBACK means the connection is unusable; log and move on —
      // release() below discards it rather than returning it to the pool.
      console.error('[DB] ROLLBACK failed:', rollbackErr);
    }
    throw err;
  } finally {
    client.release();
  }
}
