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

#[derive(serde::Deserialize)]
struct ErrorResponse {
    code: Option<String>,
    #[allow(dead_code)]
    message: Option<String>,
}

fn should_retry_error(error_text: &str) -> bool {
    if let Ok(error_response) = serde_json::from_str::<ErrorResponse>(error_text) {
        if let Some(code) = error_response.code {
            return code == "XX000";
        }
    }
    false
}

/// Get user profile with optional retry for timeout errors
pub async fn get_profile_with_optional_headers(user_id: String, email: Option<String>, username: Option<String>) -> Result<Profile> {
    const MAX_RETRIES: u32 = 3;
    const RETRY_DELAY_MS: u64 = 1000; // 1 second between retries

    for attempt in 1..=MAX_RETRIES {
        let url = format!("{host}/legacy_profile", host=&*BACKEND_API_URL);
        log::trace!("fetching {url} (attempt {}/{})", attempt, MAX_RETRIES, url = url.red());

        let mut request = reqwest::Client::new()
            .get(&url)
            .header(
                "Authorization",
                get_auth_token().await?,
            )
            .header("X-Tg-Id", &user_id);
        
        if let Some(ref email) = email {
            request = request.header("X-Email", email);
        }
        
        if let Some(ref username) = username {
            request = request.header("X-Username", username);
        }
        
        let response = request.send().await?;

        if !response.status().is_success() {
            let error_text = response.text().await?;
            
            if should_retry_error(&error_text) && attempt < MAX_RETRIES {
                log::warn!("XX000 timeout error on legacy_profile, retrying... (attempt {}/{})", attempt, MAX_RETRIES);
                tokio::time::sleep(tokio::time::Duration::from_millis(RETRY_DELAY_MS)).await;
                continue;
            }
            
            return Err(Report::msg(format!(
                "Failed to get profile: {}",
                prettify_json_or_text(&error_text)
            )));
        }

        let mut profiles: Vec<Profile> = response.json::<Vec<Profile>>().await?;
        if profiles.is_empty() {
            return Err(Report::msg("No profile found"));
        }

        log::debug!("profile: {}", serde_json::to_string_pretty(&profiles[0])?);

        return Ok(mem::take(&mut profiles[0]));
    }

    Err(Report::msg("Max retries exceeded for legacy_profile"))
}

/// Get user profile by user ID
pub async fn get_profile_by_user_id(user_id: String) -> Result<Profile> {
    get_profile_with_optional_headers(user_id, None, None).await
}

/// Get referral information for a user
pub async fn get_referral_info(user_id: String) -> Result<ReferralInfo> {
    let url = format!("{host}/legacy_referal_info", 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)
        .send()
        .await?;

    if !response.status().is_success() {
        return Err(Report::msg(format!(
            "Failed to get referral info: {}",
            prettify_json_or_text(&response.text().await?)
        )));
    }
    
    let mut referral_info = response.json::<Vec<ReferralInfo>>().await?;
    Ok(mem::take(&mut referral_info[0]))
}

/// Reset password for a specific pool
pub async fn reset_password(user_id: String, pool: PoolType) -> Result<String> {
    let url = format!("{host}/rpc/reset_password", host=&*BACKEND_API_URL);
    log::trace!("fetching {url}", url = url.red());

    let response = reqwest::Client::new()
        .post(&url)
        .json(&serde_json::json!({ "pool": pool.to_string() }))
        .header(
            "Authorization",
            get_auth_token().await?,
        )
        .header("X-Tg-Id", user_id)
        .header("Accept", "text/plain")
        .send()
        .await?;

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

    Ok(response.text().await?)
}

/// Check if user has a password set
pub async fn is_password_exists(user_id: String) -> Result<bool> {
    let url = format!("{host}/rpc/is_password_exists", 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)
        .send()
        .await?;

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

    let exists = response.text().await?;
    Ok(exists == "true")
}

/// Change account password
pub async fn change_account_password(user_id: String, old_password: Option<String>, new_password: String) -> Result<()> {
    let url = format!("{host}/rpc/change_account_password", host=&*BACKEND_API_URL);
    log::trace!("fetching {url}", url = url.red());

    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);
    }

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

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

    Ok(())
}

/// Create referral withdrawal request
pub async fn create_referral_withdrawal_request(
    user_id: String,
    amount: f64,
    comment: String,
) -> Result<WithdrawalResponse> {
    let url = format!("{host}/rpc/create_referral_withdraw_request", host=&*BACKEND_API_URL);
    log::trace!("fetching {url}", url = url.red());

    let request = WithdrawalRequest {
        amount,
        comment,
    };

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

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

    let withdrawal_response = response.json::<WithdrawalResponse>().await?;
    Ok(withdrawal_response)
}
