mod profile;
mod admin;
mod support;
mod email;
mod promo;
mod purchase;
mod whitelist;
mod merge;
pub use profile::*;
pub use admin::*;
pub use support::*;
pub use email::*;
pub use promo::*;
pub use purchase::*;
pub use whitelist::*;
pub use merge::*;
pub use rust_shared::api::{
    upsert_tg_user,
    get_profile_with_optional_headers
};

use color_eyre::{eyre::Report, Result};
use reqwest;
use serde::Serialize;
use colored::*;
use rust_shared::repository::api::prelude::*;
use rust_shared::Language;
use std::mem;

pub mod prelude {
    pub use super::*;
    pub use rust_shared::repository::api::prelude::*;
}

/// Test utility function to replace user ID for debugging purposes.
/// Only works in debug builds and when TARGET_ID environment variable is set.
#[cfg(debug_assertions)]
pub fn get_test_user_id(original_tg_id: i64) -> String {
    log::info!("get_test_user_id: {}", original_tg_id.to_string().green());
    match std::env::var("TARGET_ID") {
        Ok(target_id) => {
            log::debug!("🔧 TEST MODE: Replacing user ID {} with TARGET_ID {}", original_tg_id, target_id);
            target_id
        }
        Err(_) => original_tg_id.to_string(),
    }
}

/// Production version (no-op) - returns original ID unchanged
#[cfg(not(debug_assertions))]
pub fn get_test_user_id(original_tg_id: i64) -> String {
    original_tg_id.to_string()
}

pub async fn change_language(tg_id: i64, language: Language) -> Result<()> {
    let tg_id = get_test_user_id(tg_id);
    let url = format!("{host}/rpc/change_language", host=&*BACKEND_API_URL);
    log::trace!("fetching {url}", url = url.red());

    #[derive(Serialize)]
    struct ChangeLanguage {
        code: Language,
    }

    let response = reqwest::Client::new()
        .post(&url)
        .header(
            "Authorization",
            get_auth_token().await?,
        )
        .header("X-Tg-Id", tg_id.to_string())
        .json(&ChangeLanguage { code: language })
        .send()
        .await?;

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

    Ok(())
}

pub async fn tg_user_exists(tg_id: i64) -> Result<bool> {
    let user_id = get_test_user_id(tg_id);
    let url = format!("{host}/rpc/check_user_exists", host=&*BACKEND_API_URL);
    log::trace!("fetching {url}", url = url.red());

    #[derive(serde::Serialize)]
    struct CheckUserRequest {
        user_id: String,
    }

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

    match response.status() {
        reqwest::StatusCode::OK => {
            let exists = response.text().await?;
            Ok(exists == "true")
        }
        _ => Err(Report::msg(format!(
            "Failed to check if user exists: {}",
            prettify_json_or_text(&response.text().await?)
        ))),
    }
}

pub use rust_shared::repository::api::{
    get_countries_for_pool,
    get_pool_stats,
};

pub use rust_shared::repository::api::{
    get_active_payment_systems,
    get_tariff_price,
};

pub async fn get_reseller_token(tg_id: i64) -> Result<String> {
    let tg_id = get_test_user_id(tg_id);
    let url = format!("{host}/rpc/reseller_token", host=&*BACKEND_API_URL);
    log::trace!("fetching {url}", url = url.red());

    let response = reqwest::Client::new()
        .post(&url)
        .header(
            "Authorization",
            get_auth_token().await?,
        )
        .header("X-Tg-Id", tg_id.to_string())
        .header("Accept", "text/plain")
        .send()
        .await?;

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

    log::debug!("reseller token response is `{}`", response_body);

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

    Ok(response_body)
}

#[derive(serde::Deserialize, serde::Serialize, Debug, Default, Clone)]
pub struct ResellerProfile {
    pub id: i32,
    pub user_id: String,
    pub api_key: String,
    pub traffic_amount: f64,
    pub traffic_amount_gb: i64,
    pub traffic_formatted: String,
    pub created_at: String,
    pub updated_at: String,
    pub subusers_count: i64,
}

pub async fn get_reseller_profile(tg_id: i64) -> Result<ResellerProfile> {
    let tg_id = get_test_user_id(tg_id);
    Ok(get_reseller_profile_by_user_id(tg_id.to_string()).await?)
}

pub async fn get_reseller_profile_by_user_id(user_id: String) -> Result<ResellerProfile> {
    let url = format!("{host}/reseller_profile", host=&*BACKEND_API_URL);
    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.to_string())
        .header("Accept", "application/json")
        .send()
        .await?;

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

    let profiles: Vec<ResellerProfile> = response.json().await?;

    if profiles.len() > 0 {
        Ok(profiles[0].clone())
    } else {
        Err(Report::msg("No profile with such user id"))
    }
}

pub async fn create_tgcode(tg_id: i64) -> Result<i32> {
    let tg_id = get_test_user_id(tg_id);
    let url = format!("{host}/rpc/create_tgcode", host=&*BACKEND_API_URL);
    log::trace!("fetching {url}", url = url.red());

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

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

    log::debug!("create tgcode response is `{}`", response_body);

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

    let code = response_body.trim().parse::<i32>()?;
    Ok(code)
}

pub use rust_shared::repository::api::{
    get_banners,
    get_banner_blob,
};

pub async fn generate_proxies(
    tg_id: i64,
    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 user_id = get_test_user_id(tg_id);
    rust_shared::repository::api::generate_proxies(
        user_id, pool, quantity, countries, states, cities, asns, excluded_asns,
        zip_codes, protocol, proxy_type, anonymity, format, region
    ).await
}

pub use rust_shared::repository::api::get_states;

pub use rust_shared::repository::api::get_cities;

pub use rust_shared::repository::api::get_asns;

pub use rust_shared::repository::api::get_zip_codes;

pub async fn get_user_statistics(tg_id: i64, pool: PoolType, period: Period) -> Result<Option<UserStatistics>> {
    let user_id = get_test_user_id(tg_id);
    rust_shared::repository::api::get_user_statistics(user_id, pool, period).await
}

pub async fn get_default_proxy_settings(tg_id: i64, pool: PoolType) -> Result<DefaultProxySettings> {
    let user_id = get_test_user_id(tg_id);
    rust_shared::repository::api::get_default_proxy_settings(user_id, pool).await
}

pub async fn save_default_proxy_settings(
    tg_id: i64,
    pool: PoolType,
    settings: DefaultProxySettings,
) -> Result<DefaultProxySettings> {
    let user_id = get_test_user_id(tg_id);
    rust_shared::repository::api::save_default_proxy_settings(user_id, pool, settings).await
}
