//! Bybit exchange builder pattern implementation.
//!
//! Provides a fluent API for constructing Bybit exchange instances with
//! type-safe configuration options.

use super::{Bybit, BybitOptions};
use ccxt_core::{ExchangeConfig, Result};
use serde_json::Value;
use std::collections::HashMap;

/// Builder for creating Bybit exchange instances.
///
/// Provides a fluent API for configuring all aspects of the Bybit exchange,
/// including authentication, connection settings, and Bybit-specific options.
///
/// # Example
///
/// ```no_run
/// use ccxt_exchanges::bybit::BybitBuilder;
///
/// let bybit = BybitBuilder::new()
///     .api_key("your-api-key")
///     .secret("your-secret")
///     .testnet(true)
///     .timeout(30)
///     .build()
///     .unwrap();
/// ```
#[derive(Debug, Clone)]
pub struct BybitBuilder {
    /// Exchange configuration
    config: ExchangeConfig,
    /// Bybit-specific options
    options: BybitOptions,
}

impl Default for BybitBuilder {
    fn default() -> Self {
        Self::new()
    }
}

impl BybitBuilder {
    /// Creates a new builder with default configuration.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use ccxt_exchanges::bybit::BybitBuilder;
    ///
    /// let builder = BybitBuilder::new();
    /// ```
    pub fn new() -> Self {
        Self {
            config: ExchangeConfig {
                id: "bybit".to_string(),
                name: "Bybit".to_string(),
                ..Default::default()
            },
            options: BybitOptions::default(),
        }
    }

    /// Sets the API key for authentication.
    ///
    /// # Arguments
    ///
    /// * `key` - The API key string.
    pub fn api_key(mut self, key: impl Into<String>) -> Self {
        self.config.api_key = Some(key.into());
        self
    }

    /// Sets the API secret for authentication.
    ///
    /// # Arguments
    ///
    /// * `secret` - The API secret string.
    pub fn secret(mut self, secret: impl Into<String>) -> Self {
        self.config.secret = Some(secret.into());
        self
    }

    /// Enables or disables testnet mode.
    ///
    /// When enabled, the exchange will connect to Bybit's testnet
    /// environment instead of the production environment.
    ///
    /// # Arguments
    ///
    /// * `enabled` - Whether to enable testnet mode.
    pub fn testnet(mut self, enabled: bool) -> Self {
        self.config.sandbox = enabled;
        self.options.testnet = enabled;
        self
    }

    /// Sets the account type for trading.
    ///
    /// Valid values: "UNIFIED", "CONTRACT", "SPOT".
    ///
    /// # Arguments
    ///
    /// * `account_type` - The account type string.
    pub fn account_type(mut self, account_type: impl Into<String>) -> Self {
        self.options.account_type = account_type.into();
        self
    }

    /// Sets the receive window for signed requests.
    ///
    /// The receive window specifies how long a request is valid after
    /// the timestamp. Default is 5000 milliseconds.
    ///
    /// # Arguments
    ///
    /// * `millis` - Receive window in milliseconds.
    pub fn recv_window(mut self, millis: u64) -> Self {
        self.options.recv_window = millis;
        self
    }

    /// Sets the request timeout in seconds.
    ///
    /// # Arguments
    ///
    /// * `seconds` - Timeout duration in seconds.
    pub fn timeout(mut self, seconds: u64) -> Self {
        self.config.timeout = seconds;
        self
    }

    /// Enables or disables rate limiting.
    ///
    /// # Arguments
    ///
    /// * `enabled` - Whether to enable rate limiting.
    pub fn enable_rate_limit(mut self, enabled: bool) -> Self {
        self.config.enable_rate_limit = enabled;
        self
    }

    /// Sets the HTTP proxy server URL.
    ///
    /// # Arguments
    ///
    /// * `proxy` - The proxy server URL.
    pub fn proxy(mut self, proxy: impl Into<String>) -> Self {
        self.config.proxy = Some(proxy.into());
        self
    }

    /// Enables or disables verbose logging.
    ///
    /// # Arguments
    ///
    /// * `enabled` - Whether to enable verbose logging.
    pub fn verbose(mut self, enabled: bool) -> Self {
        self.config.verbose = enabled;
        self
    }

