use crate::repository::api::prelude::*;

/// Get countries for a specific pool
pub async fn get_countries_for_pool(pool: PoolType) -> Result<Vec<Country>> {
    let url = format!("{host}/rpc/get_dataimpulse_proxy_countries?pool={pool}",
                      host=&*BACKEND_API_URL,
                      pool=pool);
    log::trace!("fetching {url}", url = url.red());

    let response = reqwest::Client::new()
        .get(&url)
        .header(
            "Authorization",
            get_auth_token().await?,
        )
        .send()
        .await?;

    if !response.status().is_success() {
        return Err(Report::msg(format!(
            "Failed to get pool stats: {}",
            prettify_json_or_text(&response.text().await?)
        )));
    }

    let stats = response.json::<Vec<Country>>().await?;
    Ok(stats)
}

/// Get pool statistics (alias for get_countries_for_pool)
pub async fn get_pool_stats(pool: PoolType) -> Result<Vec<Country>> {
    get_countries_for_pool(pool).await
}

/// Get states for specific countries and pool
pub async fn get_states(pool: PoolType, countries: Vec<Country>) -> Result<Vec<State>> {
    let url = format!("{host}/rpc/get_dataimpulse_proxy_states",
                      host=&*BACKEND_API_URL);
    log::trace!("fetching {url}", url = url.red());

    #[derive(serde::Serialize)]
    struct StateRequest {
        pool: String,
        countries: Vec<String>,
    }

    let request = StateRequest {
        pool: pool.to_string(),
        countries: Location::extract_codes(&countries),
    };

    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", get_auth_token().await?)
        .header("Content-Type", "application/json")
        .json(&request)
        .send()
        .await?;

    if !response.status().is_success() {
        return Err(Report::msg(format!(
            "Failed to get states: {}",
            prettify_json_or_text(&response.text().await?)
        )));
    }

    let states = response.json::<Vec<State>>().await?;
    Ok(states)
}

/// Get cities for specific countries and states
pub async fn get_cities(pool: PoolType, countries: Vec<Country>, states: Vec<State>) -> Result<Vec<City>> {
    let url = format!("{host}/rpc/get_dataimpulse_proxy_cities",
                      host=&*BACKEND_API_URL);
    log::trace!("fetching {url}", url = url.red());

    #[derive(serde::Serialize)]
    struct CityRequest {
        pool: String,
        countries: Vec<String>,
        states: Vec<String>,
    }

    let request = CityRequest {
        pool: pool.to_string(),
        countries: Location::extract_codes(&countries),
        states: Location::extract_codes(&states),
    };

    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", get_auth_token().await?)
        .header("Content-Type", "application/json")
        .json(&request)
        .send()
        .await?;

    if !response.status().is_success() {
        return Err(Report::msg(format!(
            "Failed to get cities: {}",
            prettify_json_or_text(&response.text().await?)
        )));
    }

    let cities = response.json::<Vec<City>>().await?;
    Ok(cities)
}

/// Get ASNs for specific locations
pub async fn get_asns(pool: PoolType, countries: Vec<Country>, states: Vec<State>, cities: Vec<City>) -> Result<Vec<ASN>> {
    let url = format!("{host}/rpc/get_dataimpulse_proxy_asns",
                      host=&*BACKEND_API_URL);
    log::trace!("fetching {url}", url = url.red());

    #[derive(serde::Serialize)]
    struct ASNRequest {
        pool: String,
        countries: Vec<String>,
        states: Vec<String>,
        cities: Vec<String>,
    }

    let request = ASNRequest {
        pool: pool.to_string(),
        countries: Location::extract_codes(&countries),
        states: Location::extract_codes(&states),
        cities: Location::extract_codes(&cities),
    };

    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", get_auth_token().await?)
        .header("Content-Type", "application/json")
        .json(&request)
        .send()
        .await?;

    if !response.status().is_success() {
        return Err(Report::msg(format!(
            "Failed to get ASNs: {}",
            prettify_json_or_text(&response.text().await?)
        )));
    }

    let asns = response.json::<Vec<ASN>>().await?;
    Ok(asns)
}

