mod schema;
mod about_us;
mod promotion;
mod callback_handler;
mod api_menu;
mod merge_account;
pub mod profile;
pub mod get_proxy;
pub mod purchase;
pub mod default_proxy_settings;
pub use about_us::*;
pub use promotion::*;
pub use profile::*;
pub use get_proxy::*;
pub use purchase::*;
pub use default_proxy_settings::*;
pub use schema::*;
pub use callback_handler::*;
pub use api_menu::*;
pub use merge_account::*;

mod prelude {
    pub use crate::ui::prelude::*;
}

use self::prelude::*;
use captcha_rs::{Captcha, CaptchaBuilder};


#[derive(BotCommands, Clone)]
#[command(rename_rule = "snake_case")]
pub enum Command {
    Start(String),
    Clear,
    Online,
}

/// Helper function to invalidate profile cache in dialogue state
pub async fn invalidate_profile_cache(dialogue: &MasterDialogue) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
    if let Ok(Some(mut state)) = dialogue.get().await {
        state.invalidate_profile_cache();
        dialogue.update(state).await?;
    }
    Ok(())
}

/// Helper function to change language and invalidate profile cache
pub async fn change_language_and_invalidate_cache(dialogue: &MasterDialogue, tg_id: i64, language: Language) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
    api::change_language(tg_id, language).await?;
    
    invalidate_profile_cache(dialogue).await?;
    
    Ok(())
}

/// Helper function to get default proxy settings from cache or fetch if needed
pub async fn get_or_fetch_default_settings(dialogue: &MasterDialogue, tg_id: i64, pool: PoolType) -> Result<DefaultProxySettings, Box<dyn std::error::Error + Send + Sync + 'static>> {
    if let Ok(Some(mut state)) = dialogue.get().await {
        if let Some(cached_settings) = state.get_cached_default_settings() {
            log::trace!("Using cached default settings for pool: {}", pool);
            return Ok(cached_settings.clone());
        }
        
        log::trace!("Fetching fresh default settings for pool: {}", pool);
        let settings = api::get_default_proxy_settings(tg_id, pool).await?;
        
        state.update_default_settings(settings.clone());
        dialogue.update(state).await?;
        
        Ok(settings)
    } else {
        log::warn!("No dialogue state, fetching default settings for pool: {}", pool);
        let settings = api::get_default_proxy_settings(tg_id, pool).await?;
        
        let mut new_state = MasterState::new(UserStateKind::Start);
        new_state.update_default_settings(settings.clone());
        dialogue.update(new_state).await?;
        
        Ok(settings)
    }
}

/// Helper function to get countries from cache or fetch if needed
pub async fn get_or_fetch_countries(dialogue: &MasterDialogue, pool: PoolType) -> Result<Vec<Country>, Box<dyn std::error::Error + Send + Sync + 'static>> {
    if let Ok(Some(mut state)) = dialogue.get().await {
        if let Some(cached_countries) = state.get_cached_countries() {
            log::trace!("Using cached countries for pool: {}", pool);
            return Ok(cached_countries.clone());
        }
        
        log::trace!("{} fresh countries for pool: {}", "Fetching".red(), pool);
        let countries = api::get_countries_for_pool(pool).await?;
        
        state.set_cached_countries(countries.clone());
        dialogue.update(state).await?;
        
        Ok(countries)
    } else {
        log::warn!("No dialogue state, {} countries for pool: {}", "fetching".red(), pool);
        let countries = api::get_countries_for_pool(pool).await?;
        
        let mut new_state = MasterState::new(UserStateKind::Start);
        new_state.set_cached_countries(countries.clone());
        dialogue.update(new_state).await?;
        
        Ok(countries)
    }
}

/// Helper function to get cities from cache or fetch if needed
pub async fn get_or_fetch_cities(dialogue: &MasterDialogue, pool: PoolType, selected_countries: Vec<Country>, selected_states: Vec<State>) -> Result<Vec<City>, Box<dyn std::error::Error + Send + Sync + 'static>> {
    if let Ok(Some(mut state)) = dialogue.get().await {
        if let Some(cached_cities) = state.get_cached_cities() {
            log::trace!("Using cached cities for pool: {}", pool);
            return Ok(cached_cities.clone());
        }
        
        log::trace!("{} fresh cities for pool: {}", "Fetching".red(), pool);
        let cities = api::get_cities(pool, selected_countries, selected_states).await?;
        
        state.set_cached_cities(cities.clone());
        dialogue.update(state).await?;
        
        Ok(cities)
    } else {
        log::warn!("No dialogue state, {} cities for pool: {}", "fetching".red(), pool);
        let cities = api::get_cities(pool, selected_countries, selected_states).await?;
        
        let mut new_state = MasterState::new(UserStateKind::Start);
        new_state.set_cached_cities(cities.clone());
        dialogue.update(new_state).await?;
        
        Ok(cities)
    }
}

/// Helper function to get states from cache or fetch if needed
pub async fn get_or_fetch_states(dialogue: &MasterDialogue, pool: PoolType, selected_countries: Vec<Country>) -> Result<Vec<State>, Box<dyn std::error::Error + Send + Sync + 'static>> {
    if let Ok(Some(mut state)) = dialogue.get().await {
        if let Some(cached_states) = state.get_cached_states() {
            log::trace!("Using cached states for pool: {}", pool);
            return Ok(cached_states.clone());
        }
        
        log::trace!("{} fresh states for pool: {}", "Fetching".red(), pool);
        let states = api::get_states(pool, selected_countries).await?;
        
        state.set_cached_states(states.clone());
        dialogue.update(state).await?;
        
        Ok(states)
    } else {
        log::warn!("No dialogue state, {} states for pool: {}", "fetching".red(), pool);
        let states = api::get_states(pool, selected_countries).await?;
        
        let mut new_state = MasterState::new(UserStateKind::Start);
        new_state.set_cached_states(states.clone());
        dialogue.update(new_state).await?;
        
        Ok(states)
    }
}

/// Helper function to get zip codes from cache or fetch if needed
pub async fn get_or_fetch_zip_codes(dialogue: &MasterDialogue, pool: PoolType, selected_countries: Vec<Country>, selected_cities: Vec<City>, selected_states: Vec<State>) -> Result<Vec<ZipCode>, Box<dyn std::error::Error + Send + Sync + 'static>> {
    if let Ok(Some(mut state)) = dialogue.get().await {
        if let Some(cached_zip_codes) = state.get_cached_zip_codes() {
            log::trace!("Using cached zip codes for pool: {}", pool);
            return Ok(cached_zip_codes.clone());
        }
        
        log::trace!("{} fresh zip codes for pool: {}", "Fetching".red(), pool);
        let zip_codes = api::get_zip_codes(pool, selected_countries, selected_cities, selected_states).await?;
        
        state.set_cached_zip_codes(zip_codes.clone());
        dialogue.update(state).await?;
        
        Ok(zip_codes)
    } else {
        log::warn!("No dialogue state, {} zip codes for pool: {}", "fetching".red(), pool);
        let zip_codes = api::get_zip_codes(pool, selected_countries, selected_cities, selected_states).await?;
        
        let mut new_state = MasterState::new(UserStateKind::Start);
        new_state.set_cached_zip_codes(zip_codes.clone());
        dialogue.update(new_state).await?;
        
        Ok(zip_codes)
    }
}

