use serde::{Serialize, Deserialize};
use rust_shared::repository::model::{Profile, UserStatistics, PoolType, Period, TotalUsage, Country, State, City, ASN, ZipCode, PaymentSystem, PurchaseRequest, ReferralPaymentRequest, DefaultProxySettings};
use rust_shared::repository::api::prettify_json_or_text;
use rust_shared::i18n::Language;
use crate::config::get_backend_url;
use base64::Engine as _;

#[derive(Debug)]
pub enum AuthError {
    InvalidCredentials,
    NetworkError(String),
    ProfileFetchError,
    HttpError { status: u16, message: String },
    Other(String),
}

#[derive(Debug)]
pub enum WhitelistError {
    NetworkError(String),
    IpAlreadyBound,
    InvalidIpFormat,
    ServerError(String),
}

impl std::fmt::Display for AuthError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            AuthError::InvalidCredentials => write!(f, "Invalid username or password"),
            AuthError::NetworkError(msg) => write!(f, "Network error: {}", msg),
            AuthError::ProfileFetchError => write!(f, "Failed to fetch user profile"),
            AuthError::HttpError { status, message } => write!(f, "HTTP {}: {}", status, message),
            AuthError::Other(msg) => write!(f, "{}", msg),
        }
    }
}

impl std::fmt::Display for WhitelistError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            WhitelistError::NetworkError(msg) => write!(f, "Network error: {}", msg),
            WhitelistError::IpAlreadyBound => write!(f, "This IP address is already bound to another user"),
            WhitelistError::InvalidIpFormat => write!(f, "Invalid IP address format"),
            WhitelistError::ServerError(msg) => write!(f, "Server error: {}", msg),
        }
    }
}

impl std::error::Error for WhitelistError {}

#[derive(Serialize)]
struct SigninRequest {
    username: String,
    password: String,
}

#[derive(Serialize)]
struct TelegramCodeRequest {
    p_code: String,
}

/// Common function to fetch user profile after authentication
pub async fn fetch_user_profile(token: &str) -> Result<Profile, AuthError> {
    let backend_url = get_backend_url();
    let profile_url = format!("{}/legacy_profile", backend_url);
    
    let client = reqwest::Client::new();
    let profile_response = client
        .get(&profile_url)
        .header("Authorization", format!("Bearer {}", token))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !profile_response.status().is_success() {
        let error_text = profile_response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Profile fetch failed: {}", prettify_json_or_text(&error_text));
        return Err(AuthError::ProfileFetchError);
    }
    
    let profiles: Vec<Profile> = profile_response.json().await
        .map_err(|e| AuthError::Other(format!("Failed to parse profile: {}", e)))?;
    
    if profiles.is_empty() {
        return Err(AuthError::ProfileFetchError);
    }
    
    let profile = profiles.into_iter().next().unwrap();
    log::info!("Profile fetched successfully for user: {}", profile.id);
    
    Ok(profile)
}

/// Sign in with username/email and password
pub async fn signin(username: String, password: String) -> Result<(String, Profile), AuthError> {
    let backend_url = get_backend_url();
    let signin_url = format!("{}/rpc/signin", backend_url);
    
    log::info!("Attempting signin for user: {}", username);
    
    let client = reqwest::Client::new();
    let signin_response = client
        .post(&signin_url)
        .json(&SigninRequest { username, password })
        .header("Accept", "text/plain")
        .header("Content-Type", "application/json")
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    let status = signin_response.status();
    if !status.is_success() {
        let error_text = signin_response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Signin failed: {}", prettify_json_or_text(&error_text));
        return Err(AuthError::HttpError { 
            status: status.as_u16(), 
            message: error_text 
        });
    }
    
    let token = signin_response.text().await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if token.trim().is_empty() {
        return Err(AuthError::InvalidCredentials);
    }
    
    log::info!("Signin successful, fetching profile...");
    
    let profile = fetch_user_profile(&token).await?;
    
    Ok((token, profile))
}

/// Sign up with username, email and password  
pub async fn signup(username: String, email: String, password: String) -> Result<String, AuthError> {
    let backend_url = get_backend_url();
    let signup_url = format!("{}/rpc/signup", backend_url);
    
    log::info!("Attempting signup for user: {}", username);
    
    #[derive(Serialize)]
    struct SignupRequest {
        username: String,
        email: String,
        password: String,
    }
    
    let client = reqwest::Client::new();
    let signup_response = client
        .post(&signup_url)
        .json(&SignupRequest { username, email, password })
        .header("Accept", "text/plain")
        .header("Content-Type", "application/json")
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    let status = signup_response.status();
    if !status.is_success() {
        let error_text = signup_response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Signup failed: {}", prettify_json_or_text(&error_text));
        return Err(AuthError::HttpError { 
            status: status.as_u16(), 
            message: error_text 
        });
    }
    
    let token = signup_response.text().await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if token.trim().is_empty() {
        return Err(AuthError::Other("Registration failed - no token returned".to_string()));
    }
    
    log::info!("Signup successful, email verification required");
    
    Ok(token)
}

/// Sign in with Telegram code
pub async fn telegram_code_signin(code: String) -> Result<(String, Profile), AuthError> {
    let backend_url = get_backend_url();
    let telegram_signin_url = format!("{}/rpc/telegram_code_signin", backend_url);
    
    log::info!("Attempting Telegram signin with code");
    
    let client = reqwest::Client::new();
    let signin_response = client
        .post(&telegram_signin_url)
        .json(&TelegramCodeRequest { p_code: code })
        .header("Accept", "text/plain")
        .header("Content-Type", "application/json")
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    match signin_response.status().as_u16() {
        422 => return Err(AuthError::Other("Invalid code".to_string())),
        404 => return Err(AuthError::Other("Code not found".to_string())),
        400 => return Err(AuthError::Other("Invalid request".to_string())),
        429 => return Err(AuthError::Other("Too many attempts. Please try later".to_string())),
        _ => {}
    }
    
    if !signin_response.status().is_success() {
        let error_text = signin_response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Telegram signin failed: {}", prettify_json_or_text(&error_text));
        return Err(AuthError::Other("Authorization error".to_string()));
    }
    
    let token = signin_response.text().await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if token.trim().is_empty() {
        return Err(AuthError::Other("Failed to get authorization token".to_string()));
    }
    
    log::info!("Telegram signin successful, fetching profile...");
    
    let profile = fetch_user_profile(&token).await?;
    
    Ok((token, profile))
}

