//! Helpers for building and inspecting Postgres connection pools.
//!
//! Centralizes pool option construction so the rest of the codebase can request
//! pools with consistent limits and provides lightweight snapshots that can be
//! shipped to telemetry/logging.

use crate::client::config::PoolConfig;
use chrono::{DateTime, Utc};
use sqlx::postgres::{PgPool, PgPoolOptions};
use std::time::Duration;

/// Point‑in‑time view of a pool's occupancy.
#[derive(Debug, Clone)]
pub struct ConnectionPoolSnapshot {
    pub client_name: String,
    pub pool_size: u32,
    pub idle_connections: u32,
    pub active_connections: u32,
    pub max_connections: u32,
    pub recorded_at: DateTime<Utc>,
}

impl ConnectionPoolSnapshot {
    pub fn new(client_name: String, pool: &PgPool) -> Self {
        let pool_size: u32 = pool.size();
        let idle: u32 = pool.num_idle().try_into().unwrap_or_default();
        let max_connections: u32 = pool.options().get_max_connections();
        let active_connections: u32 = pool_size.saturating_sub(idle);

        Self {
            client_name,
            pool_size,
            idle_connections: idle,
            active_connections,
            max_connections,
            recorded_at: Utc::now(),
        }
    }
}

/// ## `ConnectionPoolManager`
/// Constructs Postgres pools with shared defaults so we can change limits in one place.
///
/// # Arguments
///
/// * `config` - The pool configuration.
/// * `max_lifetime` - The maximum lifetime of a connection.
/// * `test_before_acquire` - Whether to test the connection before acquiring it.
///
/// # Default Values
///
/// * `max_lifetime` - 1800 seconds (30 minutes).
/// * `test_before_acquire` - false.
///
///
/// # Returns
///
/// A `ConnectionPoolManager` instance.
///
/// Constructs Postgres pools with shared defaults so we can change limits in one place.
///
#[derive(Clone, Debug)]
pub struct ConnectionPoolManager {
    config: PoolConfig,
    max_lifetime: Duration,
    test_before_acquire: bool,
}

impl Default for ConnectionPoolManager {
    /// ## `default`
    /// Create a new connection pool manager with default values.
    ///
    /// # Returns
    ///
    /// A `ConnectionPoolManager` instance.
    ///
    /// Constructs Postgres pools with shared defaults so we can change limits in one place.
    ///
    fn default() -> Self {
        Self {
            config: PoolConfig::default(),
            max_lifetime: Duration::from_secs(1800),
            test_before_acquire: false,
        }
    }
}

impl ConnectionPoolManager {
    /// ## `new`
    /// Create a new connection pool manager.
    ///
    /// # Arguments
    ///
    /// * `config` - The pool configuration.
    ///
    /// # Returns
    ///
    /// A `ConnectionPoolManager` instance.
    ///
    pub fn new(config: PoolConfig) -> Self {
        Self {
            config,
            ..Default::default()
        }
    }

    /// Customize the max lifetime used for pools created by this manager.
    pub fn with_max_lifetime(mut self, max_lifetime: Duration) -> Self {
        self.max_lifetime = max_lifetime;
        self
    }

    /// Enable/disable validating a pooled connection before handing it out.
    pub fn with_test_before_acquire(mut self, test_before_acquire: bool) -> Self {
        self.test_before_acquire = test_before_acquire;
        self
    }

    /// Create a new pool using the managed defaults.
    pub async fn connect(&self, uri: &str) -> Result<PgPool, sqlx::Error> {
        self.build_options().connect(uri).await
    }

    /// ## `build_options`
    /// Build `PgPoolOptions` with the configured defaults.
    ///
    /// # Arguments
    ///
    /// * `self` - The connection pool manager.
    ///
    /// # Returns
    ///
    /// A `PgPoolOptions` instance.
    ///
    /// Constructs Postgres pools with shared defaults so we can change limits in one place.
    ///
    pub fn build_options(&self) -> PgPoolOptions {
        PgPoolOptions::new()
            .max_connections(self.config.max_connections)
            .min_connections(self.config.min_connections)
            .acquire_timeout(self.config.connection_timeout)
            .idle_timeout(Some(self.config.idle_timeout))
            .max_lifetime(Some(self.max_lifetime))
            .test_before_acquire(self.test_before_acquire)
    }
}