/// Helper function to get ASNs from cache or fetch if needed
pub async fn get_or_fetch_asns(dialogue: &MasterDialogue, pool: PoolType, selected_countries: Vec<Country>, selected_cities: Vec<City>, selected_states: Vec<State>) -> Result<Vec<ASN>, Box<dyn std::error::Error + Send + Sync + 'static>> {
    if let Ok(Some(mut state)) = dialogue.get().await {
        if let Some(cached_asns) = state.get_cached_asns() {
            log::trace!("Using cached ASNs for pool: {}", pool);
            return Ok(cached_asns.clone());
        }
        
        log::trace!("{} fresh ASNs for pool: {}", "Fetching".red(), pool);
        let asns = api::get_asns(pool, selected_countries, selected_cities, selected_states).await?;
        
        state.set_cached_asns(asns.clone());
        dialogue.update(state).await?;
        
        Ok(asns)
    } else {
        log::warn!("No dialogue state, {} ASNs for pool: {}", "fetching".red(), pool);
        let asns = api::get_asns(pool, selected_countries, selected_cities, selected_states).await?;
        
        let mut new_state = MasterState::new(UserStateKind::Start);
        new_state.set_cached_asns(asns.clone());
        dialogue.update(new_state).await?;
        
        Ok(asns)
    }
}

/// Helper function to update dialogue state while preserving cached profile
pub async fn update_state_preserving_cache(dialogue: &MasterDialogue, new_kind: UserStateKind) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
    if let Ok(Some(mut current_state)) = dialogue.get().await {
        log::trace!("{}", format!("Updating state to {:?} while preserving cache (has_cache: {})", new_kind, current_state.cached_profile.is_some()).bright_black());
        current_state.user_kind = new_kind;
        dialogue.update(current_state).await?;
    } else {
        log::warn!("Creating new state {:?} (no existing state to preserve)", new_kind);
        dialogue.update(MasterState::new(new_kind)).await?;
    }
    Ok(())
}

/// Helper function to check if an IP address is private/local
fn is_private_ip(ip: &str) -> bool {
    use std::net::Ipv4Addr;
    
    let ip_addr = if ip.contains('/') {
        ip.split('/').next().unwrap_or(ip)
    } else {
        ip
    };
    
    if let Ok(addr) = ip_addr.parse::<Ipv4Addr>() {
        return addr.is_private() || addr.is_loopback() || addr.is_link_local() || addr.is_multicast();
    }
    
    false
}

async fn terms_handler(bot: Bot, msg: Message, settings: HandlerSettings) -> HandlerResult {
    let ln = settings.language;

    let message = t8x!(Ru, "С правилами можно ознакомиться ниже", &ln);
    
    let rules_url = reqwest::Url::parse(&format!("https://{}/rules", *WEBSITE_DOMAIN))?;
    let terms_url = reqwest::Url::parse(&format!("https://{}/terms", *WEBSITE_DOMAIN))?;
    let privacy_url = reqwest::Url::parse(&format!("https://{}/privacy", *WEBSITE_DOMAIN))?;
    
    let keyboard = InlineKeyboardMarkup::new(vec![
        vec![InlineKeyboardButton::url(
            t8x!(Ru, "📖 Правила", &ln),
            rules_url,
        )],
        vec![InlineKeyboardButton::url(
            t8x!(Ru, "📋 Пользовательское соглашение", &ln),
            terms_url,
        )],
        vec![InlineKeyboardButton::url(
            t8x!(Ru, "🔒 Политика конфиденциальности", &ln),
            privacy_url,
        )],
    ]);
    
    bot.send_message(msg.chat.id, message)
        .reply_markup(keyboard)
        .await?;
    Ok(())
}