/// Confirm email with confirmation token
pub async fn confirm_email(confirmation_token: String) -> Result<(), AuthError> {
    let backend_url = get_backend_url();
    let confirm_email_url = format!("{}/rpc/confirm_email", backend_url);
    
    log::info!("Attempting email confirmation with token");
    
    let client = reqwest::Client::new();
    let confirm_response = client
        .post(&confirm_email_url)
        .json(&serde_json::json!({
            "confirmation_token": confirmation_token
        }))
        .header("Content-Type", "application/json")
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !confirm_response.status().is_success() {
        let error_text = confirm_response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Email confirmation failed: {}", prettify_json_or_text(&error_text));
        
        // Parse specific error messages from the database function
        if error_text.contains("Invalid or already used confirmation token") {
            return Err(AuthError::Other("Invalid or already used confirmation token".to_string()));
        } else if error_text.contains("Confirmation token expired") {
            return Err(AuthError::Other("Confirmation token expired".to_string()));
        } else if error_text.contains("User not found") {
            return Err(AuthError::Other("User not found".to_string()));
        } else {
            return Err(AuthError::Other("Email confirmation failed".to_string()));
        }
    }
    
    log::info!("Email confirmation successful");
    
    Ok(())
}

/// Get user statistics for a specific pool and period
pub async fn get_user_statistics(pool: PoolType, period: Period) -> Result<Option<UserStatistics>, AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;
    
    let backend_url = get_backend_url();
    let stats_url = format!("{}/rpc/legacy_get_statistic?period={}&pool={}",
                          backend_url,
                          period.as_str(),
                          pool);
    
    let client = reqwest::Client::new();
    let response = client
        .get(&stats_url)
        .header("Authorization", format!("Bearer {}", token))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Failed to fetch user statistics: {}", error_text);
        return Err(AuthError::NetworkError(format!("Failed to fetch statistics: {}", error_text)));
    }
    
    let response_text = response.text().await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    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 user statistics: {}. Response was: {}", e, response_text);
            None
        }
    };
    Ok(statistics)
}

pub async fn get_whitelist_ips(pool: PoolType) -> Result<Vec<String>, AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;

    let backend_url = get_backend_url();
    let url = format!("{}/rpc/get_whitelist_ips", backend_url);

    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", token))
        .header("Content-Type", "application/json")
        .json(&serde_json::json!({ "pool": pool.as_str() }))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;

    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Failed to fetch whitelist IPs: {}", error_text);
        return Err(AuthError::NetworkError(format!("Failed to fetch whitelist: {}", error_text)));
    }

    let ips: Vec<String> = response.json().await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    Ok(ips)
}

pub async fn add_ip_to_whitelist(ip: String, pool: PoolType) -> Result<String, WhitelistError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| WhitelistError::ServerError("No authentication token found".to_string()))?;

    let backend_url = get_backend_url();
    let url = format!("{}/rpc/add_ip_to_whitelist", backend_url);

    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", token))
        .header("Content-Type", "application/json")
        .json(&serde_json::json!({ "ip": ip, "pool": pool.as_str() }))
        .send()
        .await
        .map_err(|e| WhitelistError::NetworkError(e.to_string()))?;

    let status = response.status();
    if !status.is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Failed to add IP to whitelist: {}", error_text);
        
        // Check HTTP status code for specific errors
        if status == reqwest::StatusCode::BAD_REQUEST {
            return Err(WhitelistError::IpAlreadyBound);
        }
        
        return Err(WhitelistError::ServerError(format!("Failed to add IP: {}", error_text)));
    }

    let response_data: serde_json::Value = response.json().await
        .map_err(|e| WhitelistError::NetworkError(e.to_string()))?;
    
    let message = response_data.get("message")
        .and_then(|m| m.as_str())
        .unwrap_or("IP added successfully")
        .to_string();
    
    Ok(message)
}

pub async fn remove_ip_from_whitelist(ip: String, pool: PoolType) -> Result<String, AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;

    let backend_url = get_backend_url();
    let url = format!("{}/rpc/remove_ip_from_whitelist", backend_url);

    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", token))
        .header("Content-Type", "application/json")
        .json(&serde_json::json!({ "ip": ip, "pool": pool.as_str() }))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;

    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Failed to remove IP from whitelist: {}", error_text);
        return Err(AuthError::NetworkError(format!("Failed to remove IP: {}", error_text)));
    }

    let response_data: serde_json::Value = response.json().await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    let message = response_data.get("message")
        .and_then(|m| m.as_str())
        .unwrap_or("IP removed successfully")
        .to_string();
    
    Ok(message)
}

pub fn validate_ip_address(ip: &str) -> bool {
    use std::net::Ipv4Addr;
    
    if ip.parse::<Ipv4Addr>().is_ok() {
        return true;
    }
    
    if ip.contains('/') {
        let parts: Vec<&str> = ip.split('/').collect();
        if parts.len() == 2 {
            if let (Ok(_), Ok(prefix)) = (parts[0].parse::<Ipv4Addr>(), parts[1].parse::<u8>()) {
                return prefix <= 32;
            }
        }
    }
    
    false
}

