use super::HttpConfig;
use std::collections::HashMap;
/// Builder for [`HttpConfig`].
#[derive(Debug, Clone, Default)]
#[must_use]
pub struct HttpConfigBuilder(HttpConfig);

impl HttpConfigBuilder {
    /// Set the overall request timeout in seconds.
    ///
    /// This timeout applies to the entire request, including connection
    /// establishment, sending the request, and reading the response.
    ///
    /// # Parameters
    /// - `value`: Timeout duration in seconds. Must be between 1 and 86,400.
    ///
    /// # Returns
    /// The builder for method chaining.
    ///
    /// # Example
    /// ```rust
    /// use scanclient::HttpConfig;
    ///
    /// let config = HttpConfig::builder()
    ///     .timeout_secs(30)
    ///     .build();
    /// assert_eq!(config.timeout_secs, 30);
    /// ```
    pub fn timeout_secs(mut self, value: u64) -> Self {
        self.0.timeout_secs = value;
        self
    }

    /// Set the maximum buffered response body size in bytes.
    ///
    /// Responses larger than this limit fail fast instead of being read fully
    /// into memory.
    pub fn max_body_size(mut self, value: usize) -> Self {
        self.0.max_body_size = value;
        self
    }

    /// Set the maximum retry count for failed requests.
    ///
    /// The client will automatically retry requests that fail due to
    /// timeouts, connection errors, or 5xx server responses.
    ///
    /// # Parameters
    /// - `value`: Maximum number of retry attempts. Set to 0 to disable retries.
    ///
    /// # Returns
    /// The builder for method chaining.
    ///
    /// # Example
    /// ```rust
    /// use scanclient::HttpConfig;
    ///
    /// let config = HttpConfig::builder()
    ///     .max_retries(5)
    ///     .build();
    /// assert_eq!(config.max_retries, 5);
    /// ```
    pub fn max_retries(mut self, value: u32) -> Self {
        self.0.max_retries = value;
        self
    }

    /// Set the base retry delay in milliseconds.
    ///
    /// The actual delay between retries uses exponential backoff,
    /// calculated as `retry_delay_ms * 2^attempt_number`.
    ///
    /// # Parameters
    /// - `value`: Base delay in milliseconds. Must be between 1 and 60,000.
    ///
    /// # Returns
    /// The builder for method chaining.
    ///
    /// # Example
    /// ```rust
    /// use scanclient::HttpConfig;
    ///
    /// let config = HttpConfig::builder()
    ///     .retry_delay_ms(500)
    ///     .build();
    /// assert_eq!(config.retry_delay_ms, 500);
    /// ```
    pub fn retry_delay_ms(mut self, value: u64) -> Self {
        self.0.retry_delay_ms = value;
        self
    }

    /// Set the maximum number of redirects to follow.
    ///
    /// When the server returns a redirect response (3xx status code),
    /// the client will automatically follow up to this many redirects.
    ///
    /// # Parameters
    /// - `value`: Maximum number of redirects. Set to 0 to disable redirect following.
    ///
    /// # Returns
    /// The builder for method chaining.
    ///
    /// # Example
    /// ```rust
    /// use scanclient::HttpConfig;
    ///
    /// let config = HttpConfig::builder()
    ///     .max_redirects(3)
    ///     .build();
    /// assert_eq!(config.max_redirects, 3);
    /// ```
    pub fn max_redirects(mut self, value: usize) -> Self {
        self.0.max_redirects = value;
        self
    }

    /// Set the proxy URL for all requests.
    ///
    /// Supported proxy schemes: `http`, `https`, `socks5`, `socks5h`.
    /// Authentication can be included in the URL: `http://user:pass@proxy:8080`.
    ///
    /// # Parameters
    /// - `value`: The proxy URL as a string.
    ///
    /// # Returns
    /// The builder for method chaining.
    ///
    /// # Example
    /// ```rust
    /// use scanclient::HttpConfig;
    ///
    /// let config = HttpConfig::builder()
    ///     .proxy("http://127.0.0.1:8080")
    ///     .build();
    /// assert_eq!(config.proxy, Some("http://127.0.0.1:8080".to_string()));
    /// ```
    pub fn proxy(mut self, value: impl Into<String>) -> Self {
        self.0.proxy = Some(value.into());
        self
    }

