/**
 * PostgreSQL connection pool singleton.
 * All database access flows through this pool.
 *
 * Pool configuration env vars (task 2.1 / 2.2):
 *   DB_POOL_MAX                   — maximum connections (default 20)
 *   DB_POOL_MIN                   — minimum connections (default 2)
 *   DB_POOL_IDLE_TIMEOUT_MS       — idle connection timeout in ms (default 30000)
 *   DB_POOL_CONNECTION_TIMEOUT_MS — connection acquisition timeout in ms (default 5000)
 */

import { Pool } from 'pg';
import {
  dbQueryDurationSeconds,
  dbPoolActiveConnections,
  dbPoolWaitingRequests,
} from '../metrics/registry.js';

let pool: Pool | null = null;

/**
 * Returns the singleton pg Pool instance.
 * Initialises the pool on first call using DATABASE_URL and optional pool
 * tuning env vars.
 *
 * Prometheus gauges `agentidp_db_pool_active_connections` and
 * `agentidp_db_pool_waiting_requests` are updated via pool events (task 2.3).
 *
 * @returns The PostgreSQL connection pool.
 * @throws Error if DATABASE_URL is not set.
 */
export function getPool(): Pool {
  if (!pool) {
    const connectionString = process.env['DATABASE_URL'];
    if (!connectionString) {
      throw new Error('DATABASE_URL environment variable is required');
    }

    const max = parseInt(process.env['DB_POOL_MAX'] ?? '20', 10);
    const min = parseInt(process.env['DB_POOL_MIN'] ?? '2', 10);
    const idleTimeoutMillis = parseInt(process.env['DB_POOL_IDLE_TIMEOUT_MS'] ?? '30000', 10);
    const connectionTimeoutMillis = parseInt(
      process.env['DB_POOL_CONNECTION_TIMEOUT_MS'] ?? '5000',
      10,
    );

    pool = new Pool({
      connectionString,
      max,
      min,
      idleTimeoutMillis,
      connectionTimeoutMillis,
    });

    pool.on('error', (err: Error) => {
      // eslint-disable-next-line no-console
      console.error('Unexpected pg pool error', err);
    });

    // Track active connections and waiting requests via pool events (task 2.3).
    pool.on('acquire', () => {
      if (pool) {
        dbPoolActiveConnections.set(pool.totalCount - pool.idleCount);
        dbPoolWaitingRequests.set(pool.waitingCount);
      }
    });

    pool.on('remove', () => {
      if (pool) {
        dbPoolActiveConnections.set(pool.totalCount - pool.idleCount);
        dbPoolWaitingRequests.set(pool.waitingCount);
      }
    });

    pool.on('connect', () => {
      if (pool) {
        dbPoolActiveConnections.set(pool.totalCount - pool.idleCount);
        dbPoolWaitingRequests.set(pool.waitingCount);
      }
    });

    // Wrap pool.query to record Prometheus query duration.
    // Uses unknown[] + Object.defineProperty to avoid `any` while preserving
    // the pool's typed interface for all callers (TypeScript still sees Pool['query']).
    const originalQuery = pool.query.bind(pool);
    Object.defineProperty(pool, 'query', {
      value: async (...args: unknown[]): Promise<unknown> => {
        const end = dbQueryDurationSeconds.startTimer({ operation: 'query' });
        try {
          return await (originalQuery as (...a: unknown[]) => Promise<unknown>)(...args);
        } finally {
          end();
        }
      },
      writable: true,
      configurable: true,
      enumerable: false,
    });
  }
  return pool;
}

/**
 * Closes the pool and resets the singleton.
 * Used for graceful shutdown and tests.
 *
 * @returns Promise that resolves when the pool is closed.
 */
export async function closePool(): Promise<void> {
  if (pool) {
    await pool.end();
    pool = null;
  }
}