pub async fn get_api_token() -> Result<String, AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;

    let backend_url = get_backend_url();
    let url = format!("{}/rpc/reseller_token", backend_url);

    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", token))
        .header("Accept", "text/plain")
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;

    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Failed to fetch API token: {}", error_text);
        return Err(AuthError::NetworkError(format!("Failed to fetch API token: {}", error_text)));
    }

    let api_token = response.text().await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    Ok(api_token)
}

pub async fn get_referral_info() -> Result<rust_shared::repository::model::ReferralInfo, AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;

    let backend_url = get_backend_url();
    let url = format!("{}/legacy_referal_info", backend_url);

    let client = reqwest::Client::new();
    let response = client
        .get(&url)
        .header("Authorization", format!("Bearer {}", token))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;

    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Failed to fetch referral info: {}", error_text);
        return Err(AuthError::NetworkError(format!("Failed to fetch referral info: {}", error_text)));
    }

    let mut referral_data: Vec<rust_shared::repository::model::ReferralInfo> = response.json().await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if referral_data.is_empty() {
        return Err(AuthError::Other("No referral data found".to_string()));
    }
    
    Ok(referral_data.remove(0))
}

/// Check if user has account password set
pub async fn is_password_exists() -> Result<bool, AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;
    
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/is_password_exists", backend_url);
    
    let client = reqwest::Client::new();
    let response = client
        .get(&url)
        .header("Authorization", format!("Bearer {}", token))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Failed to check password existence: {}", error_text);
        return Err(AuthError::NetworkError(format!("Failed to check password existence: {}", error_text)));
    }
    
    let exists = response.text().await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    Ok(exists == "true")
}

/// Send password recovery email
pub async fn send_password_recovery_email(email: String) -> Result<(), AuthError> {
    let backend_url = get_backend_url();
    let recovery_url = format!("{}/rpc/password_recovery_email", backend_url);
    
    #[derive(Serialize)]
    struct PasswordRecoveryRequest {
        email: String,
    }
    
    let client = reqwest::Client::new();
    let response = client
        .post(&recovery_url)
        .json(&PasswordRecoveryRequest { email: email.clone() })
        .header("Content-Type", "application/json")
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Failed to send password recovery email: {}", error_text);
        return Err(AuthError::NetworkError(format!("Failed to send recovery email: {}", error_text)));
    }
    
    log::info!("Password recovery email sent to: {}", email);
    Ok(())
}

/// Reset password using recovery code
pub async fn reset_password_with_recovery_code(recovery_code: String, new_password: String) -> Result<(), AuthError> {
    let backend_url = get_backend_url();
    let reset_url = format!("{}/rpc/password_recovery", backend_url);
    
    #[derive(Serialize)]
    struct PasswordResetRequest {
        recovery_code: String,
        password: String,
    }
    
    let client = reqwest::Client::new();
    let response = client
        .post(&reset_url)
        .json(&PasswordResetRequest { 
            recovery_code, 
            password: new_password 
        })
        .header("Content-Type", "application/json")
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Failed to reset password with recovery code: {}", error_text);
        return Err(AuthError::NetworkError(format!("Failed to reset password: {}", error_text)));
    }
    
    log::info!("Password reset successful with recovery code");
    Ok(())
}

/// Change account password
pub async fn change_account_password(old_password: Option<String>, new_password: String) -> Result<(), AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;
    
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/change_account_password", backend_url);
    
    let mut request_body = serde_json::json!({
        "new_password": new_password
    });
    
    if let Some(old_pwd) = old_password {
        request_body["old_password"] = serde_json::Value::String(old_pwd);
    } else {
        request_body["old_password"] = serde_json::Value::String("".to_string());
    }
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", token))
        .header("Content-Type", "application/json")
        .json(&request_body)
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Failed to change account password: {}", error_text);
        return Err(AuthError::NetworkError(format!("Failed to change account password: {}", error_text)));
    }
    
    Ok(())
}

/// Reset proxy password for specific pool
pub async fn reset_proxy_password(pool: PoolType) -> Result<String, AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;
    
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/reset_password", backend_url);
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", token))
        .header("Content-Type", "application/json")
        .header("Accept", "text/plain")
        .json(&serde_json::json!({ "pool": pool.as_str() }))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Failed to reset proxy password: {}", error_text);
        return Err(AuthError::NetworkError(format!("Failed to reset proxy password: {}", error_text)));
    }
    
    response.text().await
        .map_err(|e| AuthError::NetworkError(e.to_string()))
}

/// Get proxy countries for a specific pool (public endpoint, no auth required)
pub async fn get_proxy_countries(pool: PoolType) -> Result<Vec<Country>, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/get_dataimpulse_proxy_countries?pool={}", backend_url, pool.as_str());
    
    let client = reqwest::Client::new();
    let response = client
        .get(&url)
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to fetch proxy countries: {}", prettify_json_or_text(&error_text))));
    }
    
    let countries = response.json::<Vec<Country>>().await
        .map_err(|e| AuthError::Other(format!("Failed to parse proxy countries response: {}", e)))?;
    
    Ok(countries)
}

/// Get proxy states for specific countries and pool
pub async fn get_proxy_states(pool: PoolType, countries: Vec<Country>) -> Result<Vec<State>, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/get_dataimpulse_proxy_states", backend_url);
    
    #[derive(Serialize)]
    struct StateRequest {
        pool: String,
        countries: Vec<String>,
    }
    
    let request = StateRequest {
        pool: pool.as_str().to_string(),
        countries: Country::extract_codes(&countries),
    };
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Content-Type", "application/json")
        .json(&request)
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to fetch proxy states: {}", prettify_json_or_text(&error_text))));
    }
    
    let states = response.json::<Vec<State>>().await
        .map_err(|e| AuthError::Other(format!("Failed to parse proxy states response: {}", e)))?;
    
    Ok(states)
}