/// Get ZIP codes for specific locations
pub async fn get_zip_codes(pool: PoolType, countries: Vec<Country>, states: Vec<State>, asns: Vec<ASN>) -> Result<Vec<ZipCode>> {
    #[derive(Serialize)]
    struct ZipCodeRequest {
        pool: String,
        countries: Vec<String>,
        states: Vec<String>,
        asns: Vec<String>,
        order_by: String,
    }

    let url = format!("{host}/rpc/get_dataimpulse_proxy_zipcodes", host=&*BACKEND_API_URL);
    log::trace!("fetching {url}", url = url.red());

    let request = ZipCodeRequest {
        pool: pool.to_string(),
        countries: Location::extract_codes(&countries),
        states: Location::extract_codes(&states),
        asns: Location::extract_codes(&asns),
        order_by: "name,asc".to_string(),
    };

    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", get_auth_token().await?)
        .header("Content-Type", "application/json")
        .json(&request)
        .send()
        .await?;

    if !response.status().is_success() {
        return Err(Report::msg(format!(
            "Failed to get zip codes: {}",
            prettify_json_or_text(&response.text().await?)
        )));
    }

    let zip_codes = response.json::<Vec<ZipCode>>().await?;
    Ok(zip_codes)
}

/// Generate proxies with specified parameters
pub async fn generate_proxies(
    user_id: String,
    pool: PoolType,
    quantity: u32,
    countries: Vec<Country>,
    states: Vec<State>,
    cities: Vec<City>,
    asns: Vec<ASN>,
    excluded_asns: Vec<ASN>,
    zip_codes: Vec<ZipCode>,
    protocol: ProxyProtocol,
    proxy_type: ProxyType,
    anonymity: bool,
    format: ProxyFormat,
    region: ProxyRegion,
) -> Result<String> {
    let url = format!("{host}/rpc/get_proxy", host=&*BACKEND_API_URL);
    log::trace!("fetching {url}", url = url.red());

    #[derive(serde::Serialize)]
    struct ProxyRequest {
        anonymous: String,
        #[serde(rename = "type")]
        proxy_type: String,
        protocol: ProxyProtocol,
        format: ProxyFormat,
        quantity: u32,
        sessionttl: Option<u32>,
        pool: String,
        countries: Vec<String>,
        cities: Vec<String>,
        states: Vec<String>,
        excluded_asns: Option<Vec<String>>,
        included_asns: Vec<String>,
        zipcodes: Vec<String>,
        region: ProxyRegion,
    }

    let excluded_asns_codes: Option<Vec<String>> = if excluded_asns.is_empty() {
        None
    } else {
        Some(Location::extract_codes(&excluded_asns))
    };

    let proxy_type_text = proxy_type.to_string();

    let request = ProxyRequest {
        anonymous: anonymity.to_string(),
        proxy_type: proxy_type_text,
        protocol,
        format,
        quantity,
        sessionttl: match proxy_type {
            ProxyType::Sticky(interval) => interval,
            _ => None,
        },
        pool: pool.to_string(),
        countries: Location::extract_codes(&countries),
        cities: Location::extract_codes(&cities),
        states: Location::extract_codes(&states),
        excluded_asns: excluded_asns_codes,
        included_asns: Location::extract_codes(&asns),
        zipcodes: Location::extract_codes(&zip_codes),
        region,
    };

    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", get_auth_token().await?)
        .header("Content-Type", "application/json")
        .header("Accept", "text/plain")
        .header("X-Tg-Id", user_id)
        .json(&request)
        .send()
        .await?;

    let status = response.status();
    let response_body = response.text().await?;

    if !status.is_success() {
        return Err(Report::msg(format!(
            "Failed to generate proxies: {}",
            prettify_json_or_text(&response_body)
        )));
    }

    Ok(response_body)
}

/// Get user statistics for a specific pool and period
pub async fn get_user_statistics(user_id: String, pool: PoolType, period: Period) -> Result<Option<UserStatistics>> {
    let url = format!("{host}/rpc/legacy_get_statistic?period={period}&pool={pool}",
                      host=&*BACKEND_API_URL,
                      period=period.as_str(),
                      pool=pool);
    log::trace!("fetching {url}", url = url.red());

    let response = reqwest::Client::new()
        .get(&url)
        .header("Authorization", get_auth_token().await?)
        .header("X-Tg-Id", user_id)
        .send()
        .await?;

    if !response.status().is_success() {
        return Err(Report::msg(format!(
            "Failed to get user statistics: {}",
            prettify_json_or_text(&response.text().await?)
        )));
    }

    let statistics = match response.json::<UserStatistics>().await {
        Ok(statistics) => Some(statistics),
        Err(e) => {
            log::error!("Failed to parse user statistics: {}", e);
            None
        }
    };
    Ok(statistics)
}

