use crate::doqium_auth_client::apis::configuration::Configuration as AuthConfiguration;
use crate::doqium_data_client::apis::configuration::Configuration as DataConfiguration;
use anyhow::{Context, Result};
use std::sync::Arc;

use crate::config::EnvironmentConfig;

pub mod auth;
pub mod data;

pub struct DoqiumClient {
    auth_config: Arc<AuthConfiguration>,
    data_config: Arc<DataConfiguration>,
}

impl DoqiumClient {
    pub fn new(token: &str) -> Result<Self> {
        let env_config = EnvironmentConfig::from_env();
        let auth_config = create_auth_config(token, &env_config, false, None)?;
        let data_config = create_data_config(token, &env_config, false, None)?;

        Ok(Self {
            auth_config: Arc::new(auth_config),
            data_config: Arc::new(data_config),
        })
    }

    pub fn new_admin(token: &str) -> Result<Self> {
        let env_config = EnvironmentConfig::from_env();

        let basic_auth = if env_config.admin_requires_basic_auth() {
            crate::config::get_admin_basic_auth()?
        } else {
            None
        };

        let auth_config = create_auth_config(token, &env_config, true, basic_auth.as_ref())?;
        let data_config = create_data_config(token, &env_config, true, basic_auth.as_ref())?;

        Ok(Self {
            auth_config: Arc::new(auth_config),
            data_config: Arc::new(data_config),
        })
    }

    pub fn auth_config(&self) -> &AuthConfiguration {
        &self.auth_config
    }

    pub fn data_config(&self) -> &DataConfiguration {
        &self.data_config
    }
}

fn create_auth_config(
    token: &str,
    env_config: &EnvironmentConfig,
    use_admin: bool,
    basic_auth: Option<&(String, String)>,
) -> Result<AuthConfiguration> {
    let mut config = AuthConfiguration::new();
    let base_url = if use_admin {
        env_config.admin_auth_base_url()
    } else {
        env_config.auth_base_url()
    };
    config.base_path = base_url.clone();

    let jar = std::sync::Arc::new(reqwest::cookie::Jar::default());
    let url = base_url.parse::<reqwest::Url>()?;
    jar.add_cookie_str(&format!("jwtToken={}", token), &url);

    let mut client_builder = reqwest::Client::builder().cookie_provider(jar);

    if env_config.should_accept_invalid_certs() {
        client_builder = client_builder.danger_accept_invalid_certs(true);
    }

    // Add basic authentication if provided
    if let Some((username, password)) = basic_auth {
        config.basic_auth = Some((username.clone(), Some(password.clone())));
    }

    let client = client_builder
        .build()
        .context("Failed to create HTTP client")?;

    config.client = client;

    Ok(config)
}

fn create_data_config(
    token: &str,
    env_config: &EnvironmentConfig,
    use_admin: bool,
    basic_auth: Option<&(String, String)>,
) -> Result<DataConfiguration> {
    let mut config = DataConfiguration::new();
    let base_url = if use_admin {
        env_config.admin_data_base_url()
    } else {
        env_config.data_base_url()
    };
    config.base_path = base_url.clone();

    let jar = std::sync::Arc::new(reqwest::cookie::Jar::default());
    let url = base_url.parse::<reqwest::Url>()?;
    jar.add_cookie_str(&format!("jwtToken={}", token), &url);

    let mut client_builder = reqwest::Client::builder().cookie_provider(jar);

    if env_config.should_accept_invalid_certs() {
        client_builder = client_builder.danger_accept_invalid_certs(true);
    }

    // Add basic authentication if provided
    if let Some((username, password)) = basic_auth {
        config.basic_auth = Some((username.clone(), Some(password.clone())));
    }

    let client = client_builder
        .build()
        .context("Failed to create HTTP client")?;

    config.client = client;

    Ok(config)
}
