// ─────────────────────────────────────────────────────────────────────────────
// Database connection pool and query helpers
//
// Provides a PostgreSQL connection pool via `pg` and two export surfaces:
//   • `query()`        – one-off parameterised queries with optional RLS context
//   • `transaction()`  – fully managed BEGIN / COMMIT / ROLLBACK blocks
// Also logs pool-level errors to stderr.
// ─────────────────────────────────────────────────────────────────────────────

import { Pool, PoolClient } from "pg"

// ── Pool initialisation ──────────────────────────────────────────────────────

const dbUrl = process.env.DATABASE_URL
if (!dbUrl) {
  throw new Error("DATABASE_URL environment variable is required")
}

const pool = new Pool({
  connectionString: dbUrl,
  max: 20,                          // Max concurrent clients in the pool
  idleTimeoutMillis: 30000,         // Close idle clients after 30 s
  connectionTimeoutMillis: 5000,    // Fail fast if a connection can't be opened
  statement_timeout: 30000,         // Kill queries that run longer than 30 s
  idle_in_transaction_session_timeout: 10000, // Abandon txns idle > 10 s
  ssl: dbUrl.includes("localhost") || dbUrl.includes("127.0.0.1")
    ? false
    : { rejectUnauthorized: false }, // Accept self-signed certs in non-local envs
})

// Surface pool-level errors (e.g. a connection drops unexpectedly)
pool.on("error", (err) => {
  console.error("Unexpected database pool error:", err)
})

// ── Single-query helper ──────────────────────────────────────────────────────

/**
 * Run a single SQL query against the connection pool.
 *
 * @param text   - Parameterised SQL string (use `$1`, `$2`, … placeholders).
 * @param params - Optional values to bind to placeholders.
 * @param userId - If provided, sets `app.current_user_id` so Postgres RLS
 *                 policies can resolve the calling user.
 * @returns      The full `pg` result object (`.rows`, `.rowCount`, …).
 */
export async function query(text: string, params?: unknown[], userId?: string) {
  const client = await pool.connect()
  try {
    if (userId) {
      await client.query("SELECT set_config('app.current_user_id', $1, true)", [userId])
    }
    const result = await client.query(text, params)
    return result
  } finally {
    client.release()
  }
}

// ── Transaction helper ───────────────────────────────────────────────────────

/**
 * Execute a callback inside a database transaction.
 *
 * - Begins a transaction, runs `callback(client)`, then COMMITs.
 * - If the callback throws, the transaction is ROLLBACK'd and the error is
 *   re-thrown.
 * - Like `query()`, an optional `userId` is pushed into the session so RLS
 *   policies can inspect the current user.
 *
 * @param callback - Async function that receives the connected `PoolClient`.
 * @param userId   - Optional user ID for RLS context.
 * @returns        Whatever the callback returns.
 */
export async function transaction<T>(
  callback: (client: PoolClient) => Promise<T>,
  userId?: string,
): Promise<T> {
  const client = await pool.connect()
  try {
    await client.query("BEGIN")
    if (userId) {
      await client.query("SELECT set_config('app.current_user_id', $1, true)", [userId])
    }
    const result = await callback(client)
    await client.query("COMMIT")
    return result
  } catch (e) {
    await client.query("ROLLBACK").catch(() => {})
    throw e
  } finally {
    client.release()
  }
}

export default pool
