import { Pool } from "pg";
import { EventEmitter } from "events";
import { logger } from "../monitoring/logger.js";
class ConnectionPool extends EventEmitter {
  pool;
  config;
  metrics;
  health;
  healthCheckTimer;
  metricsTimer;
  startTime;
  badConnections = /* @__PURE__ */ new Set();
  acquireTimes = [];
  constructor(config) {
    super();
    this.config = this.normalizeConfig(config);
    this.startTime = /* @__PURE__ */ new Date();
    this.metrics = {
      totalConnections: 0,
      idleConnections: 0,
      activeConnections: 0,
      waitingRequests: 0,
      totalAcquired: 0,
      totalReleased: 0,
      totalErrors: 0,
      averageAcquireTime: 0,
      peakConnections: 0,
      uptime: 0
    };
    this.health = {
      isHealthy: false,
      lastCheck: /* @__PURE__ */ new Date(),
      consecutiveFailures: 0,
      totalChecks: 0,
      totalFailures: 0,
      averageResponseTime: 0
    };
    this.pool = new Pool(this.config);
    this.setupPoolEvents();
    if (this.config.enableMetrics) {
      this.startMonitoring();
    }
  }
  normalizeConfig(config) {
    return {
      ...config,
      min: config.min ?? 2,
      max: config.max ?? 10,
      idleTimeoutMillis: config.idleTimeoutMillis ?? 3e4,
      connectionTimeoutMillis: config.connectionTimeoutMillis ?? 5e3,
      healthCheckInterval: config.healthCheckInterval ?? 3e4,
      healthCheckQuery: config.healthCheckQuery ?? "SELECT 1",
      retryOnFailure: config.retryOnFailure ?? true,
      maxRetries: config.maxRetries ?? 3,
      retryDelayMs: config.retryDelayMs ?? 1e3,
      enableMetrics: config.enableMetrics ?? true,
      metricsInterval: config.metricsInterval ?? 6e4
    };
  }
  setupPoolEvents() {
    this.pool.on("connect", (client) => {
      logger.debug("New database connection established");
      this.metrics.totalConnections++;
      this.updatePeakConnections();
      this.emit("connect", client);
    });
    this.pool.on("acquire", (client) => {
      this.metrics.totalAcquired++;
      this.emit("acquire", client);
    });
    this.pool.on("release", (client) => {
      this.metrics.totalReleased++;
      this.emit("release", client);
    });
    this.pool.on("remove", (client) => {
      logger.debug("Database connection removed from pool");
      this.metrics.totalConnections--;
      this.emit("remove", client);
    });
    this.pool.on("error", (error) => {
      logger.error("Database pool error:", error);
      this.metrics.totalErrors++;
      this.emit("error", error);
    });
  }
  updatePeakConnections() {
    const current = this.pool.totalCount;
    if (current > this.metrics.peakConnections) {
      this.metrics.peakConnections = current;
    }
  }
  startMonitoring() {
    if (this.config.healthCheckInterval > 0) {
      this.healthCheckTimer = setInterval(() => {
        this.performHealthCheck().catch((error) => {
          logger.error("Health check failed:", error);
        });
      }, this.config.healthCheckInterval);
    }
    if (this.config.metricsInterval > 0) {
      this.metricsTimer = setInterval(() => {
        this.updateMetrics();
        this.emit("metrics", this.getMetrics());
      }, this.config.metricsInterval);
    }
    this.performHealthCheck().catch((error) => {
      logger.warn("Initial health check failed:", error);
    });
  }
  async performHealthCheck() {
    const startTime = Date.now();
    let client;
    try {
      this.health.totalChecks++;
      client = await this.pool.connect();
      await client.query(this.config.healthCheckQuery);
      const responseTime = Date.now() - startTime;
      this.updateHealthMetrics(true, responseTime);
      logger.debug(`Health check passed in ${responseTime}ms`);
    } catch (error) {
      const responseTime = Date.now() - startTime;
      this.updateHealthMetrics(false, responseTime);
      logger.warn(`Health check failed after ${responseTime}ms:`, error);
      if (this.config.retryOnFailure && this.health.consecutiveFailures < this.config.maxRetries) {
        setTimeout(() => {
          this.performHealthCheck().catch(() => {
          });
        }, this.config.retryDelayMs);
      }
    } finally {
      if (client) {
        client.release();
      }
    }
  }
  updateHealthMetrics(success, responseTime) {
    this.health.lastCheck = /* @__PURE__ */ new Date();
    if (success) {
      this.health.isHealthy = true;
      this.health.consecutiveFailures = 0;
    } else {
      this.health.isHealthy = false;
      this.health.consecutiveFailures++;
      this.health.totalFailures++;
    }
    const weight = Math.min(this.health.totalChecks, 10);
    this.health.averageResponseTime = (this.health.averageResponseTime * (weight - 1) + responseTime) / weight;
  }
  updateMetrics() {
    this.metrics.idleConnections = this.pool.idleCount;
    this.metrics.activeConnections = this.pool.totalCount - this.pool.idleCount;
    this.metrics.waitingRequests = this.pool.waitingCount;
    this.metrics.uptime = Date.now() - this.startTime.getTime();
    if (this.acquireTimes.length > 0) {
      this.metrics.averageAcquireTime = this.acquireTimes.reduce((sum, time) => sum + time, 0) / this.acquireTimes.length;
      if (this.acquireTimes.length > 100) {
        this.acquireTimes = this.acquireTimes.slice(-100);
      }
    }
  }
  /**
   * Acquire a connection from the pool
   */
  async acquire() {
    const startTime = Date.now();
    try {
      const client = await this.pool.connect();
      const acquireTime = Date.now() - startTime;
      this.acquireTimes.push(acquireTime);
      if (this.badConnections.has(client)) {
        this.badConnections.delete(client);
        client.release(true);
        return this.acquire();
      }
      return client;
    } catch (error) {
      this.metrics.totalErrors++;
      logger.error("Failed to acquire connection:", error);
      throw error;
    }
  }
  /**
   * Release a connection back to the pool
   */
  release(client, error) {
    if (error) {
      client.release(true);
    } else {
      client.release();
    }
  }
  /**
   * Mark a connection as bad (will be removed on next acquire)
   */
  markConnectionAsBad(client) {
    this.badConnections.add(client);
    logger.warn("Connection marked as bad and will be removed");
  }
  /**
   * Get current connection metrics
   */
  getMetrics() {
    this.updateMetrics();
    return { ...this.metrics };
  }
  /**
   * Get current health status
   */
  getHealth() {
    return { ...this.health };
  }
  /**
   * Test connection to database
   */
  async ping() {
    try {
      const client = await this.acquire();
      await client.query("SELECT 1");
      this.release(client);
      return true;
    } catch {
      return false;
    }
  }
  /**
   * Get pool status information
   */
  getStatus() {
    return {
      totalCount: this.pool.totalCount,
      idleCount: this.pool.idleCount,
      waitingCount: this.pool.waitingCount,
      config: {
        min: this.config.min,
        max: this.config.max,
        idleTimeoutMillis: this.config.idleTimeoutMillis,
        connectionTimeoutMillis: this.config.connectionTimeoutMillis
      },
      health: this.getHealth(),
      metrics: this.getMetrics()
    };
  }
  /**
   * Close all connections and clean up
   */
  async close() {
    logger.info("Closing connection pool");
    if (this.healthCheckTimer) {
      clearInterval(this.healthCheckTimer);
    }
    if (this.metricsTimer) {
      clearInterval(this.metricsTimer);
    }
    await this.pool.end();
    this.badConnections.clear();
    this.emit("close");
    logger.info("Connection pool closed");
  }
  /**
   * Drain pool gracefully (wait for active connections to finish)
   */
  async drain(timeoutMs = 3e4) {
    logger.info("Draining connection pool");
    const startTime = Date.now();
    while (this.pool.totalCount - this.pool.idleCount > 0) {
      if (Date.now() - startTime > timeoutMs) {
        logger.warn("Pool drain timeout reached, forcing close");
        break;
      }
      await new Promise((resolve) => setTimeout(resolve, 100));
    }
    await this.close();
  }
  /**
   * Execute a query using a pooled connection
   */
  async query(text, params) {
    const client = await this.acquire();
    try {
      const result = await client.query(text, params);
      return {
        rows: result.rows,
        rowCount: result.rowCount || 0
      };
    } finally {
      this.release(client);
    }
  }
  /**
   * Execute multiple queries in a transaction
   */
  async transaction(callback) {
    const client = await this.acquire();
    try {
      await client.query("BEGIN");
      const result = await callback(client);
      await client.query("COMMIT");
      return result;
    } catch (error) {
      try {
        await client.query("ROLLBACK");
      } catch (rollbackError) {
        logger.error("Transaction rollback failed:", rollbackError);
        this.markConnectionAsBad(client);
      }
      throw error;
    } finally {
      this.release(client);
    }
  }
}
export {
  ConnectionPool
};
//# sourceMappingURL=connection-pool.js.map