/// Get proxy cities for specific countries and states
pub async fn get_proxy_cities(pool: PoolType, countries: Vec<Country>, states: Vec<State>) -> Result<Vec<City>, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/get_dataimpulse_proxy_cities", backend_url);
    
    #[derive(Serialize)]
    struct CityRequest {
        pool: String,
        countries: Vec<String>,
        states: Vec<String>,
    }
    
    let request = CityRequest {
        pool: pool.as_str().to_string(),
        countries: Country::extract_codes(&countries),
        states: State::extract_codes(&states),
    };
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Content-Type", "application/json")
        .json(&request)
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to fetch proxy cities: {}", prettify_json_or_text(&error_text))));
    }
    
    let cities = response.json::<Vec<City>>().await
        .map_err(|e| AuthError::Other(format!("Failed to parse proxy cities response: {}", e)))?;
    
    Ok(cities)
}

/// Get proxy ASNs for specific locations
pub async fn get_proxy_asns(pool: PoolType, countries: Vec<Country>, states: Vec<State>, cities: Vec<City>) -> Result<Vec<ASN>, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/get_dataimpulse_proxy_asns", backend_url);
    
    #[derive(Serialize)]
    struct ASNRequest {
        pool: String,
        countries: Vec<String>,
        states: Vec<String>,
        cities: Vec<String>,
    }
    
    let request = ASNRequest {
        pool: pool.as_str().to_string(),
        countries: Country::extract_codes(&countries),
        states: State::extract_codes(&states),
        cities: City::extract_codes(&cities),
    };
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Content-Type", "application/json")
        .json(&request)
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to fetch proxy ASNs: {}", prettify_json_or_text(&error_text))));
    }
    
    let asns = response.json::<Vec<ASN>>().await
        .map_err(|e| AuthError::Other(format!("Failed to parse proxy ASNs response: {}", e)))?;
    
    Ok(asns)
}

/// Get proxy ZIP codes for specific locations
pub async fn get_proxy_zip_codes(pool: PoolType, countries: Vec<Country>, states: Vec<State>, asns: Vec<ASN>) -> Result<Vec<ZipCode>, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/get_dataimpulse_proxy_zipcodes", backend_url);
    
    #[derive(Serialize)]
    struct ZipCodeRequest {
        pool: String,
        countries: Vec<String>,
        states: Vec<String>,
        asns: Vec<String>,
        order_by: String,
    }
    
    let request = ZipCodeRequest {
        pool: pool.as_str().to_string(),
        countries: Country::extract_codes(&countries),
        states: State::extract_codes(&states),
        asns: ASN::extract_codes(&asns),
        order_by: "name,asc".to_string(),
    };
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Content-Type", "application/json")
        .json(&request)
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to fetch proxy ZIP codes: {}", prettify_json_or_text(&error_text))));
    }
    
    let zip_codes = response.json::<Vec<ZipCode>>().await
        .map_err(|e| AuthError::Other(format!("Failed to parse proxy ZIP codes response: {}", e)))?;
    
    Ok(zip_codes)
}

/// Banner data structure from API
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Banner {
    pub id: u32,
    pub name: String,
    pub link: String,
}

/// Get promotional banners (public endpoint, no auth required)
pub async fn get_banners() -> Result<Vec<Banner>, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/get_banners", backend_url);
    
    let client = reqwest::Client::new();
    let response = client
        .get(&url)
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to fetch banners: {}", prettify_json_or_text(&error_text))));
    }
    
    let banners = response.json::<Vec<Banner>>().await
        .map_err(|e| AuthError::Other(format!("Failed to parse banners response: {}", e)))?;
    
    Ok(banners)
}

/// Get banner image blob (public endpoint, no auth required)
pub async fn get_banner_image(id: u32) -> Result<Vec<u8>, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/get_banner_blob?id={}", backend_url, id);
    
    let client = reqwest::Client::new();
    let response = client
        .get(&url)
        .header("Accept", "application/octet-stream")
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to fetch banner image: {}", prettify_json_or_text(&error_text))));
    }
    
    let image_bytes = response.bytes().await
        .map_err(|e| AuthError::Other(format!("Failed to read banner image: {}", e)))?;
    
    Ok(image_bytes.to_vec())
}

/// Get blog article image blob (public endpoint, no auth required)
pub async fn get_blog_article_image(article_id: i64) -> Result<Vec<u8>, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/get_blog_article_image_blob?article_id={}", backend_url, article_id);
    
    let client = reqwest::Client::new();
    let response = client
        .get(&url)
        .header("Accept", "application/octet-stream")
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to fetch blog article image: {}", prettify_json_or_text(&error_text))));
    }
    
    let image_bytes = response.bytes().await
        .map_err(|e| AuthError::Other(format!("Failed to read blog article image: {}", e)))?;
    
    Ok(image_bytes.to_vec())
}

/// Upload blog article image (requires authentication)
pub async fn upload_blog_article_image(article_id: i64, image_data: Vec<u8>) -> Result<(), AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/upload_blog_article_image", backend_url);
    
    #[derive(Serialize)]
    struct ImageUploadRequest {
        article_id: i64,
        image_data: String, // Base64 encoded
    }
    
    let base64_data = base64::engine::general_purpose::STANDARD.encode(&image_data);
    let request = ImageUploadRequest {
        article_id,
        image_data: base64_data,
    };
    
    let client = reqwest::Client::new();
    let mut request_builder = client.post(&url)
        .header("Content-Type", "application/json");
    
    // Add authentication header
    if let Some(token) = crate::auth::AuthState::get_token_from_storage() {
        request_builder = request_builder.header("Authorization", format!("Bearer {}", token));
    }
    
    let response = request_builder
        .json(&request)
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to upload image: {}", prettify_json_or_text(&error_text))));
    }
    
    Ok(())
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct DefaultPoolParameters {
    pub countries: Vec<String>,
    pub cities: Vec<String>,
    pub states: Vec<String>,
    pub zipcodes: Vec<String>,
    pub asns: Vec<String>,
    pub exclude_countries: Vec<String>,
    pub exclude_asn: Vec<String>,
    pub anonymous_filter: bool,
    pub rotation_interval: Option<u32>,
}

