use crate::repository::api::prelude::*;
use crate::Language;
use chrono::{DateTime, FixedOffset};
use color_eyre::Result;
use serde::{Deserialize, Serialize};
use base64::{Engine as _, engine::general_purpose};
use urlencoding;
use color_eyre::eyre::ErrReport;
use thiserror::Error;

#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct UnpairedTranslation {
    pub hash: String,
    pub content: String,
    pub language_legacy_code: Language,
}

pub async fn get_unpaired_translation() -> Result<Option<UnpairedTranslation>> {
    let auth_token = get_auth_token().await?;

    let url = format!("{}/unpaired_translations_legacy_language?limit=1", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .get(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .send()
        .await;

    log::debug!("get_unpaired_translation response: {:?}", response);

    match response {
        Ok(response) => match response.status() {
            reqwest::StatusCode::OK => {
                let translations = response.json::<Vec<UnpairedTranslation>>().await?;
                Ok(translations.into_iter().next())
            }
            _ => {
                let error_text = response.text().await.unwrap_or_default();
                Err(Report::msg(format!(
                    "Failed to get unpaired translation: {}",
                    prettify_json_or_text(&error_text)
                )))
            }
        },
        Err(e) => Err(Report::msg(format!(
            "Failed to get unpaired translation: {:?}",
            e
        ))),
    }
}

pub async fn add_translation(hash: String, language_legacy_code: Language, content: String) -> Result<()> {
    #[derive(Serialize)]
    struct AddTranslationRequest {
        hash: String,
        language_legacy_code: Language,
        content: String,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/add_translation", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .json(&AddTranslationRequest {
            hash,
            language_legacy_code,
            content,
        })
        .send()
        .await;

    log::debug!("add_translation response: {:?}", response);

    match response {
        Ok(response) => match response.status() {
            reqwest::StatusCode::OK | reqwest::StatusCode::NO_CONTENT => Ok(()),
            _ => {
                let error_text = response.text().await.unwrap_or_default();
                Err(Report::msg(format!(
                    "Failed to add translation: {}",
                    prettify_json_or_text(&error_text)
                )))
            }
        },
        Err(e) => Err(Report::msg(format!(
            "Failed to add translation: {:?}",
            e
        ))),
    }
}

pub async fn is_admin(tg_id: i64) -> Result<bool> {
    let user_id = get_test_user_id(tg_id);
    #[derive(Serialize)]
    struct IsAdminData {
        user_id: String,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/tg.is_admin", *BACKEND_API_URL);
    log::trace!("fetching {url}", url = url.red());
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "vgate")
        .json(&IsAdminData { user_id })
        .send()
        .await;

    log::debug!("response: {:?}", response);

    #[derive(Deserialize)]
    struct IsAdminResponse {
        is_admin: bool,
    }

    match response {
        Ok(response) => match response.status() {
            reqwest::StatusCode::OK => {
                let is_admin =
                    response.json::<IsAdminResponse>().await?.is_admin;
                Ok(is_admin)
            }
            _ => Err(Report::msg(format!(
                "Failed to get admin status: {}\n{:#?}",
                response.status(),
                response.headers(),
            ))),
        },
        Err(e) => {
            Err(Report::msg(format!("Failed to get admin status: {:?}", e)))
        }
    }
}

pub async fn get_stats() -> Result<AdminStats> {
    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/admin_stats", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "vgate")
        .send()
        .await;

    log::debug!("response: {:?}", response);

    match response {
        Ok(response) => match response.status() {
            reqwest::StatusCode::OK => {
                let admin_stats = response.json::<AdminStats>().await?;
                Ok(admin_stats)
            }
            _ => Err(Report::msg(format!(
                "Failed to get admin statistics: {}\n{:#?}",
                response.status(),
                response.headers(),
            ))),
        },
        Err(e) => Err(Report::msg(format!(
            "Failed to get admin statistics: {:?}",
            e
        ))),
    }
}

pub async fn send_daily_stats(
    from_date: Option<DateTime<FixedOffset>>,
    to_date: Option<DateTime<FixedOffset>>,
    recipient: i64
) -> Result<()> {
    let recipient = get_test_user_id(recipient);
    let auth_token = get_auth_token().await?;

    #[derive(Serialize)]
    struct SendDailyStatsRequest {
        from_date: Option<String>,
        to_date: Option<String>,
    }

    let url = format!("{}/rpc/send_daily_stats", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("X-Tg-Id", recipient)
        .json(&SendDailyStatsRequest {
            from_date: from_date
                .map(|date| date.format("%Y-%m-%d %H:%M:%S%z").to_string()),
            to_date: to_date
                .map(|date| date.format("%Y-%m-%d %H:%M:%S%z").to_string()),
        })
        .send()
        .await?;

    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_default();
        return Err(Report::msg(format!(
            "Failed to send daily statistics: {}",
            prettify_json_or_text(&error_text)
        )));
    }

    Ok(())
}

pub async fn create_traffic_promo(
    names: Vec<String>,
    pool: String,
    activations: i32,
    gb_amount: i32,
    count: i32,
    expiration_date: Option<String>,
    tg_id: i64,
) -> Result<Vec<String>> {
    let user_id = get_test_user_id(tg_id);
    #[derive(Serialize)]
    struct CreateTrafficPromoRequest {
        names: Vec<String>,
        pool: String,
        activations: i32,
        gb_amount: i32,
        count: i32,
        expiration_date: Option<String>,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/create_traffic_promo", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "vgate")
        .header("X-Tg-Id", user_id)
        .json(&CreateTrafficPromoRequest {
            names,
            pool,
            activations,
            gb_amount,
            count,
            expiration_date,
        })
        .send()
        .await?;

    match response.status() {
        reqwest::StatusCode::OK => {
            Ok(response.json::<Vec<String>>().await?)
        }
        reqwest::StatusCode::CONFLICT => {
            let error_text = response.text().await.unwrap_or_default();
            Err(Report::msg(format!("CONFLICT: {}", prettify_json_or_text(&error_text))))
        }
        _ => {
            let error_text = response.text().await.unwrap_or_default();
            Err(Report::msg(format!("Failed to create traffic promo: {}", prettify_json_or_text(&error_text))))
        }
    }
}

pub async fn get_promo_codes(page: usize, limit: usize, tg_id: i64) -> Result<Vec<PromoCode>> {
    let user_id = get_test_user_id(tg_id);
    let auth_token = get_auth_token().await?;
    let offset = page * limit;

    let url = format!("{}/tariff_promo?offset={}&limit={}", *BACKEND_ADMIN_URL, offset, limit);
    log::trace!("fetching {url}", url = url.red());
    let response = reqwest::Client::new()
        .get(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "vgate")
        .header("X-Tg-Id", user_id)
        .send()
        .await;

    log::debug!("response: {:?}", response);

    match response {
        Ok(response) => match response.status() {
            reqwest::StatusCode::OK => {
                let promo_codes = response.json::<Vec<PromoCode>>().await?;
                log::debug!("promo_codes: {:?}", promo_codes);
                Ok(promo_codes)
            }
            _ => Err(Report::msg(format!(
                "Failed to get promo codes: {}\n{:#?}",
                response.status(),
                response.headers(),
            ))),
        },
        Err(e) => Err(Report::msg(format!(
            "Failed to get promo codes: {:?}",
            e
        ))),
    }
}

pub async fn delete_promo_code(promo_name: String) -> Result<()> {
    #[derive(Serialize)]
    struct DeletePromoRequest {
        promo_name: String,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/delete_tariff_promo", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "vgate")
        .json(&DeletePromoRequest { promo_name })
        .send()
        .await?;

    match response.status() {
        reqwest::StatusCode::OK | reqwest::StatusCode::NO_CONTENT => Ok(()),
        _ => {
            let error_text = response.text().await.unwrap_or_default();
            Err(Report::msg(format!(
                "Failed to delete promo code: {}",
                prettify_json_or_text(&error_text)
            )))
        }
    }
}

pub async fn create_banner(
    name: String,
    link: String,
    blob: Vec<u8>,
) -> Result<i32> {
    #[derive(Serialize)]
    struct CreateBannerRequest {
        name: String,
        link: String,
        blob: String,
    }

    let auth_token = get_auth_token().await?;
    let blob_base64 = general_purpose::STANDARD.encode(&blob);

    let url = format!("{}/rpc/create_banner", *BACKEND_API_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "vgate")
        .json(&CreateBannerRequest {
            name,
            link,
            blob: blob_base64,
        })
        .send()
        .await?;

    match response.status() {
        reqwest::StatusCode::OK => {
            let banner_id = response.json::<i32>().await?;
            Ok(banner_id)
        }
        _ => {
            let error_text = response.text().await.unwrap_or_default();
            Err(Report::msg(format!(
                "Failed to create banner: {}",
                prettify_json_or_text(&error_text)
            )))
        }
    }
}

pub async fn update_banner(
    banner_id: i32,
    name: String,
    link: String,
    blob: Option<Vec<u8>>,
) -> Result<()> {
    #[derive(Serialize)]
    struct UpdateBannerRequest {
        id: i32,
        name: String,
        link: String,
        blob: Option<String>,
    }

    let auth_token = get_auth_token().await?;
    let blob_base64 = blob.map(|b| general_purpose::STANDARD.encode(&b));

    let url = format!("{}/rpc/update_banner", *BACKEND_API_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "vgate")
        .json(&UpdateBannerRequest {
            id: banner_id,
            name,
            link,
            blob: blob_base64,
        })
        .send()
        .await?;

    match response.status() {
        reqwest::StatusCode::OK | reqwest::StatusCode::NO_CONTENT => Ok(()),
        _ => {
            let error_text = response.text().await.unwrap_or_default();
            Err(Report::msg(format!(
                "Failed to update banner: {}",
                prettify_json_or_text(&error_text)
            )))
        }
    }
}

pub async fn delete_banner(banner_id: i32) -> Result<()> {
    #[derive(Serialize)]
    struct DeleteBannerRequest {
        id: i32,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/delete_banner", *BACKEND_API_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "vgate")
        .json(&DeleteBannerRequest { id: banner_id })
        .send()
        .await?;

    match response.status() {
        reqwest::StatusCode::OK | reqwest::StatusCode::NO_CONTENT => Ok(()),
        _ => {
            let error_text = response.text().await.unwrap_or_default();
            Err(Report::msg(format!(
                "Failed to delete banner: {}",
                prettify_json_or_text(&error_text)
            )))
        }
    }
}

pub async fn get_detailed_user_stats(
    user_id: String,
    pool: String,
    period: String,
    limit: Option<u32>,
    offset: Option<u32>,
    datetime_aggregate: Option<String>,
) -> Result<DetailedUserStatistics> {
    #[derive(Serialize)]
    struct DetailedStatsRequest {
        user_id: String,
        pool: String,
        period: String,
        limit: Option<u32>,
        offset: Option<u32>,
        datetime_aggregate: Option<String>,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/get_detailed_stats", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .json(&DetailedStatsRequest {
            user_id,
            pool,
            period,
            limit,
            offset,
            datetime_aggregate,
        })
        .send()
        .await;

    log::debug!("detailed stats response: {:?}", response);

    match response {
        Ok(response) => match response.status() {
            reqwest::StatusCode::OK => {
                let response_text = response.text().await?;
                // println!("DETAILED_STATS: {}", response_text);
                let detailed_stats =
                  serde_json::from_str::<DetailedUserStatistics>(&response_text)?
                ;
                Ok(detailed_stats)
            }
            _ => {
                let status = response.status();
                let error_text = response.text().await.unwrap_or_default();
                Err(Report::msg(format!(
                    "Failed to get detailed user statistics: {}\n{}",
                    status,
                    prettify_json_or_text(&error_text)
                )))
            }
        },
        Err(e) => Err(Report::msg(format!(
            "Failed to get detailed user statistics: {:?}",
            e
        ))),
    }
}

pub async fn add_traffic_to_user(user_id: String, traffic_gb: i32, pool: String) -> Result<AddTrafficResponse> {
    #[derive(Serialize)]
    struct AddTrafficRequest {
        target_user_id: String,
        traffic_gb: i32,
        pool: String,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/add_traffic", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .json(&AddTrafficRequest {
            target_user_id: user_id,
            traffic_gb,
            pool,
        })
        .send()
        .await?;

    match response.status() {
        reqwest::StatusCode::OK => {
            Ok(response.json::<AddTrafficResponse>().await?)
        }
        reqwest::StatusCode::UNPROCESSABLE_ENTITY => {
            let error_text = response.text().await.unwrap_or_default();
            Err(Report::msg(format!("UNPROCESSABLE_ENTITY: {}", prettify_json_or_text(&error_text))))
        }
        _ => {
            let error_text = response.text().await.unwrap_or_default();
            Err(Report::msg(format!("Failed to add traffic: {}", prettify_json_or_text(&error_text))))
        }
    }
}

pub async fn drop_balance_user(user_id: String, pool: String) -> Result<()> {
    #[derive(Serialize)]
    struct DropBalanceRequest {
        user_id: String,
        pool: String,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/drop_balance", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .json(&DropBalanceRequest {
            user_id,
            pool,
        })
        .send()
        .await?;

    match response.status() {
        reqwest::StatusCode::OK | reqwest::StatusCode::NO_CONTENT => {
            Ok(())
        }
        reqwest::StatusCode::UNPROCESSABLE_ENTITY => {
            let error_text = response.text().await.unwrap_or_default();
            Err(Report::msg(format!("UNPROCESSABLE_ENTITY: {}", prettify_json_or_text(&error_text))))
        }
        _ => {
            let error_text = response.text().await.unwrap_or_default();
            Err(Report::msg(format!("Failed to drop balance: {}", prettify_json_or_text(&error_text))))
        }
    }
}

pub async fn update_pool_price(pool: String, new_price: f64, tg_id: i64) -> Result<()> {
    let user_id = get_test_user_id(tg_id);
    #[derive(Serialize)]
    struct UpdatePriceRequest {
        pool_type: String,
        new_price: f64,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/update_pool_price", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("X-Tg-Id", user_id)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .json(&UpdatePriceRequest {
            pool_type: pool,
            new_price,
        })
        .send()
        .await?;

    let status = response.status();

    match status {
        reqwest::StatusCode::OK | reqwest::StatusCode::NO_CONTENT => {
            Ok(())
        }
        reqwest::StatusCode::FORBIDDEN => {
            let error_text = response.text().await.unwrap_or_default();
            log::error!("Access denied when updating pool price: {}", prettify_json_or_text(&error_text));
            Err(Report::msg("Access denied: admin role required"))
        }
        reqwest::StatusCode::BAD_REQUEST => {
            let error_text = response.text().await.unwrap_or_default();
            log::error!("Bad request when updating pool price: {}", prettify_json_or_text(&error_text));
            Err(Report::msg(format!("Invalid request: {}", prettify_json_or_text(&error_text))))
        }
        _ => {
            let error_text = response.text().await.unwrap_or_default();
            log::error!("Failed to update pool price with status {}: {}", status, prettify_json_or_text(&error_text));
            Err(Report::msg(format!("Failed to update price: {}", prettify_json_or_text(&error_text))))
        }
    }
}

