import { EventEmitter } from 'node:events';
import { ConnectionOptions } from 'node:tls';
import pg, { Connection, QueryResultRow, QueryResult } from 'pg';
import { StrictEventEmitter } from 'strict-event-emitter-types';

declare class PostgresPoolError extends Error {
    code: string;
    constructor(message: string, code: string);
}

interface SslSettings {
    /**
     * TLS options for the underlying socket connection.
     */
    ssl?: ConnectionOptions;
}
interface SslSettingsOrAwsRdsSsl {
    /**
     * TLS options for the underlying socket connection.
     * NOTE: `aws-rds` sets up strict tls connection details for connecting to AWS RDS instances
     */
    ssl?: ConnectionOptions | 'aws-rds';
}
interface PoolOptionsBase {
    /**
     * Number of connections to store in the pool
     */
    poolSize: number;
    /**
     * Milliseconds until an idle connection is closed and removed from the active connection pool
     */
    idleTimeoutMillis: number;
    /**
     * Milliseconds to wait for an available connection before throwing an error that no connection is available
     */
    waitForAvailableConnectionTimeoutMillis: number;
    /**
     * Milliseconds to wait to connect to postgres
     */
    connectionTimeoutMillis: number;
    /**
     * Number of retries to attempt when there's an error matching `retryConnectionErrorCodes`. A value of 0
     * will disable connection retry.
     */
    retryConnectionMaxRetries: number;
    /**
     * Milliseconds to wait between retry connection attempts after receiving a connection error with code
     * that matches `retryConnectionErrorCodes`. A value of 0 will try reconnecting immediately.
     */
    retryConnectionWaitMillis: number;
    /**
     * Error codes to trigger a connection retry. Eg. ENOTFOUND, EAI_AGAIN
     */
    retryConnectionErrorCodes: string[];
    /**
     * If connect should be retried when the database throws "the database system is starting up"
     * NOTE: This typically happens during a fail over scenario when a read-replica is being promoted to master
     */
    reconnectOnDatabaseIsStartingError: boolean;
    /**
     * Milliseconds to wait between retry connection attempts while the database is starting up. Allows you to throttle
     * how many retries should happen until databaseStartupTimeoutMillis expires. A value of 0 will
     * retry the query immediately.
     */
    waitForDatabaseStartupMillis: number;
    /**
     * If connection attempts continually return "the database system is starting up", this is the total number of milliseconds
     * to wait until an error is thrown.
     */
    databaseStartupTimeoutMillis: number;
    /**
     * If the query should be retried when the database throws "cannot execute X in a read-only transaction"
     * NOTE: This typically happens during a fail over scenario when a read-replica is being promoted to master
     */
    reconnectOnReadOnlyTransactionError: boolean;
    /**
     * Milliseconds to wait between retry queries while the connection is marked as read-only. Allows you to throttle
     * how many retries should happen until readOnlyTransactionReconnectTimeoutMillis expires. A value of 0 will
     * try reconnecting immediately.
     */
    waitForReconnectReadOnlyTransactionMillis: number;
    /**
     * If queries continually return "cannot execute X in a read-only transaction", this is the total number of
     * milliseconds to wait until an error is thrown.
     */
    readOnlyTransactionReconnectTimeoutMillis: number;
    /**
     * If the query should be retried when the database throws "Client has encountered a connection error and is not queryable"
     * NOTE: This typically happens during a fail-over scenario with the cluster
     */
    reconnectOnConnectionError: boolean;
    /**
     * Milliseconds to wait between retry queries after receiving a connection error. Allows you to throttle
     * how many retries should happen until connectionReconnectTimeoutMillis expires. A value of 0 will
     * try reconnecting immediately.
     */
    waitForReconnectConnectionMillis: number;
    /**
     * If queries continually return "Client has encountered a connection error and is not queryable", this is the total number of
     * milliseconds to wait until an error is thrown.
     */
    connectionReconnectTimeoutMillis: number;
    /**
     * Specifies the regular expression to find named parameters in a query
     */
    namedParameterFindRegExp: RegExp;
    /**
     * Returns the regular expression used to replace a named parameter in a query
     */
    getNamedParameterReplaceRegExp: (namedParameter: string) => RegExp;
    /**
     * Gets the name of a named parameter without the symbols. This should correspond to the key in the query value object
     */
    getNamedParameterName: (namedParameterWithSymbols: string) => string;
    /**
     * Throw an error if a query takes longer than the specified milliseconds
     */
    query_timeout?: number;
    /**
     * Abort a query statement if it takes longer than the specified milliseconds
     */
    statement_timeout?: number;
}
interface PoolOptionsExplicit {
    host: string;
    database: string;
    user?: string;
    password?: string;
    port?: number;
    poolSize?: number;
    idleTimeoutMillis?: number;
    waitForAvailableConnectionTimeoutMillis?: number;
    connectionTimeoutMillis?: number;
    retryConnectionMaxRetries?: number;
    retryConnectionWaitMillis?: number;
    retryConnectionErrorCodes?: string[];
    reconnectOnDatabaseIsStartingError?: boolean;
    waitForDatabaseStartupMillis?: number;
    databaseStartupTimeoutMillis?: number;
    reconnectOnReadOnlyTransactionError?: boolean;
    waitForReconnectReadOnlyTransactionMillis?: number;
    readOnlyTransactionReconnectTimeoutMillis?: number;
    reconnectOnConnectionError?: boolean;
    waitForReconnectConnectionMillis?: number;
    connectionReconnectTimeoutMillis?: number;
    namedParameterFindRegExp?: RegExp;
    getNamedParameterReplaceRegExp?: (namedParameter: string) => RegExp;
    getNamedParameterName?: (namedParameterWithSymbols: string) => string;
    query_timeout?: number;
    statement_timeout?: number;
}
interface PoolOptionsImplicit {
    connectionString: string;
    poolSize?: number;
    idleTimeoutMillis?: number;
    waitForAvailableConnectionTimeoutMillis?: number;
    connectionTimeoutMillis?: number;
    retryConnectionMaxRetries?: number;
    retryConnectionWaitMillis?: number;
    retryConnectionErrorCodes?: string[];
    reconnectOnDatabaseIsStartingError?: boolean;
    waitForDatabaseStartupMillis?: number;
    databaseStartupTimeoutMillis?: number;
    reconnectOnReadOnlyTransactionError?: boolean;
    waitForReconnectReadOnlyTransactionMillis?: number;
    readOnlyTransactionReconnectTimeoutMillis?: number;
    reconnectOnConnectionError?: boolean;
    waitForReconnectConnectionMillis?: number;
    connectionReconnectTimeoutMillis?: number;
    namedParameterFindRegExp?: RegExp;
    getNamedParameterReplaceRegExp?: (namedParameter: string) => RegExp;
    getNamedParameterName?: (namedParameterWithSymbols: string) => string;
    query_timeout?: number;
    statement_timeout?: number;
}
type PoolClient = pg.Client & {
    uniqueId: string;
    idleTimeoutTimer?: NodeJS.Timeout;
    release: (removeConnection?: boolean) => Promise<void>;
    errorHandler: (err: Error) => void;
};
type PoolClientWithConnection = PoolClient & {
    connection?: Connection;
};
interface ConnectionAddedToPoolParams {
    connectionId: PoolClient['uniqueId'];
    retryAttempt: number;
    startTime: bigint;
}
interface PoolEvents {
    connectionRequestQueued: () => void;
    connectionRequestDequeued: () => void;
    connectionAddedToPool: (params: ConnectionAddedToPoolParams) => void;
    connectionRemovedFromPool: () => void;
    connectionIdle: () => void;
    connectionRemovedFromIdlePool: () => void;
    idleConnectionActivated: () => void;
    queryDeniedForReadOnlyTransaction: () => void;
    queryDeniedForConnectionError: () => void;
    waitingForDatabaseToStart: () => void;
    retryConnectionOnError: () => void;
    error: (error: Error, client?: PoolClient) => void;
}
type PoolEmitter = StrictEventEmitter<EventEmitter, PoolEvents>;
declare const Pool_base: new () => PoolEmitter;
declare class Pool extends Pool_base {
    /**
     * Gets the number of queued requests waiting for a database connection
     * @returns Number of queued requests
     */
    get waitingCount(): number;
    /**
     * Gets the number of idle connections
     * @returns Number of idle connections
     */
    get idleCount(): number;
    /**
     * Gets the total number of connections in the pool
     * @returns Total number of connections
     */
    get totalCount(): number;
    protected options: PoolOptionsBase & SslSettings & (PoolOptionsExplicit | PoolOptionsImplicit);
    protected connectionQueueEventEmitter: EventEmitter;
    protected connections: string[];
    protected idleConnections: PoolClient[];
    protected connectionQueue: string[];
    protected isEnding: boolean;
    constructor(options: SslSettingsOrAwsRdsSsl & (PoolOptionsExplicit | PoolOptionsImplicit));
    /**
     * Gets a client connection from the pool.
     * Note: You must call `.release()` when finished with the client connection object. That will release the connection back to the pool to be used by other requests.
     * @returns Client connection
     */
    connect(): Promise<PoolClient>;
    /**
     * Gets a connection to the database and executes the specified query using named parameters. This method will release the connection back to the pool when the query has finished.
     * @param {string} text
     * @param {object} values - Keys represent named parameters in the query
     * @returns Results from query
     */
    query<TRow extends QueryResultRow = any>(text: string, values: Record<string, any>): Promise<QueryResult<TRow>>;
    /**
     * Gets a connection to the database and executes the specified query. This method will release the connection back to the pool when the query has finished.
     * @param {string} text
     * @param {object[]} values
     * @returns Results from query
     */
    query<TRow extends QueryResultRow = any>(text: string, values?: any[]): Promise<QueryResult<TRow>>;
    /**
     * Drains the pool of all active client connections and prevents additional connections
     * @returns
     */
    end(): Promise<void>;
    /**
     * Drains the pool of all idle client connections.
     */
    drainIdleConnections(): Promise<void>;
    private _query;
    /**
     * Creates a new client connection to add to the pool
     * @param {string} connectionId
     * @param {number} [retryAttempt]
     * @param {bigint} [createConnectionStartTime] - High-resolution time (in nanoseconds) for when the connection was created
     * @param {[number,number]} [databaseStartupStartTime] - hrtime when the db was first listed as starting up
     * @returns Client connection
     */
    private _createConnection;
    /**
     * Removes the client connection from the pool and tries to gracefully shut it down
     * @param {PoolClient} client
     */
    private _removeConnection;
}

export { type ConnectionAddedToPoolParams, Pool, type PoolClient, type PoolClientWithConnection, type PoolOptionsBase, type PoolOptionsExplicit, type PoolOptionsImplicit, PostgresPoolError, type SslSettings, type SslSettingsOrAwsRdsSsl };
