//! Builder helpers for AthenaClient configuration.
use crate::client::backend::{BackendError, BackendType};
use crate::client::config::{ClientConfig, ConnectionConfig, HealthConfig, PoolConfig};
use std::time::Duration;
use thiserror::Error;

/// ## `BuilderError`
/// Possible errors while building an Athena client configuration.
///
/// # Arguments
///
/// * `MissingUrl` - The connection URL is required.
///
/// # Returns
///
/// A `BuilderError` containing the error.
///
#[derive(Error, Debug)]
pub enum BuilderError {
    #[error("connection URL is required")]
    MissingUrl,
}

/// ## `AthenaClientBuilder`
/// Fluent builder for Athena clients.
///
/// # Arguments
///
/// * `backend` - The backend type.
/// * `url` - The connection URL.
/// * `key` - The connection key.
/// * `client_name` - The client name.
/// * `ssl` - Whether to use SSL.
/// * `port` - The port.
/// * `database` - The database.
/// * `pool` - The pool configuration.
/// * `health` - The health configuration.
///
/// # Returns
///
/// A `AthenaClientBuilder` containing the builder.
///
pub struct AthenaClientBuilder {
    backend: BackendType,
    url: Option<String>,
    key: Option<String>,
    client_name: Option<String>,
    ssl: Option<bool>,
    port: Option<u16>,
    database: Option<String>,
    pool: PoolConfig,
    health: HealthConfig,
}

impl Default for AthenaClientBuilder {
    /// ## `default`
    /// Create a new Athena client builder.
    ///
    /// # Arguments
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    fn default() -> Self {
        Self::new()
    }
}

impl AthenaClientBuilder {
    /// ## `new`
    /// Create a new Athena client builder.
    ///
    /// # Arguments
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn new() -> Self {
        Self {
            backend: BackendType::Native,
            url: None,
            key: None,
            client_name: None,
            ssl: None,
            port: None,
            database: None,
            pool: PoolConfig::default(),
            health: HealthConfig::default(),
        }
    }

    /// ## `backend`
    /// Set the backend type.
    ///
    /// # Arguments
    ///
    /// * `backend` - The backend type.
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn backend(mut self, backend: BackendType) -> Self {
        self.backend = backend;
        self
    }

    /// ## `url`
    /// Set the connection URL.
    ///
    /// # Arguments
    ///
    /// * `url` - The connection URL.
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn url(mut self, url: impl Into<String>) -> Self {
        self.url = Some(url.into());
        self
    }

    /// ## `key`
    /// Set the connection key.
    ///
    /// # Arguments
    ///
    /// * `key` - The connection key.
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn key(mut self, key: impl Into<String>) -> Self {
        self.key = Some(key.into());
        self
    }

    /// ## `client`
    /// Set the client name.
    ///
    /// # Arguments
    ///
    /// * `client_name` - The client name.
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn client(mut self, client_name: impl Into<String>) -> Self {
        self.client_name = Some(client_name.into());
        self
    }

    /// ## `ssl`
    /// Set whether to use SSL.
    ///
    /// # Arguments
    ///
    /// * `enabled` - Whether to use SSL.
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn ssl(mut self, enabled: bool) -> Self {
        self.ssl = Some(enabled);
        self
    }

    /// ## `port`
    /// Set the port.
    ///
    /// # Arguments
    ///
    /// * `port` - The port.
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn port(mut self, port: u16) -> Self {
        self.port = Some(port);
        self
    }

    /// ## `database`
    /// Set the database.
    ///
    /// # Arguments
    ///
    /// * `database` - The database.
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn database(mut self, database: impl Into<String>) -> Self {
        self.database = Some(database.into());
        self
    }

    /// ## `max_connections`
    /// Set the maximum number of connections.
    ///
    /// # Arguments
    ///
    /// * `max` - The maximum number of connections.
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn max_connections(mut self, max: u32) -> Self {
        self.pool.max_connections = max;
        self
    }

    /// ## `min_connections`
    /// Set the minimum number of connections.
    ///
    /// # Arguments
    ///
    /// * `min` - The minimum number of connections.
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn min_connections(mut self, min: u32) -> Self {
        self.pool.min_connections = min;
        self
    }