pub async fn get_current_price(pool_type: PoolType) -> Result<f64> {
    #[derive(Deserialize)]
    struct PricesResponse {
        residential: Option<f64>,
        residential_premium: Option<f64>,
        datacenter: Option<f64>,
        mobile: Option<f64>,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/prices", *BACKEND_API_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "vgate")
        .send()
        .await?;

    match response.status() {
        reqwest::StatusCode::OK => {
            let prices: PricesResponse = response.json().await?;
            let price = match pool_type {
                PoolType::Residential => prices.residential,
                PoolType::ResidentialPremium => prices.residential_premium,
                PoolType::Mobile => prices.mobile,
                PoolType::Datacenter => prices.datacenter,
            };
            
            price.ok_or_else(|| Report::msg("Price not set for this pool"))
        }
        _ => {
            let error_text = response.text().await.unwrap_or_default();
            log::error!("Failed to get current price: {}", prettify_json_or_text(&error_text));
            Err(Report::msg(format!("Failed to get current price: {}", prettify_json_or_text(&error_text))))
        }
    }
}

#[derive(Deserialize)]
pub struct AddTrafficResponse {
    pub success: bool,
    pub message: String,
    pub user_id: String,
    pub traffic_added_gb: i32,
    pub pool: String,
    pub balance_before: String,
    pub subuser_id: i32,
}

/// Get referral source links with usage statistics
pub async fn get_ref_link_details(ref_code: Option<String>) -> Result<RefLinkDetails> {
    #[derive(Serialize)]
    struct RefLinkDetailsRequest {
        ref_code: Option<String>,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/get_ref_link_details", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .json(&RefLinkDetailsRequest { ref_code })
        .send()
        .await;

    log::debug!("response: {:?}", response);

    match response {
        Ok(response) => match response.status() {
            reqwest::StatusCode::OK => {
                let json_data = response.json::<RefLinkDetails>().await?;
                Ok(json_data)
            }
            _ => {
                let error_text = response.text().await.unwrap_or_default();
                Err(Report::msg(format!(
                    "Failed to get ref link details: {}",
                    prettify_json_or_text(&error_text)
                )))
            }
        },
        Err(e) => Err(Report::msg(format!(
            "Failed to get ref link details: {:?}",
            e
        ))),
    }
}

pub async fn create_admin_ref(ref_code: String, display_name: String) -> Result<()> {
    #[derive(Serialize)]
    struct CreateAdminRefRequest {
        ref_code: String,
        display_name: String,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/create_admin_ref", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .json(&CreateAdminRefRequest { ref_code, display_name })
        .send()
        .await;

    log::debug!("response: {:?}", response);

    match response {
        Ok(response) => match response.status() {
            reqwest::StatusCode::OK | reqwest::StatusCode::NO_CONTENT => {
                Ok(())
            }
            reqwest::StatusCode::CONFLICT => {
                let error_text = response.text().await.unwrap_or_default();
                Err(Report::msg(format!("CONFLICT: {}", prettify_json_or_text(&error_text))))
            }
            _ => {
                let error_text = response.text().await.unwrap_or_default();
                Err(Report::msg(format!(
                    "Failed to create admin ref: {}",
                    prettify_json_or_text(&error_text)
                )))
            }
        },
        Err(e) => Err(Report::msg(format!(
            "Failed to create admin ref: {:?}",
            e
        ))),
    }
}

pub async fn broadcast_message(
    message: String,
    inline_buttons: Option<Vec<Vec<String>>>,
    users_without_purchases: bool,
) -> Result<()> {
    #[derive(Serialize)]
    struct BroadcastRequest {
        message: String,
        inline_buttons: Option<Vec<Vec<String>>>,
        users_without_purchases: bool,
        parse_mode: String,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/broadcast_message", *BACKEND_ADMIN_URL);
    log::trace!("fetching {url}", url = url.red());

    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .json(&BroadcastRequest {
            message,
            inline_buttons,
            users_without_purchases,
            parse_mode: "HTML".to_string(),
        })
        .send()
        .await?;

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

    Ok(())
}

pub async fn send_test_message(
    target_users: Vec<String>,
    message: String,
    inline_buttons: Option<Vec<Vec<String>>>,
) -> Result<()> {
    #[derive(Debug, Serialize)]
    struct SendMessageRequest {
        target_users: Vec<String>,
        message: String,
        inline_buttons: Option<Vec<Vec<String>>>,
        parse_mode: String,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/send_message", *BACKEND_ADMIN_URL);
    log::trace!("fetching {url}", url = url.red());

    let body = SendMessageRequest {
        target_users,
        message: message.clone(),
        inline_buttons,
        parse_mode: "HTML".to_string(),
    };

    log::info!("Sending test message with body: {:#?}", body);
    log::info!("Message content: {:?}", message);
    log::trace!("{}", format!("body: {:#?}", body).bright_black());

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

    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_default();
        log::error!("Failed to send test message: {}", prettify_json_or_text(&error_text));
        return Err(Report::msg(format!(
            "Failed to send test message: {}",
            prettify_json_or_text(&error_text)
        )));
    }

    Ok(())
}

pub async fn get_source_links() -> Result<Vec<SourceLink>> {
    let auth_token = get_auth_token().await?;

    let url = format!("{}/source_link", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .get(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .send()
        .await?;

    log::debug!("source_links response: {:?}", response);


    let status = response.status();
    match status {
        reqwest::StatusCode::OK => {
            let source_links = response.json::<Vec<SourceLink>>().await?;
            Ok(source_links)
        }
        _ => {
            let error_text = response.text().await.unwrap_or_default();
            Err(Report::msg(format!(
                "Failed to get source links: {}\n{}",
                status,
                prettify_json_or_text(&error_text)
            )))
        }
    }
}

pub async fn get_referral_percent() -> Result<f64> {
    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/get_referral_percent", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .send()
        .await;

    log::debug!("get_referral_percent response: {:?}", response);

    match response {
        Ok(response) => match response.status() {
            reqwest::StatusCode::OK => {
                let response_text = response.text().await?;
                log::debug!("get_referral_percent response body: {}", response_text);
                
                if let Ok(percent) = response_text.parse::<f64>() {
                    return Ok(percent);
                }
                
                if let Ok(percent) = serde_json::from_str::<f64>(&response_text) {
                    return Ok(percent);
                }
                
                Err(Report::msg(format!(
                    "Failed to parse referral percent from response: {}",
                    response_text
                )))
            }
            _ => {
                let error_text = response.text().await.unwrap_or_default();
                Err(Report::msg(format!(
                    "Failed to get referral percent: {}",
                    prettify_json_or_text(&error_text)
                )))
            }
        },
        Err(e) => Err(Report::msg(format!(
            "Failed to get referral percent: {:?}",
            e
        ))),
    }
}

pub async fn update_referral_percent(new_percent: f64) -> Result<()> {
    #[derive(Serialize)]
    struct UpdateReferralPercentRequest {
        new_percent: f64,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/update_referral_percent", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .json(&UpdateReferralPercentRequest { new_percent })
        .send()
        .await;

    log::debug!("update_referral_percent response: {:?}", response);

    match response {
        Ok(response) => match response.status() {
            reqwest::StatusCode::OK | reqwest::StatusCode::NO_CONTENT => {
                Ok(())
            }
            _ => {
                let error_text = response.text().await.unwrap_or_default();
                Err(Report::msg(format!(
                    "Failed to update referral percent: {}",
                    prettify_json_or_text(&error_text)
                )))
            }
        },
        Err(e) => Err(Report::msg(format!(
            "Failed to update referral percent: {:?}",
            e
        ))),
    }
}

pub async fn update_user_referral_percent(user_id: String, percent: f64) -> Result<()> {
    #[derive(Serialize)]
    struct UpdateUserReferralPercentRequest {
        user_id: String,
        percent: f64,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/update_user_referral_percent", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .json(&UpdateUserReferralPercentRequest { user_id: user_id.clone(), percent })
        .send()
        .await;

    log::debug!("update_user_referral_percent response: {:?}", response);

    match response {
        Ok(response) => match response.status() {
            reqwest::StatusCode::OK | reqwest::StatusCode::NO_CONTENT => {
                Ok(())
            }
            _ => {
                let error_text = response.text().await.unwrap_or_default();
                Err(Report::msg(format!(
                    "Failed to update user {} referral percent: {}",
                    user_id,
                    prettify_json_or_text(&error_text)
                )))
            }
        },
        Err(e) => Err(Report::msg(format!(
            "Failed to update user {} referral percent: {:?}",
            user_id, e
        ))),
    }
}

pub async fn get_all_payment_systems() -> Result<Vec<FullPaymentSystem>> {
    let auth_token = get_auth_token().await?;

    let url = format!("{}/available_pay", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .get(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .send()
        .await?;

    match response.status() {
        reqwest::StatusCode::OK => {
            let payment_systems = response.json::<Vec<FullPaymentSystem>>().await?;
            Ok(payment_systems)
        }
        _ => {
            let error_text = response.text().await.unwrap_or_default();
            Err(Report::msg(format!("Failed to get payment systems: {}", prettify_json_or_text(&error_text))))
        }
    }
}

pub async fn update_payment_system_availability(name: String, is_available: bool) -> Result<()> {
    #[derive(Serialize)]
    struct UpdatePaymentSystemRequest {
        name: String,
        is_available: String,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/available_pay?name=eq.{}", *BACKEND_API_URL, urlencoding::encode(&name));
    let response = reqwest::Client::new()
        .patch(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .json(&UpdatePaymentSystemRequest {
            name: name.clone(),
            is_available: if is_available { "true".to_string() } else { "false".to_string() },
        })
        .send()
        .await?;

    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_default();
        return Err(Report::msg(format!(
            "Failed to update payment system {}: {}",
            name,
            prettify_json_or_text(&error_text)
        )));
    }

    Ok(())
}

pub async fn update_payment_system_description(name: String, description: String) -> Result<()> {
    #[derive(Serialize)]
    struct UpdatePaymentSystemDescriptionRequest {
        description: String,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/available_pay?name=eq.{}", *BACKEND_API_URL, urlencoding::encode(&name));
    let response = reqwest::Client::new()
        .patch(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .json(&UpdatePaymentSystemDescriptionRequest {
            description,
        })
        .send()
        .await?;

    if !response.status().is_success() {
        let error_text = response.text().await.unwrap_or_default();
        return Err(Report::msg(format!(
            "Failed to update payment system description for {}: {}",
            name,
            prettify_json_or_text(&error_text)
        )));
    }

    Ok(())
}

pub async fn get_withdraw_requests(page: usize, limit: usize) -> Result<Vec<WithdrawRequest>> {
    let auth_token = get_auth_token().await?;
    let offset = page * limit;

    let url = format!("{}/withdraw_requests?offset={}&limit={}&order=created_at.desc", *BACKEND_ADMIN_URL, offset, limit);
    let response = reqwest::Client::new()
        .get(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .send()
        .await;

    log::debug!("get_withdraw_requests response: {:?}", response);

    match response {
        Ok(response) => match response.status() {
            reqwest::StatusCode::OK => {
                let withdraw_requests = response.json::<Vec<WithdrawRequest>>().await?;
                Ok(withdraw_requests)
            }
            _ => {
                let error_text = response.text().await.unwrap_or_default();
                Err(Report::msg(format!(
                    "Failed to get withdraw requests: {}",
                    prettify_json_or_text(&error_text)
                )))
            }
        },
        Err(e) => Err(Report::msg(format!(
            "Failed to get withdraw requests: {:?}",
            e
        ))),
    }
}

#[derive(Debug, Error)]
pub enum WithdrawRequestError {
    #[error("Insufficient balance for request {request_id}: {error_details}")]
    InsufficientBalance { request_id: i64, error_details: String },
    #[error("{message}")]
    Other { message: String },
}

impl From<ErrReport> for WithdrawRequestError {
    fn from(error: ErrReport) -> Self {
        WithdrawRequestError::Other { message: error.to_string() }
    }
}

impl From<reqwest::Error> for WithdrawRequestError {
    fn from(error: reqwest::Error) -> Self {
        WithdrawRequestError::Other { message: error.to_string() }
    }
}

pub async fn update_withdraw_request_status(request_id: i64, new_status: String, force: bool) -> Result<(), WithdrawRequestError> {
    #[derive(Serialize)]
    struct ProcessWithdrawRequest {
        request_id: i64,
        new_status: String,
        force: bool,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/process_withdraw_request", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .json(&ProcessWithdrawRequest { 
            request_id, 
            new_status: new_status.clone(),
            force
        })
        .send()
        .await?;

    match response.status() {
        reqwest::StatusCode::OK | reqwest::StatusCode::NO_CONTENT => {
            Ok(())
        }
        reqwest::StatusCode::CONFLICT => {
            let error_text = response.text().await.unwrap_or_default();
            Err(WithdrawRequestError::InsufficientBalance {
                request_id,
                error_details: error_text,
            })
        }
        _ => {
            let error_text = response.text().await.unwrap_or_default();
            Err(WithdrawRequestError::Other {
                message: format!(
                    "Failed to process withdraw request to {}: {}",
                    new_status,
                    prettify_json_or_text(&error_text)
                )
            })
        }
    }
}

pub async fn change_reseller_traffic(user_id: String, traffic_amount: i32) -> Result<(), WithdrawRequestError> {
    #[derive(Serialize)]
    struct ChangeTrafficRequest {
        user_id: String,
        traffic_amount: i32,
    }

    let auth_token = get_auth_token().await?;

    let url = format!("{}/rpc/change_reseller_traffic", *BACKEND_ADMIN_URL);
    let response = reqwest::Client::new()
        .post(&url)
        .header("Authorization", auth_token)
        .header("Content-Type", "application/json")
        .header("Content-Profile", "admin")
        .json(&ChangeTrafficRequest { 
            user_id,
            traffic_amount,
        })
        .send()
        .await?;

    match response.status() {
        reqwest::StatusCode::OK | reqwest::StatusCode::NO_CONTENT => {
            Ok(())
        }
        _ => {
            let error_text = response.text().await.unwrap_or_default();
            Err(WithdrawRequestError::Other {
                message: format!(
                    "Failed to change reseller traffic: {}",
                    prettify_json_or_text(&error_text)
                )
            })
        }
    }
}
