//! Connection pooler feature module.
//!
//! # Architecture
//!
//! Owns the Postgres connection pool abstraction split across **three domain
//! layers**, each with its own file so responsibilities never blur:
//!
//! | Layer | Type | File | Concern |
//! | --- | --- | --- | --- |
//! | Factory | [`ConnectionPoolManager`] | `manager.rs` | Builds [`PgPoolOptions`](sqlx::postgres::PgPoolOptions) from defaults and opens pools. Carries no runtime state; cloning is cheap and intentional. |
//! | Lifecycle | [`ConnectionPool`] | `pool.rs` | Owns a live [`PgPool`] plus the manager that created it. Handles `open`/`close`/`reconfigure`. |
//! | Telemetry | [`ConnectionPoolSnapshot`] | `snapshot.rs` | Pure, read-only point-in-time view for logging/metrics. No I/O, no locks. |
//! | Prometheus | `impl ConnectionPoolSnapshot` | `prometheus.rs` | Serializes snapshots into `/metrics` exposition format (`athena_pg_pool_*`). |
//!
//! Struct **declarations** live in this file (`mod.rs`) so the shape of the
//! public API is visible at a glance. **Implementations** are in per-domain
//! sibling files so each file reads as one cohesive concern.
//!
//! # Typical lifecycle
//!
//! ```no_run
//! # use athena_rs::features::connection_pooler::ConnectionPoolManager;
//! # async fn demo(uri: &str) -> Result<(), sqlx::Error> {
//! // 1. Build a factory once, share its defaults everywhere.
//! let manager = ConnectionPoolManager::default()
//!     .with_test_before_acquire(true);
//!
//! // 2. Open a live pool for a named client.
//! let mut pool = manager.open("logging".to_string(), uri).await?;
//!
//! // 3. Capture telemetry without stopping the world.
//! let snapshot = pool.snapshot();
//! tracing::info!(?snapshot, "logging pool occupancy");
//!
//! // 4. Reconfigure (atomic replace) when defaults change.
//! pool.reconfigure(uri, manager.clone()).await?;
//!
//! // 5. Drain on shutdown.
//! pool.close().await;
//! # Ok(()) }
//! ```
//!
//! # Why three types instead of one
//!
//! Separating the factory from the live pool lets multiple pools share
//! identical limits without reaching into a global. Separating the snapshot
//! from the pool keeps telemetry free of async/locking concerns: the snapshot
//! is a plain `Clone` data struct you can ship across threads, serialize,
//! persist, or hand to a Prometheus exposition writer without touching the
//! live pool again.

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

/// Factory for [`ConnectionPool`]s with shared defaults.
///
/// Centralizes pool-option construction so connection limits, lifetime caps,
/// and acquisition behaviour can change in exactly one place and apply to every
/// client pool produced afterwards.
///
/// The manager is pure configuration — it holds no runtime handles — so it is
/// cheap to [`Clone`] and safe to pass across tasks. Every [`ConnectionPool`]
/// keeps a clone of the manager that produced it so it can rebuild itself on
/// [`ConnectionPool::reconfigure`] without extra plumbing.
#[derive(Clone, Debug)]
pub struct ConnectionPoolManager {
    /// Size, timeout, and min/max limits applied to new pools.
    pub(crate) config: PoolConfig,
    /// Hard upper bound on how long any one connection may live.
    pub(crate) max_lifetime: Duration,
    /// When `true`, sqlx issues a lightweight liveness check before handing a
    /// pooled connection back to the caller. Trades a round-trip for resilience.
    pub(crate) test_before_acquire: bool,
}

/// Live, owning wrapper around a Postgres [`PgPool`].
///
/// Carries its `client_name` label (used in logs/metrics) and the
/// [`ConnectionPoolManager`] that produced it, so the pool can be
/// reconfigured or snapshotted without any external registry lookup.
///
/// `Clone` is implemented for convenience — sqlx's [`PgPool`] is `Arc`-backed,
/// so cloning shares the same underlying pool. The manager clone is also
/// cheap. Use [`ConnectionPool::close`] exactly once (on the last shared
/// instance) to drain cleanly.
#[derive(Clone, Debug)]
pub struct ConnectionPool {
    /// Identifier used in `athena_pg_pool_*` labels and structured logs.
    pub(crate) client_name: String,
    /// The live sqlx pool — `Arc`-backed internally.
    pub(crate) pool: PgPool,
    /// Factory defaults that produced this pool, kept for `reconfigure`.
    pub(crate) manager: ConnectionPoolManager,
}

/// Point‑in‑time, read-only view of a [`ConnectionPool`]'s occupancy.
///
/// Captured by [`ConnectionPool::snapshot`]. Completely decoupled from the
/// live pool: no locks are held, no async is required, and the snapshot never
/// references back into the pool. Safe to [`Clone`], [`serde::Serialize`],
/// ship across threads, and persist to Postgres (`client_connections` table)
/// or emit as Prometheus samples (via
/// [`write_prometheus`](crate::features::connection_pooler::prometheus)).
///
/// `recorded_at` is the single source of truth for staleness detection —
/// consumers that care about freshness should compare it against `Utc::now()`
/// rather than relying on wall clock at read time.
#[derive(Debug, Clone)]
pub struct ConnectionPoolSnapshot {
    /// Logical client identifier this snapshot belongs to.
    pub client_name: String,
    /// Current number of physical connections owned by the pool.
    ///
    /// Equal to `idle_connections + active_connections`.
    pub pool_size: u32,
    /// Connections currently idle in the pool, available for acquisition.
    pub idle_connections: u32,
    /// Connections currently checked out by the application (`size - idle`).
    pub active_connections: u32,
    /// Configured ceiling on `pool_size` (from
    /// [`PgPoolOptions::max_connections`](sqlx::postgres::PgPoolOptions::max_connections)).
    pub max_connections: u32,
    /// Whether the underlying [`PgPool`] reported itself as closed at capture
    /// time. A `true` value means no further acquisitions will succeed.
    pub is_closed: bool,
    /// Wall-clock moment at which this snapshot was captured.
    pub recorded_at: DateTime<Utc>,
}

pub mod manager;
pub mod pool;
pub mod prometheus;
pub mod snapshot;

#[cfg(feature = "deadpool_experimental")]
pub mod deadpool_experimental;