impl Default for DefaultPoolParameters {
    fn default() -> Self {
        Self {
            countries: vec![],
            cities: vec![],
            states: vec![],
            zipcodes: vec![],
            asns: vec![],
            exclude_countries: vec![],
            exclude_asn: vec![],
            anonymous_filter: true,
            rotation_interval: Some(5),
        }
    }
}

/// Get default pool parameters (follows package/web pattern)
pub async fn get_default_pool_parameters(pool: PoolType) -> Result<DefaultPoolParameters, AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;
    
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/get_default_pool_parameters", backend_url);
    
    #[derive(Serialize)]
    struct GetDefaultPoolRequest {
        pool: String,
    }
    
    let request = GetDefaultPoolRequest {
        pool: pool.as_str().to_string(),
    };
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", token))
        .header("Content-Type", "application/json")
        .json(&request)
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;

    if !response.status().is_success() {
        log::warn!("Failed to get default pool parameters, using defaults: {}", response.status());
        return Ok(DefaultPoolParameters::default());
    }

    let settings = response.json::<DefaultPoolParameters>().await
        .map_err(|e| {
            log::warn!("Failed to parse default pool parameters, using defaults: {}", e);
            AuthError::Other(format!("Failed to parse pool parameters: {}", e))
        })?;
    
    Ok(settings)
}

/// Save default pool parameters (follows package/web pattern)
pub async fn save_default_pool_parameters(pool: PoolType, settings: DefaultPoolParameters) -> Result<(), AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;
    
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/set_default_pool_parameters", backend_url);
    
    #[derive(Serialize)]
    struct SetDefaultPoolRequest {
        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 = SetDefaultPoolRequest {
        pool: pool.as_str().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 client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", token))
        .header("Content-Type", "application/json")
        .json(&request)
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;

    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to save pool parameters: {}", error_text)));
    }

    Ok(())
}

#[derive(Serialize, Clone, Debug)]
pub struct GetProxyRequest {
    pub anonymous: String,
    #[serde(rename = "type")]
    pub session_type: String,
    pub protocol: String,
    pub format: String,
    pub quantity: u32,
    pub sessionttl: u32,
    pub pool: String,
    pub countries: Option<Vec<String>>,
    pub cities: Option<Vec<String>>,
    pub states: Option<Vec<String>>,
    pub excluded_asns: Option<Vec<String>>,
    pub included_asns: Option<Vec<String>>,
    pub zipcodes: Option<Vec<String>>,
    pub region: String,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct GeneratedProxy {
    pub proxy_string: String,
    pub id: u32,
    pub country: Option<String>,
    pub region: Option<String>,
    pub city: Option<String>,
}

/// Generate proxies using the real /rpc/get_proxy endpoint (follows package/web pattern)
pub async fn get_proxy(request: GetProxyRequest) -> Result<String, AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;
    
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/get_proxy", backend_url);
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", token))
        .header("Accept", "text/plain")
        .header("Content-Type", "application/json")
        .json(&request)
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;

    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to get proxy: {}", error_text)));
    }

    let proxy_text = response.text().await
        .map_err(|e| AuthError::Other(format!("Failed to read proxy response: {}", e)))?;
    
    Ok(proxy_text)
}


#[derive(Clone, Deserialize)]
pub struct PoolPrices {
    pub residential: f64,
    pub residential_premium: f64,
    pub datacenter: f64,
    pub mobile: f64,
}

#[derive(Serialize)]
pub struct CalculateRequest {
    pub amounts: Vec<u32>,
}

/// Get current pool prices from the server
pub async fn get_prices() -> Result<PoolPrices, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/prices", backend_url);
    
    let client = reqwest::Client::new();
    let response = client
        .get(&url)
        .header("Accept", "application/json")
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;

    let status = response.status();
    if !status.is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Failed to fetch prices: {}", error_text);
        return Err(AuthError::HttpError { 
            status: status.as_u16(), 
            message: error_text 
        });
    }

    let prices: PoolPrices = response.json().await
        .map_err(|e| AuthError::Other(format!("Failed to parse prices: {}", e)))?;
    
    log::info!("Prices fetched successfully");
    Ok(prices)
}

/// Calculate discounts for given amounts
pub async fn calculate_discounts(amounts: Vec<u32>) -> Result<Vec<f64>, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/calculate", backend_url);
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Accept", "application/json")
        .header("Content-Type", "application/json")
        .json(&CalculateRequest { amounts })
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;

    let status = response.status();
    if !status.is_success() {
        let error_text = response.text().await
            .unwrap_or_else(|_| "Unknown error".to_string());
        log::error!("Failed to calculate discounts: {}", error_text);
        return Err(AuthError::HttpError { 
            status: status.as_u16(), 
            message: error_text 
        });
    }

    let discounts: Vec<f64> = response.json().await
        .map_err(|e| AuthError::Other(format!("Failed to parse discounts: {}", e)))?;
    
    log::info!("Discounts calculated successfully");
    Ok(discounts)
}

#[derive(Clone, Deserialize)]
pub struct DetailedPriceCalculation {
    pub base_price: f64,
    pub volume_discount: f64,
    pub volume_discount_amount: f64,
    pub promo_discount: Option<f64>,
    pub promo_discount_amount: Option<f64>,
    pub final_price: f64,
}

#[derive(Deserialize)]
pub struct PromoCodeInfo {
    pub discount: f64,
    pub description: Option<String>,
}

#[derive(Debug)]
pub enum PurchaseError {
    NetworkError(String),
    PaymentMethodNotAvailable,
    PromoCodeNotExists,
    InsufficientReferralBalance,
    Other(String),
}

