//! Connection wrapper providing health checks and metadata
//!
//! Wraps tokio_postgres::Client with lifecycle tracking and validation capabilities.

use crate::error::Result;
use std::time::{Duration, Instant};

/// Wrapper around tokio_postgres::Client with lifecycle tracking
///
/// Tracks creation time and last usage to enable connection lifecycle
/// management (max_lifetime, idle timeout detection).
pub struct Connection {
    client: tokio_postgres::Client,
    created_at: Instant,
    last_used: Instant,
}

impl Connection {
    /// Create a new Connection wrapper
    ///
    /// # Arguments
    /// * `client` - The underlying tokio_postgres client
    ///
    /// # Returns
    /// A new Connection instance with timestamps initialized to now
    pub fn new(client: tokio_postgres::Client) -> Self {
        let now = Instant::now();
        Self { client, created_at: now, last_used: now }
    }

    /// Check if the connection is still valid
    ///
    /// Executes a simple `SELECT 1` query to verify the connection is alive
    /// and can communicate with the database.
    ///
    /// # Returns
    /// * `Ok(true)` if connection is valid
    /// * `Ok(false)` if connection failed validation
    /// * `Err` if validation could not be performed
    ///
    /// # Errors
    /// Returns `PoolError::ConnectionFailed` if the validation query fails
    pub async fn is_valid(&mut self) -> Result<bool> {
        match self.client.query_one("SELECT 1", &[]).await {
            Ok(_) => {
                self.touch();
                Ok(true)
            }
            Err(_e) => {
                // Connection failed - this is expected behavior, not an error
                // We return Ok(false) to indicate validation failed
                Ok(false)
            }
        }
    }

    /// Get the age of this connection since creation
    ///
    /// # Returns
    /// Duration since the connection was created
    pub fn age(&self) -> Duration {
        self.created_at.elapsed()
    }

    /// Get the idle time since last use
    ///
    /// # Returns
    /// Duration since the connection was last used
    pub fn idle_time(&self) -> Duration {
        self.last_used.elapsed()
    }

    /// Update the last_used timestamp to now
    ///
    /// Should be called whenever the connection is checked out or used
    /// to accurately track idle time.
    pub fn touch(&mut self) {
        self.last_used = Instant::now();
    }

    /// Access the underlying tokio_postgres client
    ///
    /// # Returns
    /// Reference to the wrapped Client for executing queries
    pub fn client(&self) -> &tokio_postgres::Client {
        &self.client
    }
}

impl std::fmt::Debug for Connection {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Connection")
            .field("created_at", &self.created_at)
            .field("last_used", &self.last_used)
            .field("age", &self.age())
            .field("idle_time", &self.idle_time())
            .finish_non_exhaustive()
    }
}

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

    // Note: Connection wraps tokio_postgres::Client, which requires a real
    // database connection. Full functionality is tested in integration tests
    // (tests/basic.rs). These unit tests verify the metadata tracking logic.

    #[test]
    fn test_instant_elapsed() {
        // Verify Instant::elapsed() works as expected for age/idle_time
        let now = Instant::now();
        std::thread::sleep(Duration::from_millis(10));
        let elapsed = now.elapsed();
        assert!(elapsed >= Duration::from_millis(10));
    }

    #[test]
    fn test_timestamp_comparison() {
        // Verify that touch() logic would work correctly
        let t1 = Instant::now();
        std::thread::sleep(Duration::from_millis(10));
        let t2 = Instant::now();

        // Older timestamp has longer elapsed time
        assert!(t1.elapsed() > t2.elapsed());
    }
}
