use std::time::Duration;

use deadpool_redis::{Config as RedisConfig, Pool, Runtime};

use crate::error::RedisResult;

/// Configuration for building a Redis connection pool.
pub struct PoolConfig {
    /// Redis connection URL (e.g. `redis://localhost:6379`).
    pub url: String,
    /// Maximum number of connections in the pool.
    pub max_size: usize,
    /// Timeout for waiting to acquire a connection.
    pub wait_timeout: Duration,
    /// Timeout for creating a new connection.
    pub create_timeout: Duration,
    /// Timeout for recycling an existing connection.
    pub recycle_timeout: Duration,
}

impl PoolConfig {
    /// Create a config with sensible defaults for the given URL.
    pub fn new(url: impl Into<String>) -> Self {
        Self {
            url: url.into(),
            max_size: 16,
            wait_timeout: Duration::from_secs(2),
            create_timeout: Duration::from_secs(2),
            recycle_timeout: Duration::from_secs(2),
        }
    }

    pub fn max_size(mut self, size: usize) -> Self {
        self.max_size = size;
        self
    }

    pub fn wait_timeout(mut self, timeout: Duration) -> Self {
        self.wait_timeout = timeout;
        self
    }

    pub fn create_timeout(mut self, timeout: Duration) -> Self {
        self.create_timeout = timeout;
        self
    }

    pub fn recycle_timeout(mut self, timeout: Duration) -> Self {
        self.recycle_timeout = timeout;
        self
    }

    /// Build the `deadpool_redis::Pool`.
    pub fn build(self) -> RedisResult<Pool> {
        let cfg = RedisConfig::from_url(&self.url);
        Ok(cfg
            .builder()?
            .max_size(self.max_size)
            .wait_timeout(Some(self.wait_timeout))
            .create_timeout(Some(self.create_timeout))
            .recycle_timeout(Some(self.recycle_timeout))
            .runtime(Runtime::Tokio1)
            .build()?)
    }
}