impl std::fmt::Display for PurchaseError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            PurchaseError::NetworkError(msg) => write!(f, "Network error: {}", msg),
            PurchaseError::PaymentMethodNotAvailable => write!(f, "Payment method not available"),
            PurchaseError::PromoCodeNotExists => write!(f, "Promo code does not exist"),
            PurchaseError::InsufficientReferralBalance => write!(f, "Insufficient referral balance"),
            PurchaseError::Other(msg) => write!(f, "{}", msg),
        }
    }
}

/// Get pool prices
pub async fn get_pool_prices(token: &str) -> Result<PoolPrices, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/prices", backend_url);
    
    let client = reqwest::Client::new();
    let response = client
        .get(&url)
        .header("Authorization", format!("Bearer {}", token))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to get prices: {}", error_text)));
    }
    
    let prices = response.json::<PoolPrices>().await
        .map_err(|e| AuthError::Other(format!("Failed to parse prices: {}", e)))?;
    
    Ok(prices)
}

/// Get payment systems
pub async fn get_payment_systems(token: &str) -> Result<Vec<PaymentSystem>, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/payment_systems", backend_url);
    
    let client = reqwest::Client::new();
    let response = client
        .get(&url)
        .header("Authorization", format!("Bearer {}", token))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to get payment systems: {}", error_text)));
    }
    
    let payment_systems = response.json::<Vec<PaymentSystem>>().await
        .map_err(|e| AuthError::Other(format!("Failed to parse payment systems: {}", e)))?;
    
    Ok(payment_systems)
}

/// Calculate detailed price with discounts
pub async fn calculate_detailed_price(
    token: &str,
    pool: PoolType,
    amount: u32,
    promo_code: Option<&str>,
) -> Result<DetailedPriceCalculation, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/calculate", backend_url);
    
    let client = reqwest::Client::new();
    
    let volume_response = client
        .post(&url)
        .json(&serde_json::json!({
            "amounts": [amount]
        }))
        .header("Authorization", format!("Bearer {}", token))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !volume_response.status().is_success() {
        let error_text = volume_response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to calculate volume discount: {}", error_text)));
    }
    
    let discounts: Vec<f64> = volume_response.json().await
        .map_err(|e| AuthError::Other(format!("Failed to parse volume discount: {}", e)))?;
    let volume_discount = discounts.first().copied().unwrap_or(0.0);
    
    let prices = get_pool_prices(token).await?;
    let base_price = match pool {
        PoolType::Residential => prices.residential * amount as f64,
        PoolType::ResidentialPremium => prices.residential_premium * amount as f64,
        PoolType::Datacenter => prices.datacenter * amount as f64,
        PoolType::Mobile => prices.mobile * amount as f64,
    };
    
    let volume_discount_amount = base_price * volume_discount;
    let price_after_volume = base_price - volume_discount_amount;
    
    let (promo_discount, promo_discount_amount) = if let Some(promo) = promo_code {
        match validate_promo_code(token, promo, amount).await {
            Ok(promo_info) => {
                let discount_amount = price_after_volume * promo_info.discount;
                (Some(promo_info.discount), Some(discount_amount))
            }
            Err(_) => (None, None),
        }
    } else {
        (None, None)
    };
    
    let final_price = price_after_volume - promo_discount_amount.unwrap_or(0.0);
    
    Ok(DetailedPriceCalculation {
        base_price,
        volume_discount,
        volume_discount_amount,
        promo_discount,
        promo_discount_amount,
        final_price,
    })
}

/// Validate promo code
pub async fn validate_promo_code(
    token: &str,
    promo_code: &str,
    amount: u32,
) -> Result<PromoCodeInfo, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/validate_promo", backend_url);
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .json(&serde_json::json!({
            "promo_code": promo_code,
            "amount": amount
        }))
        .header("Authorization", format!("Bearer {}", token))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    let status = response.status();
    if !status.is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        if status == reqwest::StatusCode::UNPROCESSABLE_ENTITY {
            return Err(AuthError::Other("Promo code does not exist".to_string()));
        }
        return Err(AuthError::Other(format!("Failed to validate promo code: {}", error_text)));
    }
    
    let promo_info = response.json::<PromoCodeInfo>().await
        .map_err(|e| AuthError::Other(format!("Failed to parse promo code info: {}", e)))?;
    
    Ok(promo_info)
}

/// Create invoice for web payment
pub async fn create_invoice_web(
    token: &str,
    request: PurchaseRequest,
) -> Result<String, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/create_invoice_web", backend_url);
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .json(&request)
        .header("Authorization", format!("Bearer {}", token))
        .header("Content-Type", "application/json")
        .header("Accept", "text/plain")
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    let status = response.status();
    if !status.is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        if status == reqwest::StatusCode::CONFLICT {
            return Err(AuthError::Other("Payment method not available".to_string()));
        }
        if status == reqwest::StatusCode::UNPROCESSABLE_ENTITY {
            if error_text.contains("promocode") {
                return Err(AuthError::Other("Promo code does not exist".to_string()));
            }
        }
        return Err(AuthError::Other(format!("Failed to create invoice: {}", error_text)));
    }
    
    let invoice_url = response.text().await
        .map_err(|e| AuthError::Other(format!("Failed to read invoice URL: {}", e)))?;
    
    Ok(invoice_url)
}

/// Process referral payment
pub async fn process_referral_payment(
    token: &str,
    request: ReferralPaymentRequest,
) -> Result<(), AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/referral_payment", backend_url);
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .json(&request)
        .header("Authorization", format!("Bearer {}", token))
        .header("Content-Type", "application/json")
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    let status = response.status();
    if !status.is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        if status == reqwest::StatusCode::PAYMENT_REQUIRED {
            return Err(AuthError::Other("Insufficient referral balance".to_string()));
        }
        if status == reqwest::StatusCode::UNPROCESSABLE_ENTITY {
            if error_text.contains("promocode") {
                return Err(AuthError::Other("Promo code does not exist".to_string()));
            }
        }
        return Err(AuthError::Other(format!("Failed to process referral payment: {}", error_text)));
    }
    
    Ok(())
}