async fn faq_handler(bot: Bot, msg: Message, settings: HandlerSettings) -> HandlerResult {
    let ln = settings.language;

    let message = t8x!(Ru, r#"
❓<b>Часто задаваемые вопросы:</b>

<b>Какие прокси выбрать?</b>
<blockquote expandable>Серверные прокси: Самые дешёвые. Выдают IP-адреса облачных серверов (Amazon AWS, Google Cloud и т.д.). Легко определяются и блокируются сайтами, так как все знают, что это не настоящие пользователи. Подходят для простых задач.

Резидентские прокси (ISP-прокси): Дороже. Выдают IP-адреса реальных интернет-провайдеров (как у домашних пользователей). Максимально похожи на трафик обычного человека, поэтому их сложнее обнаружить и заблокировать. Лучший выбор для обхода сложных блокировок.

Мобильные прокси: Самые дорогие и качественные. Выдают IP-адреса мобильных операторов (как будто вы выходите в интернет с телефона через 4G/5G). Самый высокий уровень доверия со стороны сайтов, так как этот пул IP-адресов почти никогда не используется ботами. Идеальны для самых сложных задач</blockquote>

<b>Чем отличаются premium-прокси?</b>
<blockquote expandable>Главное отличие "обычных" и премиум-прокси - это их скорость и задержка. Также премиум-пул включает бесплатный дополнительный таргетинг</blockquote>

<b>Какой регион серверов выбрать?</b>
<blockquote expandable>Выбирайте ближайший к выбранной стране или авто чтобы обеспечивать максимальную скорость и низкую задержку.
Если вы находитесь в РФ вам подойдут Япония, Сингапур или Авто</blockquote>

<b>Какой протокол выбрать?</b>
<blockquote expandable>HTTP-прокси предназначен только для веб-трафика (HTTP/HTTPS) и оптимизирован для браузеров, предлагая кеширование и фильтрацию, тогда как SOCKS5 — универсальный протокол, работающий на уровне сессий, поддерживающий любые типы трафика (включая UDP, FTP) и обеспечивающий лучшую анонимность и производительность для общих задач, но с меньшей совместимостью с браузерами</blockquote>

<b>Ротационные или статичные прокси?</b>
<blockquote expandable>Ротационные прокси имеют ротацию на каждый запрос поэтому выдаётся только 1 прокси, то есть каждый раз когда вы обновляете страницу в браузере будет меняться ip.
Статичные прокси имеют ротацию по таймеру до 120 минут и отличается порт, доступные порты от 10000 до 20000.</blockquote>

<b>Что такое анонимность?</b>
<blockquote expandable>С включенным анон-фильтром вы получите больше чистых прокси, которые нигде не блокируются. Также обратите внимание, что при включении этого фильтра ваш пул IP-адресов будет уменьшен.</blockquote>

<b>Что такое ASN?</b>
<blockquote expandable>ASN — это номер, который идентифицирует конкретного провайдера (ISP)</blockquote>

<b>Как подключится через whitelist?</b>
<blockquote expandable>Чтобы подключится через whitelist необходимо:
1. Перейти в профиль → управление прокси
2. Добавляем свой ip в whitelist
3. Переходим в настройки whitelist в раздел управление прокси и настраиваем
4. Переходим в панель управления и генерируем статичные или ротационные прокси в формате host:port
5. Готово! Можете подключаться без логина и пароля.</blockquote>
    "#, &ln);
    
    let buttons = vec![(t8x!(Ru, "🏠В главное меню🏠", &ln), Callback::DeleteThisMessage).into()];
    let keyboard = make_inline_keyboard(buttons, 1);
    
    bot.send_message(msg.chat.id, message)
        .parse_mode(teloxide::types::ParseMode::Html)
        .reply_markup(keyboard)
        .await?;
    Ok(())
}

async fn blog_handler(bot: Bot, msg: Message, settings: HandlerSettings) -> HandlerResult {
    let ln = settings.language;

    let blog_url = reqwest::Url::parse(&format!("https://{}/blog", *WEBSITE_DOMAIN))?;
    
    let keyboard = InlineKeyboardMarkup::new(vec![
        vec![InlineKeyboardButton::url(
            t8x!(Ru, "📝 Перейти в блог", &ln),
            blog_url,
        )],
        vec![InlineKeyboardButton::callback(
            t8x!(Ru, "🏠В главное меню🏠", &ln),
            Callback::DeleteThisMessage.to_string(),
        )],
    ]);
    
    let message = t8x!(Ru, "📝 <b>Наш блог</b>\n\nЗдесь вы найдете полезные статьи о прокси, новости и обновления сервиса.", &ln);
    
    bot.send_message(msg.chat.id, message)
        .parse_mode(teloxide::types::ParseMode::Html)
        .reply_markup(keyboard)
        .await?;
    Ok(())
}

async fn captcha_check_handler(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    start: String,
    settings: HandlerSettings,
) -> HandlerResult {
    log::info!("capcha_check_handler text: {}", start);
    let ln = settings.language;
    let captcha = create_captcha();
    let mut bytes: Vec<u8> = Vec::new();
    let mut cursor = std::io::Cursor::new(&mut bytes);
    match captcha
        .image
        .write_to(&mut cursor, image::ImageOutputFormat::Png)
    {
        Ok(_) => {
            let image = InputFile::memory(bytes).file_name("captcha.png");

            let captcha_buttons_count = 9;
            let mut buttons = Vec::new();
            
            buttons.push(captcha.text.clone());
            
            for _ in 0..(captcha_buttons_count - 1) {
                let fake_captcha: String = (0..5)
                    .map(|_| {
                        let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
                        chars.chars().nth(rand::random::<usize>() % chars.len()).unwrap()
                    })
                    .collect();
                buttons.push(fake_captcha);
            }
            
            use rand::seq::SliceRandom;
            buttons.shuffle(&mut rand::thread_rng());
            
            let inline_buttons: Vec<InlineButton<Callback>> = buttons
                .into_iter()
                .map(|button_text| {
                    (
                        button_text.clone(),
                        Callback::CaptchaAnswer { value: button_text },
                    )
                        .into()
                })
                .collect();

            let keyboard = make_inline_keyboard(inline_buttons, 3);

            bot.send_photo(msg.chat.id, image)
                .caption(t8x!(Ru, "Пожалуйста, решите капчу, чтобы мы убедились, что вы не робот 🤝", &ln))
                .reply_markup(keyboard)
                .await?;

            update_state_preserving_cache(&dialogue, UserStateKind::ReceiveCaptcha {
                start,
                captcha_text: captcha.text,
            }).await?;
        }
        Err(e) => {
            log::error!("{}", e);
            return error_handler(bot, dialogue, msg, settings).await;
        }
    }
    Ok(())
}

// 💩

pub async fn user_entrypoint(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    start: String,
    settings: HandlerSettings,
) -> HandlerResult {
    dialogue.update_kind(MasterStateKind::User).await?;

    if api::tg_user_exists(msg.chat.id.0).await? {
        start_message_handler(bot.clone(), dialogue.clone(), msg.clone(), settings)
            .await?;
    } else {
        captcha_check_handler(bot.clone(), dialogue.clone(), msg.clone(), start, settings)
            .await?;
    }

    Ok(())
}

pub async fn online_command_handler(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    settings: HandlerSettings,
) -> HandlerResult {
    dialogue.update_kind(MasterStateKind::User).await?;

    if !api::tg_user_exists(msg.chat.id.0).await? {
        // If user doesn't exist, redirect to start
        start_message_handler(bot.clone(), dialogue.clone(), msg.clone(), settings)
            .await?;
        return Ok(());
    }

    // Show pool selection for online command
    select_pool_handler_constructor(
        bot,
        msg,
        |pool| Callback::Online { pool }.to_string(),
        Some(Callback::BackFromOnline.to_string()),
        SelectPoolHandlerSettings::from(settings.force_behavior(ForceHandlerBehavior::DeleteMessage))
    ).await?;

    Ok(())
}

pub async fn start_message_handler(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;

    let text = t8x!(Ru, r#"
        🌐Премиальные резидентские, мобильные и серверные прокси для ваших задач.  Выбирайте и работайте с лучшими прокси-серверами в любых проектах.

        Подписывайтесь на наш <a href="{channel_link}">канал</a> чтобы следить за скидками и акциями
    "#, &ln);

    let keyboard = make_menu_keyboard(ln);

    bot.send_message(
        msg.chat.id,
        strfmt!(
            text,
            channel_link => (*NEWS_CHANNEL_LINK).clone()
        )?,
    )
    .link_preview_options(no_preview())
    .reply_markup(keyboard)
    .await?;
    update_state_preserving_cache(&dialogue, UserStateKind::UserMenu).await?;

    Ok(())
}

async fn send_pool_online(
    bot: Bot,
    msg: Message,
    pool: PoolType,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;

    match api::get_pool_stats(pool).await {
        Ok(mut stats) => {
            let mut message = t8x!(Ru, "<b>Количество IP онлайн по странам:</b>\n", &ln).to_string();
            stats.sort_by(|a, b| b.count.cmp(&a.count));
            for stat in stats {
                message.push_str(&format!(
                    "{}: {}\n",
                    stat.name, stat.count,
                ));
            }
            let buttons =
                vec![(t8x!(Ru, "🏠В главное меню🏠", &ln), Callback::DeleteThisMessage).into()];
            let keyboard = make_inline_keyboard(buttons, 1);
            bot.edit_message_text(msg.chat.id, msg.id, message)
                .reply_markup(keyboard)
                .await?;
        }
        Err(error) => {
            log::error!("Failed to get pool stats: {}", error);
            let message = t8x!(Ru, "Не удалось получить данные", &ln).to_string();
            bot.edit_message_text(msg.chat.id, msg.id, message).await?;
        }
    }
    Ok(())
}

async fn referral_handler(bot: Bot, dialogue: MasterDialogue, msg: Message, settings: HandlerSettings) -> HandlerResult {
    let ln = settings.language;

    match api::get_referral_info(msg.chat.id.0).await {
        Ok(info) => {
            let message = strfmt!(
                t8x!(Ru, r#"
                    💯Реферальный процент: {percent} %
                    📎Ваши реферальные ссылки: <a href="{web_link}">сайт</a> <a href="{bot_link}">телеграм</a>
                    ⚖️Количество ваших рефералов: {referrals_count}
                    🤑Реферальный счёт: {referral_balance}
                "#, &ln),
                percent => info.percent,
                web_link => info.links.website,
                bot_link => info.links.telegram,
                referrals_count => info.count,
                referral_balance => info.balance
            )?;
            let buttons = vec![
                (t8x!(Ru, "Запросить вывод", &ln), Callback::WithdrawRequest).into(),
                (t8x!(Ru, "🔙Назад🔙", &ln), Callback::BackToProfile).into(),
            ];
            let keyboard = make_inline_keyboard(buttons, 1);

            bot.send_message(msg.chat.id, message)
                .link_preview_options(no_preview())
                .reply_markup(keyboard)
                .await?;
        }
        Err(e) => {
            log::error!("{}", e);
            return error_handler(bot, dialogue, msg, settings).await;
        }
    }
    Ok(())
}

async fn activate_promo(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;

    let message = t8x!(Ru, "🎁 <b>Введите промокод для активации трафика</b>:", &ln);
    let keyboard = make_inline_keyboard(
        vec![(t8x!(Ru, "🔙Назад🔙", &ln), Callback::BackToProfile).into()],
        1,
    );
    
    bot.edit_message_text(msg.chat.id, msg.id, message)
        .reply_markup(keyboard)
        .await?;
    update_state_preserving_cache(&dialogue, UserStateKind::ReceivePromo).await?;
    Ok(())
}

async fn receive_promo(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    settings: HandlerSettings,
    profile: Profile,
) -> HandlerResult {
    let ln = settings.language;

    let Some(promo_code) = msg.text() else {
        log::debug!("Message is None");
        return Ok(());
    };

    let promo_code = promo_code.trim();
    
    let loading_msg = bot.send_message(
        msg.chat.id,
        t8x!(Ru, "⏳ Проверяем промокод...", &ln),
    ).await?;

    match api::activate_traffic_promocode(msg.chat.id.0, promo_code).await {
        Ok(()) => {
            bot.delete_message(msg.chat.id, loading_msg.id).await?;
            
            let success_message = t8x!(Ru, "🎉 Вы успешно активировали промокод! Трафик добавлен на ваш баланс.", &ln);
            
            let success_msg = bot.send_message(msg.chat.id, success_message).await?;
            
            tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
            bot.delete_message(msg.chat.id, success_msg.id).await?;

            if let Err(e) = invalidate_profile_cache(&dialogue).await {
                log::warn!("Failed to invalidate profile cache: {}", e);
            }
            
            refresh_profile_handler(bot, dialogue, msg, settings, profile).await?;
        }
        Err(api::PromoError::PromoNotExists) => {
            bot.delete_message(msg.chat.id, loading_msg.id).await?;
            
            let keyboard = make_inline_keyboard(
                vec![(t8x!(Ru, "🔙Назад🔙", &ln), Callback::BackToProfile).into()],
                1,
            );
            
            bot.send_message(
                msg.chat.id,
                t8x!(Ru, "❌ Промокод недействительный или истёк! Попробуйте ещё раз или отмените операцию кнопкой ниже", &ln),
            ).reply_markup(keyboard).await?;
        }
        Err(api::PromoError::PromoAlreadyUsed) => {
            bot.delete_message(msg.chat.id, loading_msg.id).await?;
            
            let keyboard = make_inline_keyboard(
                vec![(t8x!(Ru, "🔙Назад🔙", &ln), Callback::BackToProfile).into()],
                1,
            );
            
            bot.send_message(
                msg.chat.id,
                t8x!(Ru, "❌ Вы уже активировали этот промокод!", &ln),
            ).reply_markup(keyboard).await?;
        }
        Err(api::PromoError::Other(error)) => {
            bot.delete_message(msg.chat.id, loading_msg.id).await?;
            
            log::error!("Failed to activate promo code: {}", error);
            
            let keyboard = make_inline_keyboard(
                vec![(t8x!(Ru, "🔙Назад🔙", &ln), Callback::BackToProfile).into()],
                1,
            );
            
            bot.send_message(
                msg.chat.id,
                t8x!(Ru, "❌ Произошла ошибка при активации промокода. Попробуйте позже.", &ln),
            ).reply_markup(keyboard).await?;
        }
        _ => {
            bot.delete_message(msg.chat.id, loading_msg.id).await?;
            
            let keyboard = make_inline_keyboard(
                vec![(t8x!(Ru, "🔙Назад🔙", &ln), Callback::BackToProfile).into()],
                1,
            );
            
            bot.send_message(
                msg.chat.id,
                t8x!(Ru, "❌ Произошла неизвестная ошибка. Попробуйте позже.", &ln),
            ).reply_markup(keyboard).await?;
        }
    }

    Ok(())
}

async fn receive_email(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    profile: Profile,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;
    
    let Some(email) = msg.text() else {
        bot.send_message(msg.chat.id, t8x!(Ru, "Пожалуйста, отправьте email в текстовом сообщении", &ln))
            .await?;
        return Ok(());
    };
    
    let email = email.trim();
    
    let email_regex = regex::Regex::new(r"^[^\s@]+@[^\s@]+\.[^\s@]+$").unwrap();
    if !email_regex.is_match(email) {
        bot.send_message(msg.chat.id, t8x!(Ru, "Пожалуйста, введите корректный email адрес", &ln))
            .await?;
        return Ok(());
    }
    
    bot.delete_message(msg.chat.id, msg.id).await?;

    let buttons =
        vec![(t8x!(Ru, "🔙Назад🔙", &ln), Callback::BackToProfile).into()];
    let keyboard = make_inline_keyboard(buttons, 1);
    
    if profile.email == Some(email.to_string()) {
        bot.send_message(
            msg.chat.id,
            t8x!(Ru, "Эта почта уже привязана к вашему аккаунту.", &ln)
        ).reply_markup(keyboard).await?;
    } else {
        match api::request_email_confirmation(msg.chat.id.0, email).await {
            Ok(_) => {
                bot.send_message(
                    msg.chat.id,
                    t8x!(Ru, "Почта успешно привязана к вашему аккаунту! Пожалуйста, проверьте вашу почту для подтверждения.", &ln)
                ).reply_markup(keyboard).await?;
            }
            Err(api::EmailError::EmailAlreadyUsed) => {
                bot.send_message(
                    msg.chat.id,
                    t8x!(Ru, "Эта почта уже привязана к другому аккаунту. Пожалуйста, используйте другую почту.", &ln)
                ).await?;
            }
            Err(api::EmailError::EmailAlreadyConnected) => {
                bot.send_message(
                    msg.chat.id,
                    t8x!(Ru, "У вас уже есть привязанная почта.", &ln)
                ).reply_markup(keyboard).await?;
                
                profile_handler(bot, dialogue, msg, profile, settings).await?;
            }
            Err(api::EmailError::Other(e)) => {
                log::error!("Failed to request email confirmation: {}", e);
                bot.send_message(
                    msg.chat.id,
                    t8x!(Ru, "Произошла ошибка при привязке почты. Пожалуйста, попробуйте позже.", &ln)
                ).reply_markup(keyboard).await?;
            }
        }
    }
    Ok(())
}

async fn receive_withdraw_amount(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    settings: HandlerSettings,
    profile: Profile,
) -> HandlerResult {
    let ln = settings.language;
    
    let Some(amount_text) = msg.text() else {
        bot.send_message(msg.chat.id, t8x!(Ru, "Пожалуйста, отправьте сумму в числовом формате", &ln))
            .await?;
        return Ok(());
    };
    
    let amount_text = amount_text.trim().replace(',', ".");
    
    let amount = match amount_text.parse::<f64>() {
        Ok(amount) if amount > 0.0 => amount,
        _ => {
            bot.send_message(msg.chat.id, t8x!(Ru, "Пожалуйста, введите корректную сумму больше 0", &ln))
                .await?;
            return Ok(());
        }
    };
    
    if amount > profile.referral_balance {
        let insufficient_balance_msg = strfmt!(
            t8x!(Ru, "❌ Недостаточно средств на реферальном счете!\n\n💰 Ваш баланс: ${available_balance:.2}\n💸 Запрашиваемая сумма: ${requested_amount:.2}\n\n✅ Максимальная сумма для вывода: ${max_amount:.2}", &ln),
            available_balance => profile.referral_balance,
            requested_amount => amount,
            max_amount => profile.referral_balance
        )?;
        
        let back_button = InlineKeyboardButton::callback(
            t8x!(Ru, "🔙Назад🔙", &ln),
            Callback::BackToProfile.to_string()
        );
        let keyboard = InlineKeyboardMarkup::new(vec![vec![back_button]]);
        
        bot.send_message(msg.chat.id, insufficient_balance_msg)
            .reply_markup(keyboard)
            .await?;
        return Ok(());
    }
    
    bot.delete_message(msg.chat.id, msg.id).await?;

    let confirmation_msg = strfmt!(
        t8x!(Ru, "💰 Сумма подтверждена: ${amount:.2}\n\n💳 Теперь укажите реквизиты для вывода средств:\n• Номер карты\n• Банк\n• Или другие платежные данные\n\n📝 Отправьте текст с реквизитами:", &ln),
        amount => amount
    )?;
    
    bot.send_message(msg.chat.id, confirmation_msg)
        .reply_markup(make_inline_keyboard(vec![(t8x!(Ru, "🔙Назад🔙", &ln), Callback::BackToProfile).into()], 1))
        .await?;
    
    update_state_preserving_cache(&dialogue, UserStateKind::ReceiveWithdrawComment { amount }).await?;
    
    Ok(())
}

async fn receive_withdraw_comment(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    settings: HandlerSettings,
    amount: f64,
) -> HandlerResult {
    let ln = settings.language;

    let chat_id = msg.chat.id;
    let message_id = msg.id;
    
    let Some(comment) = msg.text() else {
        bot.send_message(chat_id, t8x!(Ru, "Пожалуйста, отправьте текст с реквизитами для вывода", &ln))
            .await?;
        return Ok(());
    };
    
    let comment = comment.trim();
    if comment.is_empty() {
        bot.send_message(chat_id, t8x!(Ru, "Комментарий не может быть пустым. Укажите реквизиты для вывода средств.", &ln))
            .await?;
        return Ok(());
    }
    
    bot.delete_message(chat_id, message_id).await?;

    let comment_owned = comment.to_string();
    
    update_state_preserving_cache(&dialogue, UserStateKind::ConfirmWithdraw { amount, comment: comment_owned.clone() }).await?;
    
    let buttons = vec![
        (t8x!(Ru, "✅ Подтвердить", &ln), Callback::ConfirmWithdraw { amount }).into(),
        (t8x!(Ru, "🔙Назад🔙", &ln), Callback::BackToProfile).into(),
    ];
    let keyboard = make_inline_keyboard(buttons, 1);
    
    let confirmation_msg = strfmt!(
        t8x!(Ru, "💰 Подтверждение вывода средств\n\n💵 Сумма: ${amount:.2}\n💳 Реквизиты: {comment}", &ln),
        amount => amount,
        comment => comment_owned
    )?;
    
    bot.send_message(chat_id, confirmation_msg)
        .reply_markup(keyboard)
        .await?;
    
    Ok(())
}

async fn confirm_withdraw(
    bot: Bot,
    msg: Message,
    _dialogue: MasterDialogue,
    settings: HandlerSettings,
    (amount, comment): (f64, String),
) -> HandlerResult {
    let ln = settings.language;
    
    let confirmation_msg = strfmt!(
        t8x!(Ru, "💰 Подтверждение вывода средств\n\n💵 Сумма: ${amount:.2}\n💳 Реквизиты: {comment}\n\n✅ Нажмите кнопку подтверждения для создания заявки", &ln),
        amount => amount,
        comment => comment
    )?;
    
    let buttons = vec![
        (t8x!(Ru, "✅ Подтвердить", &ln), Callback::ConfirmWithdraw { amount }).into(),
        (t8x!(Ru, "🔙Назад🔙", &ln), Callback::BackToProfile).into(),
    ];
    let keyboard = make_inline_keyboard(buttons, 1);
    
    bot.send_message(msg.chat.id, confirmation_msg)
        .reply_markup(keyboard)
        .await?;
    
    Ok(())
}

pub fn make_menu_keyboard(ln: Language) -> KeyboardMarkup {
    let menu_buttons = vec![
        t8x!(Ru, "⚙️Панель управления⚙️",            &ln, "menu-button"),
        t8x!(Ru, "👤Профиль👤",                    &ln, "menu-button"),
        t8x!(Ru, "ℹ️О насℹ️",                      &ln, "menu-button"),
        //t8x!(Ru, "📝Блог📝",                       &ln, "menu-button"),
        t8x!(Ru, "❓FAQ❓",                         &ln, "menu-button"),
        t8x!(Ru, "📖Условия и правила📖",          &ln, "menu-button"),
    ];

    make_keyboard(menu_buttons, 2)
}

fn create_captcha() -> Captcha {
    CaptchaBuilder::new()
        .length(5)
        .width(130)
        .height(40)
        .dark_mode(false)
        .complexity(1)
        .compression(40)
        .build()
}

async fn website_login_handler(bot: Bot, msg: Message, settings: HandlerSettings) -> HandlerResult {
    let ln = settings.language;

    match api::create_tgcode(msg.chat.id.0).await {
        Ok(code) => {
            let message = strfmt!(t8x!(Ru, r#"
                   🔑<b>Ваш код</b>: <code>{code}</code>
                   ⏳<b>Действителен в течение 5 минут.</b>
                   🚀<b>Используйте его для авторизации через Telegram на нашем</b> <a href="{website_url}">сайте</a>
                "#, &ln),
                code => code,
                website_url => (*WEBSITE_LINK).clone()
            )?;

            let keyboard = make_inline_keyboard(
                vec![(t8x!(Ru, "🔙Назад🔙", &ln), Callback::BackToProfile).into()],
                1,
            );

            bot.edit_message_text(msg.chat.id, msg.id, message)
                .reply_markup(keyboard)
                .await?;
        }
        Err(e) => {
            log::error!("Failed to create tgcode: {}", e);
            let error_message = t8x!(Ru, "Не удалось создать код для входа. Попробуйте позже.", &ln);
            bot.send_message(msg.chat.id, error_message).await?;
        }
    }
    Ok(())
}

async fn show_language_selection(
    bot: Bot,
    dialogue: MasterDialogue, 
    msg: Message, 
    referral: String, 
    _settings: HandlerSettings
) -> HandlerResult {
    log::trace!("{}", format!("show_language_selection text: {}", referral).bright_black());
    let message_text = "🇷🇺Выберите язык/Choose the language🇺🇸";
    let keyboard = make_language_selection_keyboard();

    bot.send_message(msg.chat.id, message_text)
        .reply_markup(keyboard)
        .await?;

    if !referral.is_empty() {
        update_state_preserving_cache(&dialogue, UserStateKind::ReceiveLanguage { referral: Some(referral) }).await?;
    } else {
        update_state_preserving_cache(&dialogue, UserStateKind::ReceiveLanguage { referral: None }).await?;
    }
        
    Ok(())
}

fn make_language_selection_keyboard() -> InlineKeyboardMarkup {
    let buttons: Vec<InlineButton<Callback>> = vec![
        ("🇺🇸English", Callback::SelectLanguage { language: Language::En }).into(),
        ("🇷🇺Русский", Callback::SelectLanguage { language: Language::Ru }).into(),
    ];

    make_inline_keyboard(buttons, 2)
}

async fn manage_proxy_handler(
    bot: Bot,
    msg: Message,
    settings: HandlerSettings,
    profile: Profile,
) -> HandlerResult {
    let pools = profile.pools.keys().cloned().collect::<Vec<_>>();

    select_pool_handler_constructor(
        bot,
        msg,
        |pool| Callback::SelectPoolForManagement { pool }.to_string(),
        Some(Callback::BackToProfile.to_string()),
        SelectPoolHandlerSettings::from(settings.force_behavior(ForceHandlerBehavior::EditMessage))
            .with_message(t8x!("Choose a pool to manage:", &settings.language))
            .include_pools(pools)
    ).await?;

    Ok(())
}

async fn proxy_management_menu_handler(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    pool: PoolType,
    settings: HandlerSettings,
    profile: Profile,
) -> HandlerResult {
    let ln = settings.language;

    let pool_name = pool.display_name(&ln);
    let message = strfmt!(t8x!(Ru, r#"
            ⚙️ Управление пулом: {pool_name}
            🔐 Пароль: <code>{password}</code>
        
            Выберите действие:
        "#, &ln),
        pool_name,
        password => profile.pools.get(&pool).unwrap().get_pool_info().password.unwrap()
    )?;

    let buttons: Vec<InlineButton<Callback>> = vec![
        (t8x!(Ru, "🔐 Сменить пароль 🔐", &ln), Callback::ChangePasswordForPool { pool }).into(),
        (t8x!(Ru, "📊 Просмотреть статистику 📊", &ln), Callback::CheckStatsForPool { pool }).into(),
        (t8x!(Ru, "⛩️ Ip whitelist ⛩️", &ln), Callback::ManageWhitelistForPool { pool }).into(),
        (t8x!(Ru, "⚙️ Настройки Whitelist ⚙️", &ln), Callback::ManageDefaultProxySettings { pool }).into(),
        (t8x!(Ru, "🔙Назад🔙", &ln), Callback::ManageProxy).into(),
    ];

    let keyboard = make_inline_keyboard(buttons, 1);

    bot.edit_message_text(msg.chat.id, msg.id, message)
        .reply_markup(keyboard)
        .await?;

    update_state_preserving_cache(&dialogue, UserStateKind::ProxyManagement { selected_pool: pool }).await?;

    Ok(())
}

async fn change_password_for_pool_handler(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    pool: PoolType,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;

    match api::reset_password(msg.chat.id.0, pool).await {
        Ok(new_password) => {
            if let Err(e) = invalidate_profile_cache(&dialogue).await {
                log::warn!("Failed to invalidate profile cache: {}", e);
            }
            
            let message_text = strfmt!(t8x!(Ru, r#"
                    🔐 Пароль для пула {pool_name} успешно изменен!
                    
                    🔑 Новый пароль: <code>{new_password}</code>
                "#, &ln),
                pool_name => pool.display_name(&ln),
                new_password
            )?;
            
            let keyboard = make_inline_keyboard(
                vec![(t8x!(Ru, "🔙Назад к управлению🔙", &ln), Callback::SelectPoolForManagement { pool }).into()],
                1,
            );
            
            bot.edit_message_text(msg.chat.id, msg.id, message_text)
                .parse_mode(teloxide::types::ParseMode::Html)
                .reply_markup(keyboard)
                .await?;
        }
        Err(e) => {
            log::error!("Failed to reset password: {}", e);
            let error_message = t8x!(Ru, "Не удалось сменить пароль. Попробуйте позже.", &ln);
            let keyboard = make_inline_keyboard(
                vec![(t8x!(Ru, "🔙Назад к управлению🔙", &ln), Callback::SelectPoolForManagement { pool }).into()],
                1,
            );
            
            bot.edit_message_text(msg.chat.id, msg.id, error_message)
                .reply_markup(keyboard)
                .await?;
        }
    }

    Ok(())
}

async fn check_stats_for_pool_handler(
    bot: Bot,
    msg: Message,
    pool: PoolType,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;

    let message = strfmt!(t8x!(Ru, r#"
            📊 Статистика для пула {pool_name}
            
            📋 Выберите период:
        "#, &ln),
        pool_name => pool.display_name(&ln)
    )?;

    let buttons = vec![
        (t8x!(Ru, "📅 За неделю 📅", &ln), Callback::CheckStatsWeekForPool { pool }).into(),
        (t8x!(Ru, "📆 За месяц 📆", &ln), Callback::CheckStatsMonthForPool { pool }).into(),
        (t8x!(Ru, "🔙Назад к управлению🔙", &ln), Callback::SelectPoolForManagement { pool }).into(),
    ];
    let keyboard = make_inline_keyboard(buttons, 1);
    
    bot.edit_message_text(msg.chat.id, msg.id, message)
        .reply_markup(keyboard)
        .await?;
    
    Ok(())
}

async fn check_stats_week_for_pool_handler(
    bot: Bot,
    msg: Message,
    pool: PoolType,
    settings: HandlerSettings,
) -> HandlerResult {
    show_user_statistics(bot, msg, pool, Period::Week, settings).await
}

async fn check_stats_month_for_pool_handler(
    bot: Bot,
    msg: Message,
    pool: PoolType,
    settings: HandlerSettings,
) -> HandlerResult {
    show_user_statistics(bot, msg, pool, Period::Month, settings).await
}

async fn show_user_statistics(
    bot: Bot,
    msg: Message,
    pool: PoolType,
    period: Period,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;

    match api::get_user_statistics(msg.chat.id.0, pool, period.clone()).await {
        Ok(stats) => {
            let period_name = match period {
                Period::Week => t8x!(Ru, "неделю", &ln),
                Period::Month => t8x!(Ru, "месяц", &ln),
            };

            if let Some(stats) = stats {
                let traffic       = format_mb(stats.total.traffic);
                let extra_traffic = format_mb(stats.total.extra_traffic);

                let mut message = strfmt!(t8x!(Ru, "
                        <b>📊 Статистика пула {pool} за {period}:</b>
                    
                        <b>📈 Общее использование:</b>
                        • Трафик: <code>{traffic}</code>
                        • Дополнительный трафик: <code>{extra_traffic}</code>
                        • Запросы: <code>{request}</code>

                        <b>📅 Подробная статистика:</b>

                    ", &ln),
                    pool => pool.display_name(&ln),
                    period => period_name,
                    traffic => traffic,
                    extra_traffic => extra_traffic,
                    request => stats.total.request
                )?;

                message.push_str(&build_detailed_statistics_message(
                    stats,
                    &ln
                )?);

                let buttons = vec![
                    (t8x!(Ru, "🔙Назад к выбору периода🔙", &ln), Callback::BackToStatsMenu { pool }).into()
                ];
                let keyboard = make_inline_keyboard(buttons, 1);
            
                bot.edit_message_text(msg.chat.id, msg.id, message)
                    .parse_mode(teloxide::types::ParseMode::Html)
                    .reply_markup(keyboard)
                    .await?;
            } else {
                let message = strfmt!(
                    t8x!(Ru, "Статистика для пула {pool} за {period} не найдена", &ln), 
                    pool => pool.display_name(&ln),
                    period => period_name
                )?;
                let keyboard = make_inline_keyboard(
                    vec![(t8x!(Ru, "🔙Назад к выбору периода🔙", &ln), Callback::BackToStatsMenu { pool }).into()],
                    1,
                );
                bot.edit_message_text(msg.chat.id, msg.id, message)
                    .reply_markup(keyboard)
                    .await?;
            }
        }
        Err(error) => {
            log::error!("Failed to get user statistics: {}", error);
            let period_name = match period {
                Period::Week => t8x!(Ru, "неделю", &ln),
                Period::Month => t8x!(Ru, "месяц", &ln),
            };
            
            let message = strfmt!(
                t8x!(Ru, "❌ Не удалось получить статистику для пула {pool} за {period}", &ln), 
                pool => pool.display_name(&ln),
                period => period_name
            )?;
            let keyboard = make_inline_keyboard(
                vec![(t8x!(Ru, "🔙Назад к выбору периода🔙", &ln), Callback::BackToStatsMenu { pool }).into()],
                1,
            );
            bot.edit_message_text(msg.chat.id, msg.id, message)
                .reply_markup(keyboard)
                .await?;
        }
    }
    Ok(())
}

async fn get_back_from_buying(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    profile: Profile,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;

    let message = t8x!(Ru, "Покупка отменена!", &ln).to_string();
    let keyboard = make_profile_inline_keyboard(ln, profile);
    bot.edit_message_text(msg.chat.id, msg.id, message)
        .reply_markup(keyboard)
        .await?;
    update_state_preserving_cache(&dialogue, UserStateKind::Profile).await?;
    Ok(())
}


async fn manage_whitelist_for_pool_handler(
    bot: Bot,
    msg: Message,
    pool: PoolType,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;
    let pool_name = pool.display_name(&ln);
    
    let message = strfmt!(t8x!(Ru, r#"
                ⛩️ Управление белым списком IP для пула: {pool_name}
                
                📋 Выберите действие:
            "#, &ln),
                pool_name
            )?;

    let buttons: Vec<InlineButton<Callback>> = vec![
        (t8x!(Ru, "👁️ Просмотреть список 👁️", &ln), Callback::ViewWhitelistForPool { pool }).into(),
        (t8x!(Ru, "➕ Добавить IP ➕", &ln), Callback::AddIPToWhitelist { pool }).into(),
        (t8x!(Ru, "➖ Удалить IP ➖", &ln), Callback::RemoveIPFromWhitelist { pool }).into(),
        (t8x!(Ru, "🔙Назад к управлению🔙", &ln), Callback::SelectPoolForManagement { pool }).into(),
    ];

    let keyboard = make_inline_keyboard(buttons, 1);

    bot.edit_message_text(msg.chat.id, msg.id, message)
        .reply_markup(keyboard)
        .await?;

    Ok(())
}

async fn view_whitelist_for_pool_handler(
    bot: Bot,
    msg: Message,
    pool: PoolType,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;
    let pool_name = pool.display_name(&ln);

    match api::get_whitelist_ips(msg.chat.id.0, pool).await {
        Ok(ips) => {
            let message = if ips.is_empty() {
                strfmt!(t8x!(Ru, r#"
                        ⛩️ Белый список IP для пула {pool_name}

                        📭 Список пуст
                    "#, &ln),
                    pool_name
                )?
            } else {
                let ip_list = ips.join("\n• ");
                strfmt!(t8x!(Ru, r#"
                        ⛩️ Белый список IP для пула {pool_name}

                        📋 Список IP адресов:
                        • {ip_list}
                    "#, &ln),
                    pool_name,
                    ip_list
                )?
            };

            let buttons = vec![
                (t8x!(Ru, "🔙Назад к управлению🔙", &ln), Callback::BackToWhitelistMenu { pool }).into()
            ];
            let keyboard = make_inline_keyboard(buttons, 1);
            
            bot.edit_message_text(msg.chat.id, msg.id, message)
                .reply_markup(keyboard)
                .await?;
        }
        Err(e) => {
            log::error!("Failed to get whitelist IPs: {}", e);
            let error_message = t8x!(Ru, "❌ Не удалось получить список IP адресов. Попробуйте позже.", &ln);
            let keyboard = make_inline_keyboard(
                vec![(t8x!(Ru, "🔙Назад к управлению🔙", &ln), Callback::BackToWhitelistMenu { pool }).into()],
                1,
            );
            
            bot.edit_message_text(msg.chat.id, msg.id, error_message)
                .reply_markup(keyboard)
                .await?;
        }
    }

    Ok(())
}

async fn add_ip_to_whitelist_handler(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    pool: PoolType,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;
    let pool_name = pool.display_name(&ln);
    
    let message = strfmt!(t8x!(Ru, r#"
            ⛩️ Добавление IP в белый список для пула: {pool_name}
            
            📝 Введите публичный IP адрес (например: <code>8.8.8.8</code> или <code>203.0.113.0/24</code>):
            
            ⚠️ Локальные и приватные IP адреса не поддерживаются
        "#, &ln),
        pool_name
    )?;
    
    let back_button = InlineKeyboardButton::callback(
        t8x!(Ru, "🔙Назад🔙", &ln),
        Callback::BackToWhitelistMenu { pool }.to_string()
    );
    let keyboard = InlineKeyboardMarkup::new(vec![vec![back_button]]);
    
    bot.edit_message_text(msg.chat.id, msg.id, message)
        .reply_markup(keyboard)
        .await?;
    
    update_state_preserving_cache(&dialogue, UserStateKind::ReceiveIPToAdd { selected_pool: pool }).await?;
    
    Ok(())
}

async fn remove_ip_from_whitelist_handler(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    pool: PoolType,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;
    let pool_name = pool.display_name(&ln);
    
    let message = strfmt!(t8x!(Ru, r#"
            ⛩️ Удаление IP из белого списка для пула: {pool_name}
            
            📝 Введите IP адрес для удаления:
        "#, &ln),
        pool_name
    )?;
    
    let back_button = InlineKeyboardButton::callback(
        t8x!(Ru, "🔙Назад🔙", &ln),
        Callback::BackToWhitelistMenu { pool }.to_string()
    );
    let keyboard = InlineKeyboardMarkup::new(vec![vec![back_button]]);
    
    bot.edit_message_text(msg.chat.id, msg.id, message)
        .reply_markup(keyboard)
        .await?;
    
    update_state_preserving_cache(&dialogue, UserStateKind::ReceiveIPToRemove { selected_pool: pool }).await?;
    
    Ok(())
}

async fn receive_ip_to_add(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    selected_pool: PoolType,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;
    let ip = msg.text().unwrap_or("").trim().to_string();
    
    if !api::validate_ip_address(&ip) {
        let message = t8x!(Ru, "❌ Неверный формат IP адреса. Попробуйте еще раз.", &ln);
        bot.send_message(msg.chat.id, message).await?;
        return Ok(());
    }
    
    if is_private_ip(&ip) {
        let message = t8x!(Ru, "❌ Локальные и приватные IP адреса не поддерживаются. Используйте только публичные IP адреса.", &ln);
        bot.send_message(msg.chat.id, message).await?;
        return Ok(());
    }
    
    match api::add_ip_to_whitelist(msg.chat.id.0, ip.clone(), selected_pool).await {
        Ok(response_message) => {
            let success_message = format!("✅ {}", response_message);
            let keyboard = make_inline_keyboard(
                vec![(t8x!(Ru, "🔙Назад к управлению🔙", &ln), Callback::BackToWhitelistMenu { pool: selected_pool }).into()],
                1,
            );
            
            bot.send_message(msg.chat.id, success_message)
                .reply_markup(keyboard)
                .await?;
        }
        Err(e) => {
            log::error!("Failed to add IP to whitelist: {}", e);
            
            let error_message = if e.to_string().contains("PT406") || e.to_string().contains("Local and private IP addresses are not allowed") {
                t8x!(Ru, "❌ Локальные и приватные IP адреса не поддерживаются. Используйте только публичные IP адреса.", &ln).to_string()
            } else if e.to_string().contains("PT400") || e.to_string().contains("Bind is already exists for other subuser") {
                t8x!(Ru, "❌ Этот IP адрес уже используется другим пользователем. Выберите другой IP адрес.", &ln).to_string()
            } else {
                format!("❌ {}", t8x!(Ru, "Не удалось добавить IP {} в белый список. Попробуйте позже.", &ln).replace("{}", &ip))
            };
            
            let keyboard = make_inline_keyboard(
                vec![(t8x!(Ru, "🔙Назад к управлению🔙", &ln), Callback::BackToWhitelistMenu { pool: selected_pool }).into()],
                1,
            );
            
            bot.send_message(msg.chat.id, error_message)
                .reply_markup(keyboard)
                .await?;
        }
    }
    
    update_state_preserving_cache(&dialogue, UserStateKind::WhitelistManagement { selected_pool }).await?;
    Ok(())
}

async fn receive_ip_to_remove(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    selected_pool: PoolType,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;
    let ip = msg.text().unwrap_or("").trim().to_string();
    
    if !api::validate_ip_address(&ip) {
        let message = t8x!(Ru, "❌ Неверный формат IP адреса. Попробуйте еще раз.", &ln);
        bot.send_message(msg.chat.id, message).await?;
        return Ok(());
    }
    
    match api::remove_ip_from_whitelist(msg.chat.id.0, ip.clone(), selected_pool).await {
        Ok(response_message) => {
            let success_message = format!("✅ {}", response_message);
            let keyboard = make_inline_keyboard(
                vec![(t8x!(Ru, "🔙Назад к управлению🔙", &ln), Callback::BackToWhitelistMenu { pool: selected_pool }).into()],
                1,
            );
            
            bot.send_message(msg.chat.id, success_message)
                .reply_markup(keyboard)
                .await?;
        }
        Err(e) => {
            log::error!("Failed to remove IP from whitelist: {}", e);
            let error_message = format!("❌ Не удалось удалить IP {} из белого списка. Попробуйте позже.", ip);
            let keyboard = make_inline_keyboard(
                vec![(t8x!(Ru, "🔙Назад к управлению🔙", &ln), Callback::BackToWhitelistMenu { pool: selected_pool }).into()],
                1,
            );
            
            bot.send_message(msg.chat.id, error_message)
                .reply_markup(keyboard)
                .await?;
        }
    }
    
    update_state_preserving_cache(&dialogue, UserStateKind::WhitelistManagement { selected_pool }).await?;
    Ok(())
}

async fn set_account_password_handler(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;

    let back_button = InlineKeyboardButton::callback(
        t8x!(Ru, "🔙Назад🔙", &ln),
        Callback::Api.to_string()
    );
    let keyboard = InlineKeyboardMarkup::new(vec![vec![back_button]]);
    
    bot.edit_message_text(
        msg.chat.id,
        msg.id,
        t8x!(Ru, "🔒 Введите новый пароль для вашего аккаунта:\n\n⚠️ Пароль должен содержать не менее 8 символов", &ln)
    )
    .reply_markup(keyboard)
    .await?;
    
    update_state_preserving_cache(&dialogue, UserStateKind::ReceiveAccountPassword).await?;
    Ok(())
}

pub async fn receive_account_password(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;

    let password_text = msg.text().unwrap_or("");
    
    bot.delete_message(msg.chat.id, msg.id).await?;

    if password_text.trim().len() < 8 {
        bot.send_message(msg.chat.id, t8x!(Ru, "❌ Пароль должен содержать не менее 8 символов", &ln))
            .reply_markup(make_menu_keyboard(ln))
            .await?;
        update_state_preserving_cache(&dialogue, UserStateKind::UserMenu).await?;
        return Ok(());
    }

    match api::change_account_password(msg.chat.id.0, Some(String::new()), password_text.trim().to_string()).await {
        Ok(_) => {
            let success_message = t8x!(Ru, "✅ Пароль успешно установлен!", &ln);
            bot.send_message(msg.chat.id, success_message)
                .reply_markup(make_menu_keyboard(ln))
                .await?;

            invalidate_profile_cache(&dialogue).await?;
        }
        Err(e) => {
            log::error!("Failed to set account password: {}", e);
            let error_message = t8x!(Ru, "❌ Не удалось установить пароль. Попробуйте еще раз.", &ln);
            bot.send_message(msg.chat.id, error_message)
                .reply_markup(make_menu_keyboard(ln))
                .await?;
        }
    }

    update_state_preserving_cache(&dialogue, UserStateKind::UserMenu).await?;
    Ok(())

    // // Check if user already has a password
    // match api::is_password_exists(msg.chat.id.0).await {
    //         // For setting password (user doesn't have one), we don't need old password
    //         let old_password = if has_password { Some(String::new()) } else { None };
    //
    //         match api::change_account_password(msg.chat.id.0, old_password, text.trim().to_string()).await {
    //                 let success_message = t8x!(Ru, "✅ Пароль успешно установлен!", &ln);
    //                 bot.send_message(msg.chat.id, success_message)
    //                     .reply_markup(make_menu_keyboard(ln))
    //                     .await?;
    //
    //                 // Invalidate profile cache since password status changed
    //                 invalidate_profile_cache(&dialogue).await?;
    //             }
    //                 log::error!("Failed to set account password: {}", e);
    //                 let error_message = t8x!(Ru, "❌ Не удалось установить пароль. Попробуйте еще раз.", &ln);
    //                 bot.send_message(msg.chat.id, error_message)
    //                     .reply_markup(make_menu_keyboard(ln))
    //                     .await?;
    //             }
    //         }
    //     }
    //         log::error!("Failed to check password existence: {}", e);
    //         let error_message = t8x!(Ru, "❌ Произошла ошибка. Попробуйте еще раз.", &ln);
    //         bot.send_message(msg.chat.id, error_message)
    //             .reply_markup(make_menu_keyboard(ln))
    //             .await?;
    //     }
    // }

}