    /// ## `connection_timeout`
    /// Set the connection timeout.
    ///
    /// # Arguments
    ///
    /// * `timeout` - The connection timeout.
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn connection_timeout(mut self, timeout: Duration) -> Self {
        self.pool.connection_timeout = timeout;
        self
    }

    /// ## `idle_timeout`
    /// Set the idle timeout.
    ///
    /// # Arguments
    ///
    /// * `timeout` - The idle timeout.
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn idle_timeout(mut self, timeout: Duration) -> Self {
        self.pool.idle_timeout = timeout;
        self
    }

    /// ## `health_tracking`
    /// Set whether to enable health tracking.
    ///
    /// # Arguments
    ///
    /// * `enabled` - Whether to enable health tracking.
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn health_tracking(mut self, enabled: bool) -> Self {
        self.health.enabled = enabled;
        self
    }

    /// ## `circuit_breaker_threshold`
    /// Set the circuit breaker threshold.
    ///
    /// # Arguments
    ///
    /// * `threshold` - The circuit breaker threshold.
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn circuit_breaker_threshold(mut self, threshold: u32) -> Self {
        self.health.circuit_breaker_threshold = threshold;
        self
    }
    /// ## `circuit_breaker_timeout`
    /// Set the circuit breaker timeout.
    ///
    /// # Arguments
    ///
    /// * `timeout` - The circuit breaker timeout.
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn circuit_breaker_timeout(mut self, timeout: Duration) -> Self {
        self.health.circuit_breaker_timeout = timeout;
        self
    }
    /// ## `health_check_interval`
    /// Set the health check interval.
    ///
    /// # Arguments
    ///
    /// * `interval` - The health check interval.
    ///
    /// # Returns
    ///
    /// A `AthenaClientBuilder` containing the builder.
    ///
    pub fn health_check_interval(mut self, interval: Duration) -> Self {
        self.health.check_interval = interval;
        self
    }

    /// ## `build_config`
    /// Finalize builder into shared configuration.
    ///
    /// # Arguments
    ///
    /// # Returns
    ///
    /// A `Result` containing the client configuration.
    ///
    /// A `BuilderError` containing the error.
    ///
    pub fn build_config(self) -> Result<ClientConfig, BuilderError> {
        let url: String = self.url.ok_or(BuilderError::MissingUrl)?;
        let mut connection: ConnectionConfig = ConnectionConfig::new(url);

        if let Some(key) = self.key {
            connection.key = Some(key);
        }

        if let Some(ssl) = self.ssl {
            connection.ssl = ssl;
        }

        connection.port = self.port;
        connection.database = self.database;

        Ok(ClientConfig::new(
            self.backend,
            self.client_name,
            connection,
            self.pool,
            self.health,
        ))
    }
}

impl From<BuilderError> for BackendError {
    /// ## `from`
    /// Convert a `BuilderError` to a `BackendError`.
    ///
    /// # Arguments
    ///
    /// * `value` - The `BuilderError` to convert.
    ///
    /// # Returns
    ///
    /// A `BackendError` containing the error.
    ///
    fn from(value: BuilderError) -> Self {
        BackendError::Generic(value.to_string())
    }
}

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

    #[test]
    fn builder_defaults_backend_to_native() {
        let config = AthenaClientBuilder::new()
            .url("http://localhost:4052")
            .build_config()
            .expect("builder should succeed with URL");

        assert_eq!(config.backend_type, BackendType::Native);
    }

    #[test]
    fn builder_persists_client_name_when_set() {
        let config = AthenaClientBuilder::new()
            .url("http://localhost:4052")
            .client("reporting")
            .build_config()
            .expect("builder should succeed with URL");

        assert_eq!(config.client_name.as_deref(), Some("reporting"));
    }

    #[test]
    fn builder_requires_url() {
        let err = AthenaClientBuilder::new()
            .build_config()
            .expect_err("missing URL should fail");

        assert!(matches!(err, BuilderError::MissingUrl));
    }
}