/// Activate traffic promocode
pub async fn activate_traffic_promocode(
    token: &str,
    promo_code: &str,
) -> Result<(), AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/activate_traffic_promocode", backend_url);
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .json(&serde_json::json!({
            "code": promo_code
        }))
        .header("Authorization", format!("Bearer {}", token))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    let status = response.status();
    if !status.is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        if status == reqwest::StatusCode::UNPROCESSABLE_ENTITY {
            return Err(AuthError::Other("Promo code does not exist or has expired".to_string()));
        }
        if status == reqwest::StatusCode::CONFLICT {
            return Err(AuthError::Other("Promo code has already been used".to_string()));
        }
        return Err(AuthError::Other(format!("Failed to activate promo code: {}", error_text)));
    }
    
    Ok(())
}

/// Track referral source when user visits the site
pub async fn track_referral_source(
    referral_code: Option<&str>,
    source: Option<&str>,
    utm_source: Option<&str>,
    utm_medium: Option<&str>,
    utm_campaign: Option<&str>,
) -> Result<(), AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/track_referral_source", backend_url);
    
    let client = reqwest::Client::new();
    let mut params = vec![];
    
    if let Some(code) = referral_code {
        params.push(("referral_code", code));
    }
    if let Some(source) = source {
        params.push(("source", source));
    }
    if let Some(utm_source) = utm_source {
        params.push(("utm_source", utm_source));
    }
    if let Some(utm_medium) = utm_medium {
        params.push(("utm_medium", utm_medium));
    }
    if let Some(utm_campaign) = utm_campaign {
        params.push(("utm_campaign", utm_campaign));
    }
    
    if !params.is_empty() {
        let response = client
            .post(&url)
            .form(&params)
            .send()
            .await
            .map_err(|e| AuthError::NetworkError(e.to_string()))?;
        
        if !response.status().is_success() {
            let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
            log::warn!("Failed to track referral source: {}", error_text);
        }
    }
    
    Ok(())
}

/// Attach referral data to registration request
pub async fn register_with_referral(
    username: &str,
    password: &str,
    referral_code: Option<&str>,
    source: Option<&str>,
) -> Result<String, AuthError> {
    let backend_url = get_backend_url();
    let url = format!("{}/register", backend_url);
    
    let mut request_data = serde_json::json!({
        "username": username,
        "password": password
    });
    
    if let Some(code) = referral_code {
        request_data["referral_code"] = serde_json::Value::String(code.to_string());
    }
    if let Some(source) = source {
        request_data["source"] = serde_json::Value::String(source.to_string());
    }
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .json(&request_data)
        .header("Content-Type", "application/json")
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Registration failed: {}", error_text)));
    }
    
    let token = response.text().await
        .map_err(|e| AuthError::Other(format!("Failed to parse registration response: {}", e)))?;
    
    if token.trim().is_empty() {
        return Err(AuthError::Other("Registration failed - no token returned".to_string()));
    }
    
    log::info!("Registration with referral successful, email verification required");
    
    Ok(token)
}

// ============================================================================
// Blog Article API Functions
// ============================================================================


#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct BlogArticle {
    pub id: i64,
    pub title: String,
    pub content: String,
    pub language: Language,
    pub hidden: bool,
    pub created_at: String,
    pub updated_at: String,
    pub has_image: Option<bool>,
}

#[derive(Serialize, Clone, Debug)]
pub struct CreateArticleRequest {
    pub title: String,
    pub content: String,
    pub language: Language,
    pub hidden: bool,
}

#[derive(Serialize, Clone, Debug)]
pub struct UpdateArticleRequest {
    pub title: Option<String>,
    pub content: Option<String>,
    pub language: Option<Language>,
    pub hidden: Option<bool>,
}

/// Create a new blog article
pub async fn create_blog_article(
    title: String,
    content: String,
    language: Language,
    hidden: bool,
) -> Result<i64, AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;
    
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/create_blog_article", backend_url);
    
    let request = CreateArticleRequest {
        title,
        content,
        language,
        hidden,
    };
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", token))
        .header("Content-Type", "application/json")
        .json(&request)
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to create article: {}", error_text)));
    }
    
    let result: serde_json::Value = response.json().await
        .map_err(|e| AuthError::Other(format!("Failed to parse create article response: {}", e)))?;
    
    let article_id = result.get("id")
        .and_then(|v| v.as_i64())
        .ok_or_else(|| AuthError::Other("Invalid response: missing article ID".to_string()))?;
    
    Ok(article_id)
}

/// Get all public blog articles (hidden articles visible only to admins)
pub async fn get_blog_articles(
    limit: Option<u32>,
    offset: Option<u32>,
    search_query: Option<String>,
    user_language: Option<Language>,
) -> Result<Vec<BlogArticle>, AuthError> {
    let backend_url = get_backend_url();
    let mut url = format!("{}/rpc/get_blog_articles", backend_url);
    
    // Add query parameters
    let mut params = Vec::new();
    if let Some(limit) = limit {
        params.push(format!("limit_count={}", limit));
    }
    if let Some(offset) = offset {
        params.push(format!("offset_count={}", offset));
    }
    if let Some(search) = search_query {
        if !search.trim().is_empty() {
            params.push(format!("search_query={}", urlencoding::encode(&search)));
        }
    }
    if let Some(lang) = user_language {
        params.push(format!("user_language={}", lang.to_string()));
    }
    
    if !params.is_empty() {
        url.push('?');
        url.push_str(&params.join("&"));
    }
    
    let client = reqwest::Client::new();
    let mut request = client.get(&url);
    
    // Add authentication header if token is available
    if let Some(token) = crate::auth::AuthState::get_token_from_storage() {
        request = request.header("Authorization", format!("Bearer {}", token));
    }
    
    let response = request
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to get articles: {}", error_text)));
    }
    
    let result: serde_json::Value = response.json().await
        .map_err(|e| AuthError::Other(format!("Failed to parse articles response: {}", e)))?;
    
    let articles_array = result.get("articles")
        .and_then(|v| v.as_array())
        .ok_or_else(|| AuthError::Other("Invalid response: missing articles array".to_string()))?;
    
    let articles: Vec<BlogArticle> = serde_json::from_value(serde_json::Value::Array(articles_array.clone()))
        .map_err(|e| AuthError::Other(format!("Failed to parse articles: {}", e)))?;
    
    Ok(articles)
}