    /// Set the proxy username used for `Proxy-Authorization`.
    pub fn proxy_user(mut self, value: impl Into<String>) -> Self {
        self.0.proxy_user = Some(value.into());
        self
    }

    /// Set the proxy password used for `Proxy-Authorization`.
    pub fn proxy_password(mut self, value: impl Into<String>) -> Self {
        self.0.proxy_password = Some(value.into());
        self
    }

    /// Set the User-Agent header for all requests.
    ///
    /// The User-Agent identifies the client to the server. This value
    /// is sent with every request and cannot be empty.
    ///
    /// # Parameters
    /// - `value`: The User-Agent string.
    ///
    /// # Returns
    /// The builder for method chaining.
    ///
    /// # Example
    /// ```rust
    /// use scanclient::HttpConfig;
    ///
    /// let config = HttpConfig::builder()
    ///     .user_agent("MyScanner/1.0")
    ///     .build();
    /// assert_eq!(config.user_agent, "MyScanner/1.0".to_string());
    /// ```
    pub fn user_agent(mut self, value: impl Into<String>) -> Self {
        self.0.user_agent = value.into();
        self
    }

    /// Replace the custom headers map for all requests.
    ///
    /// These headers are sent with every request in addition to the
    /// User-Agent header. Header names must be valid HTTP header names.
    ///
    /// # Parameters
    /// - `value`: A `HashMap` of header names to header values.
    ///
    /// # Returns
    /// The builder for method chaining.
    ///
    /// # Example
    /// ```rust
    /// use std::collections::HashMap;
    /// use scanclient::HttpConfig;
    ///
    /// let mut headers = HashMap::new();
    /// headers.insert("X-API-Key".to_string(), "secret123".to_string());
    ///
    /// let config = HttpConfig::builder()
    ///     .custom_headers(headers)
    ///     .build();
    /// assert_eq!(config.custom_headers.get("X-API-Key"), Some(&"secret123".to_string()));
    /// ```
    pub fn custom_headers(mut self, value: HashMap<String, String>) -> Self {
        self.0.custom_headers = value;
        self
    }

    /// Set the rate limit for requests per second.
    ///
    /// When set, the client will limit outgoing requests to the specified
    /// rate using a token bucket algorithm. This is useful for avoiding
    /// overwhelming target servers.
    ///
    /// # Parameters
    /// - `value`: Maximum requests per second, or `None` to disable rate limiting.
    ///
    /// # Returns
    /// The builder for method chaining.
    ///
    /// # Example
    /// ```rust
    /// use scanclient::HttpConfig;
    ///
    /// let config = HttpConfig::builder()
    ///     .rate_limit_per_sec(Some(10))
    ///     .build();
    /// assert_eq!(config.rate_limit_per_sec, Some(10));
    /// ```
    pub fn rate_limit_per_sec(mut self, value: Option<u32>) -> Self {
        self.0.rate_limit_per_sec = value;
        self
    }

    /// Allow retries for non-idempotent HTTP methods.
    ///
    /// By default, only idempotent methods (GET, HEAD, OPTIONS, TRACE,
    /// PUT, DELETE) are retried. Enabling this allows POST, PATCH, and
    /// other non-idempotent methods to be retried as well.
    ///
    /// # Parameters
    /// - `value`: `true` to allow retries for all methods, `false` for idempotent only.
    ///
    /// # Returns
    /// The builder for method chaining.
    ///
    /// # Example
    /// ```rust
    /// use scanclient::HttpConfig;
    ///
    /// let config = HttpConfig::builder()
    ///     .retry_non_idempotent_methods(true)
    ///     .build();
    /// assert!(config.retry_non_idempotent_methods);
    /// ```
    pub fn retry_non_idempotent_methods(mut self, value: bool) -> Self {
        self.0.retry_non_idempotent_methods = value;
        self
    }

