//! Connection pool implementation
//!
//! ⚠️ DOGFOOD NOTICE: Contains intentional violations 6-7 for Aphoria demonstration
//!
//! **VIOLATION 6:** No connection validation before checkout from `get()` method.
//!                  Should call `conn.is_valid()` but doesn't.
//!                  Consequence: Returns stale/broken connections to application.
//!
//! **VIOLATION 7:** No metrics exposed (no PoolMetrics field in struct).
//!                  Should track active/idle/waiting counts.
//!                  Consequence: No observability into pool health.
//!
//! These violations will be:
//! - Detected by Aphoria scan in Day 3
//! - Fixed incrementally in Day 4

use crate::{
    config::PoolConfig,
    connection::Connection,
    error::{PoolError, Result},
};
use std::collections::VecDeque;
use std::sync::Arc;
use tokio::sync::Mutex;

/// PostgreSQL connection pool with lifecycle management
///
/// Manages a pool of reusable database connections with support for:
/// - Connection lifecycle tracking (creation time, last usage)
/// - Automatic connection creation on demand
/// - Thread-safe connection checkout/return
///
/// # Intentional Violations
///
/// ❌ **VIOLATION 7:** No metrics field
/// Should have `metrics: Arc<PoolMetrics>` to track:
/// - Active connections (checked out)
/// - Idle connections (available in pool)
/// - Waiting requests (blocked on connection)
/// - Connection errors
/// - Validation failures
///
/// Without metrics, operators cannot:
/// - Detect pool exhaustion before failure
/// - Tune pool sizing based on actual usage
/// - Alert on connection health degradation
/// - Debug performance issues
pub struct ConnectionPool {
    config: PoolConfig,
    connections: Arc<Mutex<VecDeque<Connection>>>,
    // ❌ VIOLATION 7: No metrics field
    // Should be: metrics: Arc<PoolMetrics>,
}

impl ConnectionPool {
    /// Create a new connection pool with the given configuration
    ///
    /// # Arguments
    /// * `config` - Pool configuration (timeouts, limits, connection string)
    ///
    /// # Returns
    /// * `Ok(ConnectionPool)` - Successfully initialized pool
    /// * `Err(PoolError)` - Configuration validation failed
    ///
    /// # Errors
    /// Returns `PoolError::InvalidConfiguration` if config is invalid
    pub fn new(config: PoolConfig) -> Result<Self> {
        Ok(Self {
            config,
            connections: Arc::new(Mutex::new(VecDeque::new())),
            // ❌ VIOLATION 7: No metrics initialization
            // Should be: metrics: Arc::new(PoolMetrics::new()),
        })
    }

    /// Get a connection from the pool
    ///
    /// Returns an available connection from the pool, or creates a new one
    /// if the pool is empty (subject to max_connections limit if configured).
    ///
    /// # Returns
    /// * `Ok(Connection)` - A connection ready for use
    /// * `Err(PoolError)` - Pool exhausted or connection creation failed
    ///
    /// # Errors
    /// - `PoolError::PoolExhausted` if max_connections reached and all in use
    /// - `PoolError::ConnectionFailed` if new connection creation fails
    /// - `PoolError::ConnectionTimeout` if acquisition exceeds configured timeout
    ///
    /// # ❌ VIOLATION 6: Missing pre-checkout validation
    ///
    /// Claims violated: `dbpool/validation/frequency` required `on_checkout`
    ///
    /// This method should call `conn.is_valid().await?` before returning a
    /// connection from the pool, but it doesn't. This means:
    ///
    /// **Consequence:**
    /// - Returns stale connections that failed during idle (network blip, DB restart)
    /// - Application receives broken connection, query fails
    /// - User sees "connection reset by peer" or "broken pipe" errors
    /// - No automatic retry, request fails to client
    ///
    /// **Real-world scenario:**
    /// 1. Connection sits idle for 5 minutes
    /// 2. Database restarts during deployment
    /// 3. Connection is now dead but still in pool
    /// 4. Application calls `get()`, receives dead connection
    /// 5. Application tries to execute query → immediate failure
    /// 6. Request fails, user sees 500 error
    ///
    /// **Correct implementation would be:**
    /// ```rust,ignore
    /// if let Some(mut conn) = conns.pop_front() {
    ///     if conn.is_valid().await? {
    ///         return Ok(conn);
    ///     }
    ///     // Drop invalid connection, try next
    /// }
    /// ```
    pub async fn get(&mut self) -> Result<Connection> {
        let mut conns = self.connections.lock().await;

        // ❌ VIOLATION 6: Pop and return immediately without validation
        // Should check conn.is_valid() before returning!
        if let Some(conn) = conns.pop_front() {
            // ❌ No validation here - just return potentially stale connection
            return Ok(conn);
        }

        // Pool empty - create new connection
        drop(conns); // Release lock before potentially slow operation
        self.create_connection().await
    }

    /// Return a connection to the pool
    ///
    /// Adds the connection to the back of the pool queue, making it available
    /// for future `get()` calls.
    ///
    /// # Arguments
    /// * `conn` - The connection to return to the pool
    ///
    /// # Note
    /// No validation is performed on return. Validation happens at checkout
    /// time (or should, but VIOLATION 6 means it doesn't in this implementation).
    pub async fn return_connection(&self, conn: Connection) {
        let mut conns = self.connections.lock().await;
        conns.push_back(conn);
    }

    /// Get the current size of the connection pool
    ///
    /// Returns the number of idle connections currently in the pool.
    /// Does not include connections that are checked out.
    ///
    /// # Returns
    /// Number of idle connections available
    pub async fn size(&self) -> usize {
        let conns = self.connections.lock().await;
        conns.len()
    }

    /// Create a new database connection
    ///
    /// Private helper that establishes a new connection using the configured
    /// connection string.
    ///
    /// # Returns
    /// * `Ok(Connection)` - New connection ready for use
    /// * `Err(PoolError)` - Connection establishment failed
    ///
    /// # Errors
    /// - `PoolError::ConnectionFailed` if connection cannot be established
    /// - `PoolError::InvalidCredentials` if authentication fails
    /// - `PoolError::ConnectionTimeout` if connection takes too long
    async fn create_connection(&self) -> Result<Connection> {
        // Parse connection string and connect
        let (client, connection) =
            tokio_postgres::connect(&self.config.connection_string, tokio_postgres::NoTls)
                .await
                .map_err(|e| PoolError::connection_failed(format!("Failed to connect: {}", e)))?;

        // Spawn connection task to handle async messages
        tokio::spawn(async move {
            if let Err(e) = connection.await {
                eprintln!("Connection error: {}", e);
            }
        });

        Ok(Connection::new(client))
    }
}

impl std::fmt::Debug for ConnectionPool {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ConnectionPool")
            .field("config", &self.config)
            .field("connections", &"Arc<Mutex<VecDeque<Connection>>>")
            .finish_non_exhaustive()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_pool_creation() {
        let config = PoolConfig::default();
        let pool = ConnectionPool::new(config);
        assert!(pool.is_ok());
    }

    #[tokio::test]
    async fn test_pool_size_empty() {
        let config = PoolConfig::default();
        let pool = ConnectionPool::new(config).unwrap();
        assert_eq!(pool.size().await, 0);
    }

    #[test]
    fn test_pool_debug() {
        let config = PoolConfig::default();
        let pool = ConnectionPool::new(config).unwrap();
        let debug_str = format!("{:?}", pool);
        assert!(debug_str.contains("ConnectionPool"));
    }

    // Note: Full integration tests with real database connections are in tests/basic.rs
    // These unit tests only verify the structure and basic functionality without I/O
}