/// Get a specific blog article by ID
pub async fn get_blog_article(article_id: i64) -> Result<BlogArticle, AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;
    
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/get_blog_article?article_id={}", backend_url, article_id);
    
    let client = reqwest::Client::new();
    let response = client
        .get(&url)
        .header("Authorization", format!("Bearer {}", token))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to get article: {}", error_text)));
    }
    
    let article: BlogArticle = response.json().await
        .map_err(|e| AuthError::Other(format!("Failed to parse article response: {}", e)))?;
    
    Ok(article)
}

/// Check if the current user can edit a specific article
pub async fn can_edit_article(article_id: i64) -> Result<bool, AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;
    
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/can_edit_article", backend_url);
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", token))
        .header("Content-Type", "application/json")
        .json(&serde_json::json!({
            "article_id": article_id
        }))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to check article access: {}", error_text)));
    }
    
    let result: serde_json::Value = response.json().await
        .map_err(|e| AuthError::Other(format!("Failed to parse access check response: {}", e)))?;
    
    let can_edit = result.get("can_edit")
        .and_then(|v| v.as_bool())
        .ok_or_else(|| AuthError::Other("Invalid response: missing can_edit field".to_string()))?;
    
    Ok(can_edit)
}

/// Update a blog article
pub async fn update_blog_article(
    article_id: i64,
    title: Option<String>,
    content: Option<String>,
    language: Option<Language>,
    hidden: Option<bool>,
) -> Result<(), AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;
    
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/update_blog_article", backend_url);
    
    let request = UpdateArticleRequest {
        title,
        content,
        language,
        hidden,
    };
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", token))
        .header("Content-Type", "application/json")
        .json(&serde_json::json!({
            "article_id": article_id,
            "title": request.title,
            "content": request.content,
            "language": request.language.map(|l| l.to_string()),
            "hidden": request.hidden
        }))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to update article: {}", error_text)));
    }
    
    Ok(())
}

/// Delete a blog article
pub async fn delete_blog_article(article_id: i64) -> Result<(), AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;
    
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/delete_blog_article", backend_url);
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", token))
        .header("Content-Type", "application/json")
        .json(&serde_json::json!({
            "article_id": article_id
        }))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to delete article: {}", error_text)));
    }
    
    Ok(())
}

/// Toggle article visibility (hide/show)
pub async fn toggle_blog_article_visibility(article_id: i64) -> Result<bool, AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;
    
    let backend_url = get_backend_url();
    let url = format!("{}/rpc/toggle_blog_article_visibility", backend_url);
    
    let client = reqwest::Client::new();
    let response = client
        .post(&url)
        .header("Authorization", format!("Bearer {}", token))
        .header("Content-Type", "application/json")
        .json(&serde_json::json!({
            "article_id": article_id
        }))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to toggle article visibility: {}", error_text)));
    }
    
    let result: serde_json::Value = response.json().await
        .map_err(|e| AuthError::Other(format!("Failed to parse toggle response: {}", e)))?;
    
    let article = result.get("article")
        .ok_or_else(|| AuthError::Other("Invalid response: missing article".to_string()))?;
    
    let hidden = article.get("hidden")
        .and_then(|v| v.as_bool())
        .ok_or_else(|| AuthError::Other("Invalid response: missing hidden status".to_string()))?;
    
    Ok(hidden)
}

/// Get all articles for the current user (including hidden ones)
pub async fn get_my_articles(
    limit: Option<u32>,
    offset: Option<u32>,
) -> Result<Vec<BlogArticle>, AuthError> {
    let token = crate::auth::AuthState::get_token_from_storage()
        .ok_or_else(|| AuthError::Other("No authentication token found".to_string()))?;
    
    let backend_url = get_backend_url();
    let mut url = format!("{}/rpc/get_my_articles", backend_url);
    
    // Add query parameters
    let mut params = Vec::new();
    if let Some(limit) = limit {
        params.push(format!("limit_count={}", limit));
    }
    if let Some(offset) = offset {
        params.push(format!("offset_count={}", offset));
    }
    
    if !params.is_empty() {
        url.push('?');
        url.push_str(&params.join("&"));
    }
    
    let client = reqwest::Client::new();
    let response = client
        .get(&url)
        .header("Authorization", format!("Bearer {}", token))
        .send()
        .await
        .map_err(|e| AuthError::NetworkError(e.to_string()))?;
    
    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string());
        return Err(AuthError::Other(format!("Failed to get my articles: {}", error_text)));
    }
    
    let result: serde_json::Value = response.json().await
        .map_err(|e| AuthError::Other(format!("Failed to parse my articles response: {}", e)))?;
    
    let articles_array = result.get("articles")
        .and_then(|v| v.as_array())
        .ok_or_else(|| AuthError::Other("Invalid response: missing articles array".to_string()))?;
    
    let articles: Vec<BlogArticle> = serde_json::from_value(serde_json::Value::Array(articles_array.clone()))
        .map_err(|e| AuthError::Other(format!("Failed to parse articles: {}", e)))?;
    
    Ok(articles)
}