    /// Enable or disable TLS certificate verification.
    ///
    /// When enabled (default), the client verifies that the server's
    /// certificate is valid and trusted. Disabling this is insecure
    /// and should only be done for testing or when connecting to
    /// servers with self-signed certificates.
    ///
    /// This setting is mutually exclusive with `tls_accept_invalid_certs`
    /// and `tls_accept_invalid_hostnames`.
    ///
    /// # Parameters
    /// - `value`: `true` to enable verification, `false` to disable.
    ///
    /// # Returns
    /// The builder for method chaining.
    ///
    /// # Example
    /// ```rust
    /// use scanclient::HttpConfig;
    ///
    /// let config = HttpConfig::builder()
    ///     .tls_verify(false)
    ///     .tls_accept_invalid_certs(true)
    ///     .build();
    /// assert!(!config.tls_verify);
    /// ```
    pub fn tls_verify(mut self, value: bool) -> Self {
        self.0.tls_verify = value;
        self
    }

    /// Allow connections to servers with invalid TLS certificates.
    ///
    /// When enabled, the client accepts any TLS certificate,
    /// including expired, self-signed, or otherwise invalid certs.
    /// This is insecure and should only be used for testing.
    ///
    /// Must be used together with `tls_verify(false)`.
    ///
    /// # Parameters
    /// - `value`: `true` to accept invalid certificates.
    ///
    /// # Returns
    /// The builder for method chaining.
    ///
    /// # Example
    /// ```rust
    /// use scanclient::HttpConfig;
    ///
    /// let config = HttpConfig::builder()
    ///     .tls_verify(false)
    ///     .tls_accept_invalid_certs(true)
    ///     .build();
    /// assert!(config.tls_accept_invalid_certs);
    /// ```
    pub fn tls_accept_invalid_certs(mut self, value: bool) -> Self {
        self.0.tls_accept_invalid_certs = value;
        self
    }

    /// Allow connections where the TLS certificate hostname doesn't match.
    ///
    /// When enabled, the client accepts certificates even if the hostname
    /// in the certificate doesn't match the requested URL. This is insecure
    /// and should only be used for testing.
    ///
    /// Must be used together with `tls_verify(false)`.
    ///
    /// # Parameters
    /// - `value`: `true` to accept hostname mismatches.
    ///
    /// # Returns
    /// The builder for method chaining.
    ///
    /// # Example
    /// ```rust
    /// use scanclient::HttpConfig;
    ///
    /// let config = HttpConfig::builder()
    ///     .tls_verify(false)
    ///     .tls_accept_invalid_hostnames(true)
    ///     .build();
    /// assert!(config.tls_accept_invalid_hostnames);
    /// ```
    pub fn tls_accept_invalid_hostnames(mut self, value: bool) -> Self {
        self.0.tls_accept_invalid_hostnames = value;
        self
    }

    /// Set the connection establishment timeout in seconds.
    ///
    /// This timeout applies only to establishing the TCP connection,
    /// not the entire request. It is typically shorter than the
    /// overall request timeout.
    ///
    /// # Parameters
    /// - `value`: Connection timeout in seconds. Must be between 1 and 86,400.
    ///
    /// # Returns
    /// The builder for method chaining.
    ///
    /// # Example
    /// ```rust
    /// use scanclient::HttpConfig;
    ///
    /// let config = HttpConfig::builder()
    ///     .connect_timeout_secs(5)
    ///     .build();
    /// assert_eq!(config.connect_timeout_secs, 5);
    /// ```
    pub fn connect_timeout_secs(mut self, value: u64) -> Self {
        self.0.connect_timeout_secs = value;
        self
    }

    /// Finish building the configuration.
    ///
    /// Consumes the builder and returns a fully configured `HttpConfig`
    /// that can be used to create a `ScanClient`.
    ///
    /// # Returns
    /// A new `HttpConfig` instance with all the configured settings.
    ///
    /// # Example
    /// ```rust
    /// use scanclient::HttpConfig;
    ///
    /// let config = HttpConfig::builder()
    ///     .timeout_secs(30)
    ///     .max_retries(5)
    ///     .user_agent("MyScanner/1.0")
    ///     .build();
    /// ```
    pub fn build(self) -> HttpConfig {
        self.0
    }
}
