use regex::Regex;
use reqwest::Client;
use serde_json::Value;

/// Sends a GET request to the qBittorrent API and parses the JSON response.
///
/// # Arguments
///
/// * `client` - The HTTP client used to perform the request
/// * `url` - The full API endpoint URL
///
/// # Returns
///
/// * `Some(Value)` - Parsed JSON response if the request succeeds
/// * `None` - If the request fails or the response cannot be parsed
pub async fn qb_get(client: &Client, url: String) -> Option<Value> {
    match client.get(&url).send().await {
        Ok(r) => r.json().await.ok(),
        Err(e) => {
            log::warn!("qB GET error: {}", e);
            None
        }
    }
}
/// Get case agnostic environment variables.
///
/// # Arguments
///
/// * `key` - Environment variable's key.
/// * `default` - Fallback value is env var is not available.
///
/// # Returns
///
/// Returns the resolved env var or a default string.
pub fn get_env_var(key: &str, default: Option<&str>) -> String {
    if let Ok(upper) = std::env::var(key.to_uppercase()) {
        return upper;
    }
    if let Ok(lower) = std::env::var(key.to_lowercase()) {
        return lower;
    }
    default.unwrap_or_default().to_string()
}

/// Load dotenv file using the `env_file` CLI arg or an environment variable.
///
/// # Arguments
///
/// * `env_file` - Takes the `env_file` passed through CLI argument.
pub fn load_env_file(mut env_file: String) {
    if env_file.is_empty() {
        env_file = get_env_var("env_file", Some(".env"));
    }
    let env_file_path = std::env::current_dir().unwrap_or_default().join(env_file);
    let _ = dotenv::from_path(env_file_path.as_path());
}

/// Verifies the strength of a secret string.
///
/// # Description
/// A secret is considered strong if it satisfies all the following:
///
///  - Has at least "min_length" characters
///  - Contains at least 1 digit
///  - Contains at least 1 symbol (non-alphanumeric, non-whitespace)
///  - Contains at least 1 uppercase letter
///  - Contains at least 1 lowercase letter
///
/// # Arguments
///
/// * `value` - The secret string to validate.
/// * `min_length` - Minimum required length of the secret.
///
/// # Returns
///
/// * `Ok(())` if the secret meets all strength requirements.
/// * `Err(String)` with a message describing the first failed condition.
pub fn complexity_checker(value: &str, min_length: usize) -> Result<(), String> {
    if value.trim().is_empty() {
        return Err("Value cannot be empty".to_string());
    }

    if value.len() < min_length {
        return Err(format!(
            "Minimum length is {}, received {}",
            min_length,
            value.len()
        ));
    }

    let digit_re = Regex::new(r"\d").unwrap();
    let upper_re = Regex::new(r"[A-Z]").unwrap();
    let lower_re = Regex::new(r"[a-z]").unwrap();
    let symbol_re = Regex::new(r#"[^\w\s]"#).unwrap();

    if !digit_re.is_match(value) {
        return Err("Value must include an integer".to_string());
    }

    if !upper_re.is_match(value) {
        return Err("Value must include at least one uppercase letter".to_string());
    }

    if !lower_re.is_match(value) {
        return Err("Value must include at least one lowercase letter".to_string());
    }

    if !symbol_re.is_match(value) {
        return Err("Value must contain at least one special character".to_string());
    }

    Ok(())
}