/// Get current user statistics for a specific pool and period (no user_id required)
pub async fn get_current_user_statistics(pool: PoolType, period: Period) -> Result<Option<UserStatistics>> {
    let url = format!("{host}/rpc/legacy_get_statistic?period={period}&pool={pool}",
                      host=&*BACKEND_API_URL,
                      period=period.as_str(),
                      pool=pool);
    log::trace!("fetching {url}", url = url.red());

    let response = reqwest::Client::new()
        .get(&url)
        .header("Authorization", get_auth_token().await?)
        .send()
        .await?;

    if !response.status().is_success() {
        return Err(Report::msg(format!(
            "Failed to get current user statistics: {}",
            prettify_json_or_text(&response.text().await?)
        )));
    }

    let response_text = response.text().await?;
    log::trace!("Statistics API response: {}", response_text);
    
    if response_text.trim() == "[]" {
        log::info!("API returned empty array, creating empty statistics");
        let empty_stats = UserStatistics {
            usage: vec![],
            total: TotalUsage {
                traffic: 0,
                extra_traffic: 0,
                request: 0,
            },
        };
        return Ok(Some(empty_stats));
    }
    
    let statistics = match serde_json::from_str::<UserStatistics>(&response_text) {
        Ok(statistics) => Some(statistics),
        Err(e) => {
            log::error!("Failed to parse current user statistics: {}. Response was: {}", e, response_text);
            None
        }
    };
    Ok(statistics)
}

/// Get default proxy settings for a pool
pub async fn get_default_proxy_settings(user_id: String, pool: PoolType) -> Result<DefaultProxySettings> {
    let url = format!("{host}/rpc/get_default_pool_parameters",
                      host=&*BACKEND_API_URL);
    log::trace!("fetching {url}", url = url.red());

    #[derive(serde::Serialize)]
    struct DefaultSettingsRequest {
        pool: String,
    }

    let request = DefaultSettingsRequest {
        pool: pool.to_string(),
    };

    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", get_auth_token().await?)
        .header("X-Tg-Id", user_id)
        .header("Content-Type", "application/json")
        .json(&request)
        .send()
        .await?;

    if !response.status().is_success() {
        log::warn!("Failed to get default settings, using defaults: {}", 
                   prettify_json_or_text(&response.text().await?));
        return Ok(DefaultProxySettings::default());
    }

    let settings = match response.json::<DefaultProxySettings>().await {
        Ok(settings) => settings,
        Err(e) => {
            log::warn!("Failed to parse default settings, using defaults: {}", e);
            DefaultProxySettings::default()
        }
    };
    
    Ok(settings)
}

/// Save default proxy settings for a pool
pub async fn save_default_proxy_settings(
    user_id: String,
    pool: PoolType,
    settings: DefaultProxySettings,
) -> Result<DefaultProxySettings> {
    let url = format!("{host}/rpc/set_default_pool_parameters",
                      host=&*BACKEND_API_URL);
    log::trace!("posting to {url}", url = url.red());

    #[derive(serde::Serialize)]
    struct SaveSettingsRequest {
        pool: String,
        countries: Vec<String>,
        cities: Vec<String>,
        states: Vec<String>,
        zipcodes: Vec<String>,
        asns: Vec<String>,
        exclude_countries: Vec<String>,
        exclude_asn: Vec<String>,
        anonymous_filter: bool,
        rotation_interval: Option<u32>,
    }

    let request = SaveSettingsRequest {
        pool: pool.to_string(),
        countries: settings.countries,
        cities: settings.cities,
        states: settings.states,
        zipcodes: settings.zipcodes,
        asns: settings.asns,
        exclude_countries: settings.exclude_countries,
        exclude_asn: settings.exclude_asn,
        anonymous_filter: settings.anonymous_filter,
        rotation_interval: settings.rotation_interval,
    };

    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", get_auth_token().await?)
        .header("X-Tg-Id", user_id)
        .header("Content-Type", "application/json")
        .json(&request)
        .send()
        .await?;

    if !response.status().is_success() {
        return Err(Report::msg(format!(
            "Failed to save default settings: {}",
            prettify_json_or_text(&response.text().await?)
        )));
    }

    if response.status().is_success() {
        let settings = response.json::<DefaultProxySettings>().await?;
        Ok(settings)
    } else {
        Err(Report::msg(format!(
            "Failed to save default settings: {}",
            prettify_json_or_text(&response.text().await?)
        )))
    }
}
