//! Lifecycle-layer implementation for [`ConnectionPool`].
//!
//! [`ConnectionPool`] owns the underlying [`PgPool`] plus the
//! [`ConnectionPoolManager`] that produced it. All open/close/reconfigure
//! operations flow through this layer so the query executor never has to
//! manage pool lifetime itself.

use sqlx::postgres::PgPool;

use crate::features::connection_pooler::{ConnectionPool, ConnectionPoolManager};

impl ConnectionPool {
    /// Wrap an already-connected [`PgPool`] with its owning manager.
    ///
    /// Crate-internal: external callers go through
    /// [`ConnectionPoolManager::open`](super::ConnectionPoolManager::open)
    /// which is the canonical "create" entry point.
    pub(crate) fn wrap(client_name: String, pool: PgPool, manager: ConnectionPoolManager) -> Self {
        Self {
            client_name,
            pool,
            manager,
        }
    }

    /// Open a new pool end-to-end: builds options via `manager` and connects
    /// to `uri`. Thin convenience over [`ConnectionPoolManager::open`].
    pub async fn open(
        client_name: String,
        uri: &str,
        manager: &ConnectionPoolManager,
    ) -> Result<Self, sqlx::Error> {
        manager.open(client_name, uri).await
    }

    /// Client identifier this pool was opened for.
    pub fn client_name(&self) -> &str {
        &self.client_name
    }

    /// Borrow the underlying [`PgPool`] for query execution.
    ///
    /// Returned as a reference so callers can `.clone()` (sqlx's `PgPool` is
    /// `Arc`-backed and cheap to clone) or borrow as needed.
    pub fn pg_pool(&self) -> &PgPool {
        &self.pool
    }

    /// Borrow the manager that produced this pool.
    pub fn manager(&self) -> &ConnectionPoolManager {
        &self.manager
    }

    /// Gracefully drain and close the pool, waiting for outstanding
    /// connections to finish.
    pub async fn close(self) {
        self.pool.close().await;
    }

    /// Rebuild the pool with a new `manager`/`uri` atomically.
    ///
    /// `sqlx`'s [`PgPool`] options are immutable after construction, so
    /// "editing" pool configuration means opening a fresh pool and gracefully
    /// closing the old one. On failure, the existing pool is preserved and
    /// the error is propagated.
    pub async fn reconfigure(
        &mut self,
        uri: &str,
        manager: ConnectionPoolManager,
    ) -> Result<(), sqlx::Error> {
        let replacement = manager.open(self.client_name.clone(), uri).await?;
        let previous = std::mem::replace(&mut self.pool, replacement.pool);
        self.manager = manager;
        previous.close().await;
        Ok(())
    }
}
