//! Factory for constructing a rate-limited [`QBittorrentClient`].

use std::sync::Arc;
use std::time::Duration;

use crate::QBittorrentClient;
use crate::QBittorrentClientOptions;
use reqwest::cookie::Jar;
use reqwest::header::{HeaderMap, HeaderValue};
use reqwest::{ClientBuilder, header};
use tokio::sync::Mutex;

/// Default number of requests allowed per duration.
const DEFAULT_RATE_COUNT: usize = 10;
/// Default duration in seconds before rate limit is reset.
const DEFAULT_RATE_DURATION: usize = 10;

/// Create a [`QBittorrentClient`]
pub struct QBittorrentClientFactory {
    /// Configuration for the client to create.
    pub options: QBittorrentClientOptions,
}

impl QBittorrentClientFactory {
    /// Create a new [`QBittorrentClient`] with rate limiting applied.
    #[must_use]
    #[expect(
        clippy::as_conversions,
        reason = "usize to u64 is safe for small config values"
    )]
    pub fn create(&self) -> QBittorrentClient {
        let rate_count = self.options.rate_limit_count.unwrap_or(DEFAULT_RATE_COUNT) as u64;
        let rate_duration = self
            .options
            .rate_limit_duration
            .unwrap_or(DEFAULT_RATE_DURATION) as u64;
        let rate_duration = Duration::from_secs(rate_duration);
        let cookies = Arc::new(Jar::default());
        let client = ClientBuilder::new()
            .default_headers(self.get_headers())
            .cookie_provider(cookies.clone())
            .build()
            .expect("Client builder should not fail");
        let client = tower::ServiceBuilder::new()
            .rate_limit(rate_count, rate_duration)
            .service(client);
        QBittorrentClient {
            host: self.options.host.clone(),
            cookies,
            username: self.options.username.clone(),
            password: self.options.password.clone(),
            client: Mutex::new(client),
        }
    }

    fn get_headers(&self) -> HeaderMap {
        let mut headers = HeaderMap::new();
        if let Some(user_agent) = self.options.user_agent.clone() {
            headers.insert(
                header::USER_AGENT,
                HeaderValue::try_from(user_agent).expect("user agent should not fail"),
            );
        }
        headers.insert(
            header::CONTENT_TYPE,
            HeaderValue::from_static("application/json"),
        );
        headers
    }
}