    /// Sets a custom option.
    ///
    /// # Arguments
    ///
    /// * `key` - Option key.
    /// * `value` - Option value as JSON.
    pub fn option(mut self, key: impl Into<String>, value: Value) -> Self {
        self.config.options.insert(key.into(), value);
        self
    }

    /// Sets multiple custom options.
    ///
    /// # Arguments
    ///
    /// * `options` - HashMap of option key-value pairs.
    pub fn options(mut self, options: HashMap<String, Value>) -> Self {
        self.config.options.extend(options);
        self
    }

    /// Returns the current configuration (for testing purposes).
    #[cfg(test)]
    pub fn get_config(&self) -> &ExchangeConfig {
        &self.config
    }

    /// Returns the current options (for testing purposes).
    #[cfg(test)]
    pub fn get_options(&self) -> &BybitOptions {
        &self.options
    }

    /// Builds the Bybit exchange instance.
    ///
    /// # Returns
    ///
    /// Returns a `Result` containing the configured `Bybit` instance.
    ///
    /// # Errors
    ///
    /// Returns an error if the exchange cannot be initialized.
    pub fn build(self) -> Result<Bybit> {
        Bybit::new_with_options(self.config, self.options)
    }
}

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

    #[test]
    fn test_builder_default() {
        let builder = BybitBuilder::new();
        assert_eq!(builder.config.id, "bybit");
        assert_eq!(builder.config.name, "Bybit");
        assert!(!builder.config.sandbox);
        assert_eq!(builder.options.account_type, "UNIFIED");
        assert_eq!(builder.options.recv_window, 5000);
    }

    #[test]
    fn test_builder_api_key() {
        let builder = BybitBuilder::new().api_key("test-key");
        assert_eq!(builder.config.api_key, Some("test-key".to_string()));
    }

    #[test]
    fn test_builder_secret() {
        let builder = BybitBuilder::new().secret("test-secret");
        assert_eq!(builder.config.secret, Some("test-secret".to_string()));
    }

    #[test]
    fn test_builder_testnet() {
        let builder = BybitBuilder::new().testnet(true);
        assert!(builder.config.sandbox);
        assert!(builder.options.testnet);
    }

    #[test]
    fn test_builder_account_type() {
        let builder = BybitBuilder::new().account_type("CONTRACT");
        assert_eq!(builder.options.account_type, "CONTRACT");
    }

    #[test]
    fn test_builder_recv_window() {
        let builder = BybitBuilder::new().recv_window(10000);
        assert_eq!(builder.options.recv_window, 10000);
    }

    #[test]
    fn test_builder_timeout() {
        let builder = BybitBuilder::new().timeout(60);
        assert_eq!(builder.config.timeout, 60);
    }

    #[test]
    fn test_builder_chaining() {
        let builder = BybitBuilder::new()
            .api_key("key")
            .secret("secret")
            .testnet(true)
            .timeout(30)
            .recv_window(10000)
            .account_type("SPOT");

        assert_eq!(builder.config.api_key, Some("key".to_string()));
        assert_eq!(builder.config.secret, Some("secret".to_string()));
        assert!(builder.config.sandbox);
        assert_eq!(builder.config.timeout, 30);
        assert_eq!(builder.options.recv_window, 10000);
        assert_eq!(builder.options.account_type, "SPOT");
    }

    #[test]
    fn test_builder_build() {
        let result = BybitBuilder::new().build();
        assert!(result.is_ok());

        let bybit = result.unwrap();
        assert_eq!(bybit.id(), "bybit");
        assert_eq!(bybit.name(), "Bybit");
    }

    #[test]
    fn test_builder_build_with_credentials() {
        let result = BybitBuilder::new()
            .api_key("test-key")
            .secret("test-secret")
            .build();

        assert!(result.is_ok());
    }

    #[test]
    fn test_builder_enable_rate_limit() {
        let builder = BybitBuilder::new().enable_rate_limit(false);
        assert!(!builder.config.enable_rate_limit);
    }

    #[test]
    fn test_builder_proxy() {
        let builder = BybitBuilder::new().proxy("http://proxy.example.com:8080");
        assert_eq!(
            builder.config.proxy,
            Some("http://proxy.example.com:8080".to_string())
        );
    }

    #[test]
    fn test_builder_verbose() {
        let builder = BybitBuilder::new().verbose(true);
        assert!(builder.config.verbose);
    }
}
