use crate::ui::prelude::*;
use std::sync::Arc;

const ITEMS_PER_PAGE: usize = 18;
const CHUNK_SIZE: usize = 50;
const MAX_AMOUNT: u32 = 10000;
const MAX_INTERVAL: u32 = 120;
const ITEMS_PER_ROW: usize = 2;
const MAX_DISPLAY_ITEMS: usize = 5;

/// Helper function to update existing locations or add new ones without duplicates
fn update_or_add_locations(existing_locations: &mut Vec<SelectableLocation>, new_locations: Vec<Location>) {
    let mut updated_locations = Vec::new();
    
    for new_location in new_locations {
        if let Some(existing) = existing_locations.iter().find(|l| l.location.code == new_location.code) {
            updated_locations.push(SelectableLocation::new_selected(new_location, existing.selected));
        } else {
            updated_locations.push(SelectableLocation::new(new_location));
        }
    }
    
    *existing_locations = updated_locations;
}

/// Helper function to truncate long lists for display
fn format_selected_items(items: Vec<String>, ln: Language) -> String {
    if items.is_empty() {
        return t8x!(Ru, "❗Не выбрано", &ln).to_string();
    }
    
    if items.len() <= MAX_DISPLAY_ITEMS {
        format!("✅ {}", items.join(", "))
    } else {
        let displayed_items = items.iter().take(MAX_DISPLAY_ITEMS).cloned().collect::<Vec<_>>().join(", ");
        let remaining = items.len() - MAX_DISPLAY_ITEMS;
        format!("✅ {} {} (+{})", displayed_items, t8x!(Ru, "и других", &ln), remaining)
    }
}

/// Helper function to format optional selected items (not required fields)
fn format_optional_selected_items(items: Vec<String>, ln: Language) -> String {
    if items.is_empty() {
        return t8x!(Ru, "Не обязательно", &ln).to_string();
    }
    
    if items.len() <= MAX_DISPLAY_ITEMS {
        format!("✅ {}", items.join(", "))
    } else {
        let displayed_items = items.iter().take(MAX_DISPLAY_ITEMS).cloned().collect::<Vec<_>>().join(", ");
        let remaining = items.len() - MAX_DISPLAY_ITEMS;
        format!("✅ {} {} (+{})", displayed_items, t8x!(Ru, "и других", &ln), remaining)
    }
}

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

    let (state, pool) = handle_state(&bot, &msg, &user_dialogue, &dialogue, &settings, ln).await?;

    let data = state.storage(pool);

    let selected_countries = data.selected_countries().into_iter().map(|mut c| mem::take(&mut c.code)).collect::<Vec<_>>();

    let countries_text = if selected_countries.is_empty() {
        t8x!(Ru, "❗Не выбрано", &ln).to_string()
    } else if selected_countries.len() == data.countries.len() && data.countries.len() > MAX_DISPLAY_ITEMS {
        format!("✅ 🌍 {}", t8x!(Ru, "Все страны", &ln))
    } else {
        format_selected_items(selected_countries, ln)
    };

    let selected_states = data.selected_states().into_iter().map(|mut s| mem::take(&mut s.code)).collect::<Vec<_>>();
    let states_text = format_optional_selected_items(selected_states, ln);

    let amount_text = format!("✅ {}", data.amount);

    let show_interval = matches!(data.r#type, ProxyType::Sticky(..));
    let default_interval_text = t8x!(Ru, "От 1 до 120 минут в зависимости от региона", &ln);
    let interval_text = if show_interval {
        match &data.r#type {
            ProxyType::Sticky(Some(interval)) => {
                strfmt!(t8x!(Ru, "<b>Интервал ротации:</b> ✅ {interval} мин", &ln), interval => interval.to_string())?
            }
            ProxyType::Sticky(None) => {
                strfmt!(t8x!(Ru, "<b>Интервал ротации:</b> {default_interval_text}", &ln), default_interval_text => default_interval_text)?
            }
            _ => "".to_string()
        }
    } else {
        "".to_string()
    };

    let format_text = format!("✅ {}", data.format);

    let selected_cities = data.selected_cities().into_iter().map(|mut c| mem::take(&mut c.code)).collect::<Vec<_>>();
    let cities_text = format_optional_selected_items(selected_cities, ln);

    let selected_asns = data.selected_included_asns().into_iter().map(|mut c| mem::take(&mut c.code)).collect::<Vec<_>>();
    let included_asns_text = format_optional_selected_items(selected_asns, ln);

    let selected_excluded_asns = data.selected_excluded_asns().into_iter().map(|mut c| mem::take(&mut c.code)).collect::<Vec<_>>();
    let excluded_asns_text = format_optional_selected_items(selected_excluded_asns, ln);

    let selected_zip_codes = data.selected_zip_codes().into_iter().map(|mut zc| mem::take(&mut zc.code)).collect::<Vec<_>>();
    let zip_codes_text = format_optional_selected_items(selected_zip_codes, ln);
    
    let message = t8x!(Ru, r#"
        <b>🌍Для начала необходимо настроить ваши {pool_name} прокси!</b>

        <b>🔹 Страны</b>: {countries_text}
        <b>🔹 Штаты:</b> {states_text}
        <b>🔹 Города:</b> {cities_text}
        <b>🔹 ZIP-коды:</b> {zip_codes_text}

        <b>⚙️ Параметры:</b>
        <b>Регион серверов:</b> {region}
        <b>Анонимность:</b> {anonymity}
        <b>Тип:</b> {proxy_type}
        <b>Протокол:</b> {protocol}
        <b>Формат:</b> {format_text}
        <b>Количество прокси:</b> {amount_text}
        {interval_text}

        <b>🔢 ASN:</b>
        <b>Выбранные ASN:</b> {included_asns_text}
        <b>Исключить ASN:</b> {excluded_asns_text}
    "#, &ln);

    let keyboard = make_proxy_settings_keyboard(ln, show_interval);

    let anonymity = if data.anonymity { "✅" } else { "❌" };

    log::debug!("message_id: {:?}", msg.id);
    
    bot.edit_message_text(msg.chat.id, msg.id, strfmt!(
            message,
            pool_name => pool.display_name(&ln),
            countries_text,
            states_text,
            amount_text,
            protocol => data.protocol.to_string(),
            proxy_type => data.r#type.to_string(),
            anonymity,
            region => data.region.display_name_custom(&ln, true),
            interval_text,
            format_text,
            cities_text,
            included_asns_text,
            excluded_asns_text,
            zip_codes_text
        )?)
        .parse_mode(teloxide::types::ParseMode::Html)
        .reply_markup(keyboard)
        .await?;
    Ok(())
}

pub fn make_proxy_settings_keyboard(ln: Language, show_interval: bool) -> InlineKeyboardMarkup {
    let mut keyboard = InlineKeyboardMarkup::default();
    
    keyboard = keyboard.append_row(vec![
        InlineKeyboardButton::callback(
            t8x!(Ru, "🌏Выбор страны 🌏", &ln),
            GetProxyCallback::ProxySelectCountries.to_string()
        ),
        InlineKeyboardButton::callback(
            t8x!(Ru, "🌍Выбор хоста🌍", &ln),
            GetProxyCallback::ProxySelectRegion.to_string()
        )
    ]);
    
    keyboard = keyboard.append_row(vec![
        InlineKeyboardButton::callback(
            t8x!(Ru, "🌏Дополнительные настройки локации 🌏", &ln),
            GetProxyCallback::ProxySelectLocations.to_string()
        )
    ]);
    
    keyboard = keyboard.append_row(vec![
        InlineKeyboardButton::callback(
            t8x!(Ru, "📡HTTP/s📡", &ln),
            GetProxyCallback::ProxySetProtocol { protocol: ProxyProtocol::Http }.to_string()
        ),
        // InlineKeyboardButton::callback(
        //     t8x!(Ru, "💿HTTPS💿", &ln),
        //     GetProxyCallback::ProxySetProtocol { protocol: ProxyProtocol::Https }.to_string()
        // ),
        InlineKeyboardButton::callback(
            t8x!(Ru, "🧦SOCKS5🧦", &ln),
            GetProxyCallback::ProxySetProtocol { protocol: ProxyProtocol::Socks5 }.to_string()
        ),
    ]);
    
    keyboard = keyboard.append_row(vec![
        InlineKeyboardButton::callback(
            t8x!(Ru, "💻Статичный⏸", &ln),
            GetProxyCallback::ProxySetType { proxy_type: ProxyType::Sticky(None) }.to_string()
        ),
        InlineKeyboardButton::callback(
            t8x!(Ru, "🔁Ротационный💻", &ln),
            GetProxyCallback::ProxySetType { proxy_type: ProxyType::Rotating }.to_string()
        ),
    ]);
    
    keyboard = keyboard.append_row(vec![
        InlineKeyboardButton::callback(
            t8x!(Ru, "🎭Вкл анонимность✅", &ln),
            GetProxyCallback::ProxySetAnonymity { enabled: true }.to_string()
        ),
        InlineKeyboardButton::callback(
            t8x!(Ru, "❌Выкл анонимность🎭", &ln),
            GetProxyCallback::ProxySetAnonymity { enabled: false }.to_string()
        ),
    ]);
    
    if show_interval {
        keyboard = keyboard.append_row(vec![
            InlineKeyboardButton::callback(
                t8x!(Ru, "⏳Изменить время ротации⏳", &ln),
                GetProxyCallback::ProxySetInterval.to_string()
            )
        ]);
    }
    
    keyboard = keyboard.append_row(vec![
        InlineKeyboardButton::callback(
            t8x!(Ru, "🔘Формат🔘", &ln),
            GetProxyCallback::ProxySetFormat.to_string()
        ),
        InlineKeyboardButton::callback(
            t8x!(Ru, "🔢Количество🔢", &ln),
            GetProxyCallback::ProxySetAmount.to_string()
        )
    ]);
    
    keyboard = keyboard.append_row(vec![
        InlineKeyboardButton::callback(
            t8x!(Ru, "❌Исключить ASN❌", &ln),
            GetProxyCallback::ProxySelectExcludedASNs.to_string()
        )
    ]);
    
    keyboard = keyboard.append_row(vec![
        InlineKeyboardButton::callback(
            t8x!(Ru, "▶️Генерировать▶️", &ln),
            GetProxyCallback::ProxyGenerate.to_string()
        ),
        InlineKeyboardButton::callback(
            t8x!(Ru, "🔙Назад🔙", &ln),
            Callback::DeleteThisMessage.to_string()
        )
    ]);
    
    keyboard
}

pub async fn get_proxy_callback_handler(
    bot: Bot,
    user_dialogue: MasterDialogue,
    get_proxy_dialogue: GetProxyDialogue,
    q: CallbackQuery,
    settings: HandlerSettings,
) -> HandlerResult {
    let ln = settings.language;

    let Some(data) = q.data.clone() else {
        log::error!("Callback data is None");
        return Err("Callback data is None".into());
    };

    let Some(message) = get_callback_message(q.clone()) else {
        log::error!("Message does not exist");
        return Err("Message does not exist".into());
    };

    use GetProxyCallback::*;
    let callback: GetProxyCallback = serde_json::from_str(&data)?;
    match callback {
        GetProxy { pool } => {
            bot.answer_callback_query(q.id.clone()).await?;

            log::debug!("pool: {:?}, message_id: {:?}", pool, message.id);

            crate::ui::user::update_state_preserving_cache(&user_dialogue, crate::ui::UserStateKind::ProxyGeneration).await?;

            if let Some(mut state) = get_proxy_dialogue.get().await? {
                state.garbage_collect(&bot).await?;
                if let Some(current_message) = state.current_message {
                    if current_message.id != message.id {
                        bot.delete_message(message.chat.id, current_message.id).await.ok();
                    }
                }
                state.current_message = Some(message.clone());

                state.pool = Some(pool);
                state.set_step(ProxySelectionStep::MainMenu);
                get_proxy_dialogue.update(state).await?;
            } else {
                get_proxy_dialogue.update(GetProxyStates::new(Some(pool), Some(message.clone()))).await?;
            }
            
            show_proxy_settings_handler(bot, message, user_dialogue, get_proxy_dialogue, settings).await?;
        }
        ProxySelectCountries => {
            bot.answer_callback_query(q.id.clone()).await?;
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;
            
            state.set_step(ProxySelectionStep::ChoseSearchable {
                query: String::new(),
                step: ProxySearcheble::ChoseCountry,
            });
            
            match api::get_countries_for_pool(pool).await {
                Ok(countries_vec) => {
                    let storage = state.storage_mut(pool);
                    update_or_add_locations(&mut storage.countries, countries_vec);
                    storage.page = 0;

                    show_selection_filtered(&bot, &message, pool, 0, &storage.countries, "", &settings, country_selection_config(settings.language)).await?;

                    get_proxy_dialogue.update(state).await?;
                }
                Err(e) => {
                    log::error!("Failed to get countries: {}", e);
                    bot.send_message(message.chat.id, t8x!(Ru, "Ошибка загрузки списка стран", &ln))
                        .await?;
                }
            }
        }
        ProxySelectRegion => {
            bot.answer_callback_query(q.id.clone()).await?;
            let (_, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;
            show_region_selection_menu_handler(bot, message, pool, settings).await?;
        }
        ProxySetRegion {
            region,
        } => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);
            data.region = region;

            get_proxy_dialogue.update(state).await?;

            bot.answer_callback_query(q.id.clone())
                .text(format!("Регион установлен: {}", region))
                .send()
                .await?;

            show_proxy_settings_handler(bot, message, user_dialogue, get_proxy_dialogue, settings).await?;
        }
        ProxySelectLocations => {
            let (state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage(pool);
            let selected_count = data.countries.iter().filter(|c| c.selected).count();
            
            if selected_count == 0 {
                bot.answer_callback_query(q.id.clone())
                    .text(t8x!(Ru, "❗ Сначала выберите хотя бы одну страну", &ln))
                    .show_alert(true)
                    .send()
                    .await?;
            } else {
                bot.answer_callback_query(q.id.clone()).text(t8x!(Ru, r#"
                        ⚠️ Внимание! ⚠️
                        При использовании этой опции трафик списывается В ДВОЙНОМ ОБЪЕМЕ.
                    "#, &ln))
                    .show_alert(true)
                    .send()
                    .await?;
                
                let keyboard = make_additional_location_settings_keyboard(ln, pool);
                bot.edit_message_text(message.chat.id, message.id, t8x!(Ru, r#"
                        <b>🌍 Настройки локации</b>
                        <b>⚠️ Внимание!</b>
                        При использовании этой опции трафик списывается <b>в двойном объеме.</b>
                    "#, &ln))
                    .reply_markup(keyboard)
                    .await?;
            }
        }
        ProxySelectStates => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage(pool);

            let selected_countries = data.selected_countries();

            if selected_countries.is_empty() {
                bot.answer_callback_query(q.id.clone())
                    .text(t8x!(Ru, "❗ Сначала выберите хотя бы одну страну", &ln))
                    .show_alert(true)
                    .send()
                    .await?;
                return Ok(());
            }
            
            state.set_step(ProxySelectionStep::ChoseSearchable {
                query: String::new(),
                step: ProxySearcheble::ChoseState,
            });
            
            match api::get_states(pool, selected_countries).await {
                Ok(states_vec) => {
                    let storage = state.storage_mut(pool);
                    update_or_add_locations(&mut storage.states, states_vec);
                    storage.page = 0;

                    show_selection_filtered(&bot, &message, pool, 0, &storage.states, "", &settings, state_selection_config(settings.language)).await?;

                    get_proxy_dialogue.update(state).await?;
                }
                Err(e) => {
                    log::error!("Failed to get states: {}", e);
                    bot.send_message(message.chat.id, t8x!(Ru, "Ошибка загрузки списка штатов", &ln))
                        .await?;
                }
            }
        }
        ProxySelectCities => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage(pool);

            let selected_countries = data.selected_countries();
            let selected_states = data.selected_states();

            if selected_countries.is_empty() {
                bot.answer_callback_query(q.id.clone())
                    .text(t8x!(Ru, "❗ Сначала выберите хотя бы одну страну", &ln))
                    .show_alert(true)
                    .send()
                    .await?;
                return Ok(());
            }
            
            state.set_step(ProxySelectionStep::ChoseSearchable {
                query: String::new(),
                step: ProxySearcheble::ChoseCity,
            });
            
            match api::get_cities(pool, selected_countries, selected_states).await {
                Ok(cities_vec) => {
                    let storage = state.storage_mut(pool);
                    update_or_add_locations(&mut storage.cities, cities_vec);
                    storage.page = 0;

                    show_selection_filtered(&bot, &message, pool, 0, &storage.cities, "", &settings, city_selection_config(settings.language)).await?;

                    get_proxy_dialogue.update(state).await?;
                }
                Err(e) => {
                    log::error!("Failed to get cities: {}", e);
                    bot.send_message(message.chat.id, t8x!(Ru, "Ошибка загрузки списка городов", &ln))
                        .await?;
                }
            }
        }
        ProxySelectASNs => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage(pool);

            let selected_countries = data.selected_countries();

            if selected_countries.is_empty() {
                bot.answer_callback_query(q.id.clone())
                    .text(t8x!(Ru, "❗ Сначала выберите хотя бы одну страну", &ln))
                    .show_alert(true)
                    .send()
                    .await?;
                return Ok(());
            }

            let selected_states = data.selected_states();
            let selected_cities = data.selected_cities();
            
            state.set_step(ProxySelectionStep::ChoseSearchable {
                query: String::new(),
                step: ProxySearcheble::ChoseAsn,
            });
            
            match api::get_asns(pool, selected_countries, selected_states, selected_cities).await {
                Ok(asns_vec) => {
                    let storage = state.storage_mut(pool);
                    update_or_add_locations(&mut storage.included_asns, asns_vec);
                    storage.page = 0;

                    show_selection_filtered(&bot, &message, pool, 0, &storage.included_asns, "", &settings, asn_selection_config(settings.language)).await?;

                    get_proxy_dialogue.update(state).await?;
                }
                Err(e) => {
                    log::error!("Failed to get ASNs: {}", e);
                    bot.send_message(message.chat.id, t8x!(Ru, "Ошибка загрузки списка ASN", &ln))
                        .await?;
                }
            }
        }
        ProxySelectExcludedASNs => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage(pool);

            let selected_countries = data.selected_countries();

            if selected_countries.is_empty() {
                bot.answer_callback_query(q.id.clone())
                    .text(t8x!(Ru, "❗ Сначала выберите хотя бы одну страну", &ln))
                    .show_alert(true)
                    .send()
                    .await?;
                return Ok(());
            }

            let selected_states = data.selected_states();
            let selected_cities = data.selected_cities();
            
            state.set_step(ProxySelectionStep::ChoseSearchable {
                query: String::new(),
                step: ProxySearcheble::ChoseExcludeAsn,
            });
            
            match api::get_asns(pool, selected_countries, selected_states, selected_cities).await {
                Ok(asns_vec) => {
                    let storage = state.storage_mut(pool);
                    update_or_add_locations(&mut storage.excluded_asns, asns_vec);

                    show_selection_filtered(&bot, &message, pool, 0, &storage.excluded_asns, "", &settings, excluded_asn_selection_config(settings.language)).await?;

                    storage.page = 0;

                    get_proxy_dialogue.update(state).await?;
                }
                Err(e) => {
                    log::error!("Failed to get ASNs: {}", e);
                    bot.send_message(message.chat.id, t8x!(Ru, "Ошибка загрузки списка ASN", &ln))
                        .await?;
                }
            }
        }
        ProxySelectZipCodes => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage(pool);

            let selected_countries = data.selected_countries();
            let selected_states = data.selected_states();
            let selected_included_asns = data.selected_included_asns();

            if selected_countries.is_empty() {
                bot.answer_callback_query(q.id.clone())
                    .text(t8x!(Ru, "❗ Сначала выберите хотя бы одну страну", &ln))
                    .show_alert(true)
                    .send()
                    .await?;
                return Ok(());
            }
            
            state.set_step(ProxySelectionStep::ChoseSearchable {
                query: String::new(),
                step: ProxySearcheble::ChoseZip,
            });
            
            match api::get_zip_codes(pool, selected_countries, selected_states, selected_included_asns).await {
                Ok(zip_codes_vec) => {
                    let storage = state.storage_mut(pool);
                    update_or_add_locations(&mut storage.zip_codes, zip_codes_vec);

                    show_selection_filtered(&bot, &message, pool, 0, &storage.zip_codes, "", &settings, zip_code_selection_config(settings.language)).await?;

                    storage.page = 0;

                    get_proxy_dialogue.update(state).await?;
                }
                Err(e) => {
                    log::error!("Failed to get zip codes: {}", e);
                    bot.send_message(message.chat.id, t8x!(Ru, "Ошибка загрузки списка ZIP-кодов", &ln))
                        .await?;
                }
            }
        }
        ProxySetProtocol {
            protocol,
        } => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);
            data.protocol = protocol;

            get_proxy_dialogue.update(state).await?;

            bot.answer_callback_query(q.id.clone())
                .text(format!("Протокол установлен: {}", protocol))
                .send()
                .await?;

            show_proxy_settings_handler(bot, message, user_dialogue, get_proxy_dialogue, settings).await?;
        }
        ProxySetType {
            proxy_type,
        } => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);
            data.r#type = proxy_type;

            get_proxy_dialogue.update(state).await?;

            bot.answer_callback_query(q.id.clone())
                .text(format!("Тип прокси установлен: {}", proxy_type))
                .send()
                .await?;

            show_proxy_settings_handler(bot, message, user_dialogue, get_proxy_dialogue, settings).await?;
        }
        ProxySetAnonymity {
            enabled,
        } => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);
            data.anonymity = enabled;

            get_proxy_dialogue.update(state).await?;

            let status = if enabled { "включена" } else { "выключена" };
            bot.answer_callback_query(q.id.clone())
                .text(format!("Анонимность: {}", status))
                .send()
                .await?;

            show_proxy_settings_handler(bot, message, user_dialogue, get_proxy_dialogue, settings).await?;
        }
        ProxySetFormat => {
            bot.answer_callback_query(q.id.clone()).await?;

            let (_, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;
            
            let keyboard = make_format_selection_keyboard(ln, pool);
            bot.edit_message_text(message.chat.id, message.id, t8x!(Ru, "Выберите нужный формат:", &ln))
                .reply_markup(keyboard)
                .await?;
        }
        ProxyFormatSelect { format } => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);
            data.format = format;

            get_proxy_dialogue.update(state).await?;

            bot.answer_callback_query(q.id.clone())
                .text(format!("Формат установлен: {}", format))
                .send()
                .await?;

            show_proxy_settings_handler(bot, message, user_dialogue, get_proxy_dialogue, settings).await?;
        }
        ProxySetAmount => {
            bot.answer_callback_query(q.id.clone()).await?;
            log::trace!("ProxySetAmount");
            
            let (mut state, _) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let Some(message) = state.current_message.clone() else {
                return Err("Message not found".into());
            };

            state.set_step(ProxySelectionStep::ReceiveAmount);
            
            bot.edit_message_text(message.chat.id, message.id, t8x!(Ru, "Введите необходимое количество прокси числом:", &ln))
                .await?;

            get_proxy_dialogue.update(state).await?;
        }
        ProxySetInterval => {
            bot.answer_callback_query(q.id.clone()).await?;

            let (mut state, _) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let Some(message) = state.current_message.clone() else {
                return Err("Message not found".into());
            };

            state.set_step(ProxySelectionStep::ReceiveInterval);

            bot.edit_message_text(message.chat.id, message.id, strfmt!(t8x!(Ru, 
                    "Введите время ротации в минутах (от 1 до {max_interval}):",
                &ln),
                max_interval => MAX_INTERVAL.to_string()
            )?).await?;

            get_proxy_dialogue.update(state).await?;
        }
        ProxySetASN => {
            bot.answer_callback_query(q.id.clone()).await?;
            bot.send_message(message.chat.id, t8x!(Ru, "Введите нужные для исключения AS числа через запятую без пробелов (23,45,21,12):", &ln))
                .await?;
        }
        ProxyCountryPage { page } => {
            bot.answer_callback_query(q.id.clone()).await?;
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);
            data.page = page;
            show_selection_filtered(&bot, &message, pool, page, &data.countries, "", &settings, country_selection_config(settings.language)).await?;
            get_proxy_dialogue.update(state).await?;
        }
        ProxyCountrySelect { index } => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            log::debug!("search_query: {:?}, index: {:?}", state.clone().search_query(), index);

            let search_query = state.search_query().to_owned();
            let data = state.storage_mut(pool);

            let start_idx = data.page * ITEMS_PER_PAGE;
            
            let mut sorted_countries: Vec<_> = data.countries.iter().map(|loc| &loc.location).cloned().collect();
            sorted_countries.sort_by(|a, b| a.name.cmp(&b.name));
            
            if !search_query.is_empty() {
                let search_lower = search_query.to_lowercase();
                sorted_countries.retain(|country| {
                    country.name.to_lowercase().contains(&search_lower) ||
                    country.code.to_lowercase().contains(&search_lower)
                });
            }
            
            if let Some(country) = sorted_countries.get(start_idx + index) {
                if let Some(current_state) = data.countries.iter_mut().find(|loc| &loc.location == country) {
                    current_state.selected = !current_state.selected;
                }
                
                let page = data.page;

                show_selection_filtered(&bot, &message, pool, page, &data.countries, &search_query, &settings, country_selection_config(settings.language)).await?;

                get_proxy_dialogue.update(state).await?;

            }
        }
        ProxySelectAll => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);

            let all_selected = data.countries.iter().all(|loc| loc.selected);
            
            for loc in data.countries.iter_mut() {
                loc.selected = !all_selected;
            }
            
            let action_text = if all_selected {
                t8x!(Ru, "Все страны отменены", &ln)
            } else {
                t8x!(Ru, "Все страны выбраны", &ln)
            };
            
            bot.answer_callback_query(q.id.clone())
                .text(action_text)
                .send()
                .await?;
            
            show_selection_filtered(&bot, &message, pool, data.page, &data.countries, "", &settings, country_selection_config(settings.language)).await?;
            get_proxy_dialogue.update(state).await?;
        }
        ProxyCountryConfirm => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);

            let selected_count = data.countries.iter().filter(|loc| loc.selected).count();
            let text = if selected_count == 0 {
                t8x!(Ru, "Выбраны все страны", &ln)
            } else {
                &strfmt!(t8x!(Ru, "Выбрано стран: {count}", &ln),
                    count => selected_count
                )?
            };
            
            state.set_step(ProxySelectionStep::MainMenu);
            get_proxy_dialogue.update(state).await?;
            
            bot.answer_callback_query(q.id.clone())
                .text(text)
                .send()
                .await?;
            
            show_proxy_settings_handler(bot, message, user_dialogue, get_proxy_dialogue, settings).await?;
        }
        ProxyClearSearch => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            state.clear_search();
            let data = state.storage(pool);
            show_selection_filtered(&bot, &message, pool, 0, &data.countries, "", &settings, country_selection_config(settings.language)).await?;

            get_proxy_dialogue.update(state).await?;
        }
        ProxyGenerate => {
            let (state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage(pool);

            if data.amount == 0 {
                bot.answer_callback_query(q.id.clone())
                    .text(t8x!(Ru, "❗ Сначала укажите количество прокси", &ln))
                    .show_alert(true)
                    .send()
                    .await?;
                return Ok(());
            }

            bot.answer_callback_query(q.id.clone())
                .text(t8x!(Ru, "🔄 Генерирую прокси...", &ln))
                .send()
                .await?;

            match api::generate_proxies(
                message.chat.id.0,
                pool,
                data.amount,
                data.selected_countries(),
                data.selected_states(),
                data.selected_cities(),
                data.selected_included_asns(),
                data.selected_excluded_asns(),
                data.selected_zip_codes(),
                data.protocol,
                data.r#type,
                data.anonymity,
                data.format,
                data.region
            ).await {
                Ok(proxies) => {
                    if proxies.trim().is_empty() {
                        let error_msg = t8x!(Ru, "❌ Не удалось сгенерировать прокси. Возможно, у вас недостаточно трафика или нет доступных IP в выбранном пуле.", &ln);
                        bot.edit_message_text(message.chat.id, message.id, error_msg).await?;
                    } else {
                        let proxy_lines: Vec<&str> = proxies.trim().split('\n').collect();
                        let chunks = proxy_lines.chunks(CHUNK_SIZE); // 50 proxies per message
                        let chunks_count = chunks.len();
                        
                        if chunks_count > 1 {
                            let file_content = proxies.trim().to_string();
                            let file = InputFile::memory(file_content.into_bytes())
                                .file_name("proxies.txt");
                            
                            let message_text = strfmt!(t8x!(Ru, r#"
                                    ✅ <b>Ваши прокси ({proxy_count} шт.):</b>
                                    
                                    📁 Прокси отправлены в виде файла для удобства
                                "#, &ln),
                                proxy_count => proxy_lines.len()
                            )?;
                            
                            let keyboard = InlineKeyboardMarkup::new(vec![
                                vec![
                                    InlineKeyboardButton::callback(
                                        t8x!(Ru, "🔄 Сгенерировать ещё", &ln),
                                        GetProxyCallback::GetProxy { pool }.to_string()
                                    )
                                ]
                            ]);
                            
                            bot.edit_message_text(message.chat.id, message.id, message_text)
                                .parse_mode(teloxide::types::ParseMode::Html)
                                .reply_markup(keyboard)
                                .await?;
                            
                            bot.send_document(message.chat.id, file)
                                .await?;
                        } else {
                            for (i, chunk) in chunks.enumerate() {
                                let chunk_text = chunk.join("\n");
                                let message_text = strfmt!(t8x!(Ru, r#"
                                        ✅ <b>Ваши прокси ({proxy_lines_len} шт.):</b>
                                    
                                        <code>
                                        {chunk_text}
                                        </code>
                                    "#, &ln),
                                    proxy_lines_len => proxy_lines.len(),
                                    chunk_text
                                )?;
                                
                                if i == 0 {
                                    let keyboard = InlineKeyboardMarkup::new(vec![
                                        vec![
                                            InlineKeyboardButton::callback(
                                                t8x!(Ru, "🔄 Сгенерировать ещё", &ln),
                                                GetProxyCallback::GetProxy { pool }.to_string()
                                            )
                                        ]
                                    ]);
                                    
                                    bot.edit_message_text(message.chat.id, message.id, message_text)
                                        .parse_mode(teloxide::types::ParseMode::Html)
                                        .reply_markup(keyboard)
                                        .await?;
                                } else {
                                    bot.send_message(message.chat.id, message_text)
                                        .parse_mode(teloxide::types::ParseMode::Html)
                                        .await?;
                                }
                            }
                        }
                    }
                }
                Err(e) => {
                    log::error!("Failed to generate proxies: {}", e);
                    let error_msg = t8x!(Ru, "❌ Ошибка при генерации прокси. Попробуйте позже или обратитесь в поддержку.", &ln);
                    bot.edit_message_text(message.chat.id, message.id, error_msg).await?;
                }
            }
        }
        ProxyBack => {
            bot.answer_callback_query(q.id.clone()).await?;
            crate::ui::user::update_state_preserving_cache(&user_dialogue, crate::ui::UserStateKind::UserMenu).await?;
            bot.delete_message(message.chat.id, message.id).await?;
        }
        ProxyStatePage { page } => {
            bot.answer_callback_query(q.id.clone()).await?;
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);
            data.page = page;
            show_selection_filtered(&bot, &message, pool, page, &data.states, "", &settings, state_selection_config(settings.language)).await?;
            get_proxy_dialogue.update(state).await?;
        }
        ProxyStateSelect { index } => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            log::debug!("search_query: {:?}, index: {:?}", state.clone().search_query(), index);

            let search_query = state.search_query().to_owned();
            let data = state.storage_mut(pool);

            let start_idx = data.page * ITEMS_PER_PAGE;
            
            let mut sorted_states: Vec<_> = data.states.iter().map(|loc| &loc.location).cloned().collect();
            sorted_states.sort_by(|a, b| a.name.cmp(&b.name));
            
            if !search_query.is_empty() {
                let search_lower = search_query.to_lowercase();
                sorted_states.retain(|state| {
                    state.name.to_lowercase().contains(&search_lower) ||
                    state.code.to_lowercase().contains(&search_lower)
                });
            }
            
            if let Some(state_item) = sorted_states.get(start_idx + index) {
                if let Some(current_state) = data.states.iter_mut().find(|loc| &loc.location == state_item) {
                    current_state.selected = !current_state.selected;
                }
                
                let page = data.page;

                show_selection_filtered(&bot, &message, pool, page, &data.states, &search_query, &settings, state_selection_config(settings.language)).await?;
                get_proxy_dialogue.update(state).await?;

            }
        }
        ProxySelectAllStates => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);

            let all_selected = data.states.iter().all(|loc| loc.selected);
            
            for loc in data.states.iter_mut() {
                loc.selected = !all_selected;
            }
            
            let action_text = if all_selected {
                t8x!(Ru, "Все штаты отменены", &ln)
            } else {
                t8x!(Ru, "Все штаты выбраны", &ln)
            };
            
            bot.answer_callback_query(q.id.clone())
                .text(action_text)
                .send()
                .await?;
            
            show_selection_filtered(&bot, &message, pool, data.page, &data.states, "", &settings, state_selection_config(settings.language)).await?;
            get_proxy_dialogue.update(state).await?;
        }
        ProxyStateClearSearch => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            state.clear_search();
            let data = state.storage(pool);
            show_selection_filtered(&bot, &message, pool, 0, &data.states, "", &settings, state_selection_config(settings.language)).await?;

            get_proxy_dialogue.update(state).await?;
        }
        ProxyStateConfirm => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);

            let selected_count = data.states.iter().filter(|loc| loc.selected).count();
            let text = if selected_count == 0 {
                t8x!(Ru, "Штаты не выбраны", &ln)
            } else {
                &strfmt!(t8x!(Ru, "Выбрано штатов: {count}", &ln),
                    count => selected_count
                )?
            };
            
            state.set_step(ProxySelectionStep::MainMenu);
            get_proxy_dialogue.update(state).await?;
            
            bot.answer_callback_query(q.id.clone())
                .text(text)
                .send()
                .await?;
            
            let keyboard = make_additional_location_settings_keyboard(ln, pool);
            bot.edit_message_text(message.chat.id, message.id, t8x!(Ru, r#"
                    <b>🌍 Настройки локации</b>
                    <b>⚠️ Внимание!</b>
                    При использовании этой опции трафик списывается <b>в двойном объеме.</b>
                "#, &ln))
                .reply_markup(keyboard)
                .await?;
        }
        ProxyCityPage { page } => {
            bot.answer_callback_query(q.id.clone()).await?;
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);
            data.page = page;
            show_selection_filtered(&bot, &message, pool, page, &data.cities, "", &settings, city_selection_config(settings.language)).await?;
            get_proxy_dialogue.update(state).await?;
        }
        ProxyCitySelect { index } => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            log::debug!("search_query: {:?}, index: {:?}", state.clone().search_query(), index);

            let search_query = state.search_query().to_owned();
            let data = state.storage_mut(pool);

            let start_idx = data.page * ITEMS_PER_PAGE;
            
            let mut sorted_cities: Vec<_> = data.cities.iter().map(|loc| &loc.location).cloned().collect();
            sorted_cities.sort_by(|a, b| a.name.cmp(&b.name));
            
            if !search_query.is_empty() {
                let search_lower = search_query.to_lowercase();
                sorted_cities.retain(|city| {
                    city.name.to_lowercase().contains(&search_lower) ||
                    city.code.to_lowercase().contains(&search_lower)
                });
            }
            
            if let Some(city) = sorted_cities.get(start_idx + index) {
                if let Some(current_state) = data.cities.iter_mut().find(|loc| &loc.location == city) {
                    current_state.selected = !current_state.selected;
                }
                
                let page = data.page;

                show_selection_filtered(&bot, &message, pool, page, &data.cities, &search_query, &settings, city_selection_config(settings.language)).await?;
                get_proxy_dialogue.update(state).await?;

            }
        }
        ProxySelectAllCities => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);

            let all_selected = data.cities.iter().all(|loc| loc.selected);
            
            for loc in data.cities.iter_mut() {
                loc.selected = !all_selected;
            }
            
            let action_text = if all_selected {
                t8x!(Ru, "Все города отменены", &ln)
            } else {
                t8x!(Ru, "Все города выбраны", &ln)
            };
            
            bot.answer_callback_query(q.id.clone())
                .text(action_text)
                .send()
                .await?;
            
            show_selection_filtered(&bot, &message, pool, data.page, &data.cities, "", &settings, city_selection_config(settings.language)).await?;
            get_proxy_dialogue.update(state).await?;
        }
        ProxyCityClearSearch => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            state.clear_search();
            let data = state.storage(pool);
            show_selection_filtered(&bot, &message, pool, 0, &data.cities, "", &settings, city_selection_config(settings.language)).await?;

            get_proxy_dialogue.update(state).await?;
        }
        ProxyCityConfirm => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);

            let selected_count = data.cities.iter().filter(|loc| loc.selected).count();
            let text = if selected_count == 0 {
                t8x!(Ru, "Города не выбраны", &ln)
            } else {
                &strfmt!(t8x!(Ru, "Выбрано городов: {count}", &ln),
                    count => selected_count
                )?
            };
            
            state.set_step(ProxySelectionStep::MainMenu);
            get_proxy_dialogue.update(state).await?;
            
            bot.answer_callback_query(q.id.clone())
                .text(text)
                .send()
                .await?;
            
            let keyboard = make_additional_location_settings_keyboard(ln, pool);
            bot.edit_message_text(message.chat.id, message.id, t8x!(Ru, r#"
                    <b>🌍 Настройки локации</b>
                    <b>⚠️ Внимание!</b>
                    При использовании этой опции трафик списывается <b>в двойном объеме.</b>
                "#, &ln))
                .reply_markup(keyboard)
                .await?;
        }
        ProxyASNPage { page } => {
            bot.answer_callback_query(q.id.clone()).await?;
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);
            data.page = page;
            show_selection_filtered(&bot, &message, pool, page, &data.included_asns, "", &settings, asn_selection_config(settings.language)).await?;
            get_proxy_dialogue.update(state).await?;
        }
        ProxyASNSelect { index } => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            log::debug!("search_query: {:?}, index: {:?}", state.clone().search_query(), index);

            let search_query = state.search_query().to_owned();
            let data = state.storage_mut(pool);

            let start_idx = data.page * ITEMS_PER_PAGE;
            
            let mut sorted_asns: Vec<_> = data.included_asns.iter().map(|loc| &loc.location).cloned().collect();
            sorted_asns.sort_by(|a, b| a.name.cmp(&b.name));
            
            if !search_query.is_empty() {
                let search_lower = search_query.to_lowercase();
                sorted_asns.retain(|asn| {
                    asn.name.to_lowercase().contains(&search_lower) ||
                    asn.code.to_lowercase().contains(&search_lower)
                });
            }
            
            if let Some(asn) = sorted_asns.get(start_idx + index) {
                if let Some(current_state) = data.included_asns.iter_mut().find(|loc| &loc.location == asn) {
                    current_state.selected = !current_state.selected;
                }
                
                let page = data.page;

                show_selection_filtered(&bot, &message, pool, page, &data.included_asns, &search_query, &settings, asn_selection_config(settings.language)).await?;
                get_proxy_dialogue.update(state).await?;

            }
        }
        ProxySelectAllASNs => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);

            let all_selected = data.included_asns.iter().all(|loc| loc.selected);
            
            for loc in data.included_asns.iter_mut() {
                loc.selected = !all_selected;
            }
            
            let action_text = if all_selected {
                t8x!(Ru, "Все ASN отменены", &ln)
            } else {
                t8x!(Ru, "Все ASN выбраны", &ln)
            };
            
            bot.answer_callback_query(q.id.clone())
                .text(action_text)
                .send()
                .await?;
            
            show_selection_filtered(&bot, &message, pool, data.page, &data.included_asns, "", &settings, asn_selection_config(settings.language)).await?;
            get_proxy_dialogue.update(state).await?;
        }
        ProxyASNConfirm => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);

            let selected_count = data.included_asns.iter().filter(|loc| loc.selected).count();
            let text = if selected_count == 0 {
                t8x!(Ru, "ASN не выбраны", &ln)
            } else {
                &strfmt!(t8x!(Ru, "Выбрано ASN: {count}", &ln),
                    count => selected_count
                )?
            };
            
            state.set_step(ProxySelectionStep::MainMenu);
            get_proxy_dialogue.update(state).await?;
            
            bot.answer_callback_query(q.id.clone())
                .text(text)
                .send()
                .await?;
            
            let keyboard = make_additional_location_settings_keyboard(ln, pool);
            bot.edit_message_text(message.chat.id, message.id, t8x!(Ru, r#"
                    <b>🌍 Настройки локации</b>
                    <b>⚠️ Внимание!</b>
                    При использовании этой опции трафик списывается <b>в двойном объеме.</b>
                "#, &ln))
                .reply_markup(keyboard)
                .await?;
        }
        ProxyASNClearSearch => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            state.clear_search();
            let data = state.storage(pool);
            show_selection_filtered(&bot, &message, pool, 0, &data.included_asns, "", &settings, asn_selection_config(settings.language)).await?;

            get_proxy_dialogue.update(state).await?;
        }
        ProxyExcludedASNPage { page } => {
            bot.answer_callback_query(q.id.clone()).await?;
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);
            data.page = page;
            show_selection_filtered(&bot, &message, pool, page, &data.excluded_asns, "", &settings, excluded_asn_selection_config(settings.language)).await?;
            get_proxy_dialogue.update(state).await?;
        }
        ProxyExcludedASNSelect { index } => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            log::debug!("search_query: {:?}, index: {:?}", state.clone().search_query(), index);

            let search_query = state.search_query().to_owned();
            let data = state.storage_mut(pool);

            let start_idx = data.page * ITEMS_PER_PAGE;
            
            let mut sorted_asns: Vec<_> = data.excluded_asns.iter().map(|loc| &loc.location).cloned().collect();
            sorted_asns.sort_by(|a, b| a.name.cmp(&b.name));
            
            if !search_query.is_empty() {
                let search_lower = search_query.to_lowercase();
                sorted_asns.retain(|asn| {
                    asn.name.to_lowercase().contains(&search_lower) ||
                    asn.code.to_lowercase().contains(&search_lower)
                });
            }
            
            if let Some(asn) = sorted_asns.get(start_idx + index) {
                if let Some(current_state) = data.excluded_asns.iter_mut().find(|loc| &loc.location == asn) {
                    current_state.selected = !current_state.selected;
                }
                
                let page = data.page;

                show_selection_filtered(&bot, &message, pool, page, &data.excluded_asns, &search_query, &settings, excluded_asn_selection_config(settings.language)).await?;
                get_proxy_dialogue.update(state).await?;

            }
        }
        ProxySelectAllExcludedASNs => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);

            let all_selected = data.excluded_asns.iter().all(|loc| loc.selected);
            
            for loc in data.excluded_asns.iter_mut() {
                loc.selected = !all_selected;
            }
            
            let action_text = if all_selected {
                t8x!(Ru, "Все исключаемые ASN отменены", &ln)
            } else {
                t8x!(Ru, "Все исключаемые ASN выбраны", &ln)
            };
            
            bot.answer_callback_query(q.id.clone())
                .text(action_text)
                .send()
                .await?;
            
            show_selection_filtered(&bot, &message, pool, data.page, &data.excluded_asns, "", &settings, excluded_asn_selection_config(settings.language)).await?;
            get_proxy_dialogue.update(state).await?;
        }
        ProxyExcludedASNConfirm => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);

            let selected_count = data.excluded_asns.iter().filter(|loc| loc.selected).count();
            let text = if selected_count == 0 {
                t8x!(Ru, "Исключаемые ASN не выбраны", &ln)
            } else {
                &strfmt!(t8x!(Ru, "Исключено ASN: {count}", &ln),
                    count => selected_count
                )?
            };
            
            state.set_step(ProxySelectionStep::MainMenu);
            get_proxy_dialogue.update(state).await?;
            
            bot.answer_callback_query(q.id.clone())
                .text(text)
                .send()
                .await?;
            
            show_proxy_settings_handler(bot, message, user_dialogue, get_proxy_dialogue, settings).await?;
        }
        ProxyExcludedASNClearSearch => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            state.clear_search();
            let data = state.storage(pool);
            show_selection_filtered(&bot, &message, pool, 0, &data.excluded_asns, "", &settings, excluded_asn_selection_config(settings.language)).await?;

            get_proxy_dialogue.update(state).await?;
        }
        ProxyZipCodePage { page } => {
            bot.answer_callback_query(q.id.clone()).await?;
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);
            data.page = page;
            show_selection_filtered(&bot, &message, pool, page, &data.zip_codes, "", &settings, zip_code_selection_config(settings.language)).await?;
            get_proxy_dialogue.update(state).await?;
        }
        ProxyZipCodeSelect { index } => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            log::debug!("search_query: {:?}, index: {:?}", state.clone().search_query(), index);

            let search_query = state.search_query().to_owned();
            let data = state.storage_mut(pool);

            let start_idx = data.page * ITEMS_PER_PAGE;
            
            let mut sorted_zip_codes: Vec<_> = data.zip_codes.iter().map(|loc| &loc.location).cloned().collect();
            sorted_zip_codes.sort_by(|a, b| a.name.cmp(&b.name));
            
            if !search_query.is_empty() {
                let search_lower = search_query.to_lowercase();
                sorted_zip_codes.retain(|zip_code| {
                    zip_code.name.to_lowercase().contains(&search_lower) ||
                    zip_code.code.to_lowercase().contains(&search_lower)
                });
            }
            
            if let Some(zip_code) = sorted_zip_codes.get(start_idx + index) {
                if let Some(current_state) = data.zip_codes.iter_mut().find(|loc| &loc.location == zip_code) {
                    current_state.selected = !current_state.selected;
                }
                
                let page = data.page;

                show_selection_filtered(&bot, &message, pool, page, &data.zip_codes, &search_query, &settings, zip_code_selection_config(settings.language)).await?;
                get_proxy_dialogue.update(state).await?;

            }
        }
        ProxySelectAllZipCodes => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);

            let all_selected = data.zip_codes.iter().all(|loc| loc.selected);
            
            for loc in data.zip_codes.iter_mut() {
                loc.selected = !all_selected;
            }
            
            let action_text = if all_selected {
                t8x!(Ru, "Все ZIP-коды отменены", &ln)
            } else {
                t8x!(Ru, "Все ZIP-коды выбраны", &ln)
            };
            
            bot.answer_callback_query(q.id.clone())
                .text(action_text)
                .send()
                .await?;
            
            show_selection_filtered(&bot, &message, pool, data.page, &data.zip_codes, "", &settings, zip_code_selection_config(settings.language)).await?;
            get_proxy_dialogue.update(state).await?;
        }
        ProxyZipCodeConfirm => {
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            let data = state.storage_mut(pool);

            let selected_count = data.zip_codes.iter().filter(|loc| loc.selected).count();
            let text = if selected_count == 0 {
                t8x!(Ru, "ZIP-коды не выбраны", &ln)
            } else {
                &strfmt!(t8x!(Ru, "Выбрано ZIP-кодов: {count}", &ln),
                    count => selected_count
                )?
            };
            
            state.set_step(ProxySelectionStep::MainMenu);
            get_proxy_dialogue.update(state).await?;
            
            bot.answer_callback_query(q.id.clone())
                .text(text)
                .send()
                .await?;
            
            let keyboard = make_additional_location_settings_keyboard(ln, pool);
            bot.edit_message_text(message.chat.id, message.id, t8x!(Ru, r#"
                    <b>🌍 Настройки локации</b>
                    <b>⚠️ Внимание!</b>
                    При использовании этой опции трафик списывается <b>в двойном объеме.</b>
                "#, &ln))
                .reply_markup(keyboard)
                .await?;
        }
        ProxyZipCodeClearSearch => {
            bot.answer_callback_query(q.id.clone()).await?;
            
            let (mut state, pool) = handle_state(&bot, &message, &user_dialogue, &get_proxy_dialogue, &settings, ln).await?;

            state.clear_search();
            let data = state.storage(pool);
            show_selection_filtered(&bot, &message, pool, 0, &data.zip_codes, "", &settings, zip_code_selection_config(settings.language)).await?;

            get_proxy_dialogue.update(state).await?;
        }
    }

    Ok(())
}

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

    let message = t8x!(Ru, "Выберите регион:", &ln);
    let keyboard = make_region_selection_keyboard(ln, pool);
    bot.send_message(msg.chat.id, message)
        .reply_markup(keyboard)
        .await?;
    Ok(())
}

fn make_region_selection_keyboard(ln: Language, pool: PoolType) -> InlineKeyboardMarkup {
    let mut buttons: Vec<InlineButton<String>> = ProxyRegion::iter().map(|region| {
        (region.display_name_custom(&ln, true), GetProxyCallback::ProxySetRegion { region }.to_string()).into()
    }).collect();
    
    let back = (t8x!(Ru, "🔙Назад", &ln), GetProxyCallback::GetProxy { pool }.to_string()).into();
    buttons.push(back);

    make_inline_keyboard(buttons, 2)
}

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

    log::trace!("handle_country_search_query");
    
    let Some(text) = msg.text() else {
        bot.send_message(msg.chat.id, t8x!(Ru, "Пожалуйста, отправьте текстовое сообщение для поиска", &ln))
            .await?;
        return Ok(());
    };

    let (mut state, _) = handle_state(&bot, &msg, &user_dialogue, &dialogue, &settings, ln).await?;

    state.set_search_query(text);

    bot.delete_message(msg.chat.id, msg.id).await?;

    let ProxySelectionStep::ChoseSearchable { step, .. } = state.step().clone() else {
        return Err("Invalid step".into());
    };

    let Some(ref current_message) = state.current_message.clone() else {
        return Err("Current message id not found".into());
    };
    dialogue.update(state).await?;


    let (state, pool) = handle_state(&bot, current_message, &user_dialogue, &dialogue, &settings, settings.language).await?;
    let storage = state.storage(pool);
    let search_query = state.search_query();

    match step {
        ProxySearcheble::ChoseCountry => {
            show_selection_filtered(&bot, current_message, pool, 0, &storage.countries, search_query, &settings, country_selection_config(settings.language)).await?;
        }
        ProxySearcheble::ChoseState => {
            show_selection_filtered(&bot, current_message, pool, 0, &storage.states, search_query, &settings, state_selection_config(settings.language)).await?;
        }
        ProxySearcheble::ChoseCity => {
            show_selection_filtered(&bot, current_message, pool, 0, &storage.cities, search_query, &settings, city_selection_config(settings.language)).await?;
        }
        ProxySearcheble::ChoseAsn => {
            show_selection_filtered(&bot, current_message, pool, 0, &storage.included_asns, search_query, &settings, asn_selection_config(settings.language)).await?;
        }
        ProxySearcheble::ChoseExcludeAsn => {
            show_selection_filtered(&bot, current_message, pool, 0, &storage.excluded_asns, search_query, &settings, excluded_asn_selection_config(settings.language)).await?;
        }
        ProxySearcheble::ChoseZip => {
            show_selection_filtered(&bot, current_message, pool, 0, &storage.zip_codes, search_query, &settings, zip_code_selection_config(settings.language)).await?;
        }
    }

    Ok(())
}

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

    let (mut state, pool) = handle_state(&bot, &msg, &user_dialogue, &dialogue, &settings, ln).await?;
    state.promise_to_remove(msg.clone());
    
    let Some(text) = msg.text() else {
        bot.send_message(msg.chat.id, t8x!(Ru, "Пожалуйста, отправьте число", &ln))
            .await?;
        return Ok(());
    };

    let amount = match text.trim().parse::<u32>() {
        Ok(amount) if amount > 0 && amount <= MAX_AMOUNT => amount,
        Ok(_) => {
            let message = bot.send_message(msg.chat.id, strfmt!(t8x!(Ru, 
                    "Количество должно быть от 1 до {MAX_AMOUNT}", &ln),
                    MAX_AMOUNT)?
                )
                .await?;
            state.promise_to_remove(message);
            dialogue.update(state).await?;
            return Ok(());
        }
        Err(_) => {
            let message = bot.send_message(msg.chat.id, t8x!(Ru, "Пожалуйста, введите правильное число", &ln))
                .await?;
            state.promise_to_remove(message);
            dialogue.update(state).await?;
            return Ok(());
        }
    };

    state.storage_mut(pool).amount = amount;
    state.set_step(ProxySelectionStep::MainMenu);

    let Some(ref current_message) = state.current_message.clone() else {
        return Err("Current message id not found".into());
    };

    state.garbage_collect(&bot).await?;

    dialogue.update(state).await?;

    show_proxy_settings_handler(bot, current_message.clone(), user_dialogue, dialogue, settings).await?;

    Ok(())
}

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

    let (mut state, pool) = handle_state(&bot, &msg, &user_dialogue, &dialogue, &settings, ln).await?;

    state.promise_to_remove(msg.clone());
    
    let Some(text) = msg.text() else {
        let message = bot.send_message(msg.chat.id, t8x!(Ru, "Пожалуйста, отправьте число", &ln))
            .await?;
        state.promise_to_remove(message);
        dialogue.update(state).await?;
        return Ok(());
    };

    let interval = match text.trim().parse::<u32>() {
        Ok(interval) if (1..=MAX_INTERVAL).contains(&interval) => interval,
        Ok(_) => {
            let message = bot.send_message(msg.chat.id, t8x!(Ru, "Время ротации должно быть от 1 до {MAX_INTERVAL} минут", &ln))
                .await?;
            state.promise_to_remove(message);
            dialogue.update(state).await?;
            return Ok(());
        }
        Err(_) => {
            let message = bot.send_message(msg.chat.id, t8x!(Ru, "Пожалуйста, введите правильное число", &ln))
                .await?;
            state.promise_to_remove(message);
            dialogue.update(state).await?;
            return Ok(());
        }
    };

    let data = state.storage_mut(pool);
    data.r#type = ProxyType::Sticky(Some(interval));
    state.set_step(ProxySelectionStep::MainMenu);

    let Some(ref current_message) = state.current_message.clone() else {
        return Err("Current message id not found".into());
    };

    state.garbage_collect(&bot).await?;

    dialogue.update(state).await?;

    show_proxy_settings_handler(bot, current_message.clone(), user_dialogue, dialogue, settings).await?;

    Ok(())
}

pub fn make_additional_location_settings_keyboard(ln: Language, pool: PoolType) -> InlineKeyboardMarkup {
    let mut keyboard = InlineKeyboardMarkup::default();
    
    keyboard = keyboard.append_row(vec![
        InlineKeyboardButton::callback(
            t8x!(Ru, "🌏Выбор штатов 🌏", &ln),
            GetProxyCallback::ProxySelectStates.to_string()
        )
    ]);
    
    keyboard = keyboard.append_row(vec![
        InlineKeyboardButton::callback(
            t8x!(Ru, "🌏Выбор города 🌏", &ln),
            GetProxyCallback::ProxySelectCities.to_string()
        )
    ]);
    
    keyboard = keyboard.append_row(vec![
        InlineKeyboardButton::callback(
            t8x!(Ru, "🌏Выбор ASN 🌏", &ln),
            GetProxyCallback::ProxySelectASNs.to_string()
        )
    ]);
    
    keyboard = keyboard.append_row(vec![
        InlineKeyboardButton::callback(
            t8x!(Ru, "🌏Выбор ZIP-кодов 🌏", &ln),
            GetProxyCallback::ProxySelectZipCodes.to_string()
        )
    ]);
    
    keyboard = keyboard.append_row(vec![
        InlineKeyboardButton::callback(
            t8x!(Ru, "🔙Назад🔙", &ln),
            GetProxyCallback::GetProxy {pool}.to_string()
        )
    ]);
    
    keyboard
}

pub fn make_format_selection_keyboard(ln: Language, pool: PoolType) -> InlineKeyboardMarkup {
    let mut keyboard = InlineKeyboardMarkup::default();

    for format in ProxyFormat::iter() {
        keyboard = keyboard.append_row(vec![
            InlineKeyboardButton::callback(
                format.to_string(),
                GetProxyCallback::ProxyFormatSelect { format }.to_string()
            )
        ]);
    }
    
    keyboard = keyboard.append_row(vec![
        InlineKeyboardButton::callback(
            t8x!(Ru, "🔙Назад🔙", &ln),
            GetProxyCallback::GetProxy { pool }.to_string()
        )
    ]);
    
    keyboard
}

async fn handle_state(
    bot: &Bot,
    msg: &Message,
    user_dialogue: &MasterDialogue,
    get_proxy_dialogue: &GetProxyDialogue,
    settings: &HandlerSettings,
    ln: Language
) -> Result<(GetProxyStates, PoolType), HandlerError> {
    let Some(state) = get_proxy_dialogue.get().await? else {
        error_handler(bot.clone(), user_dialogue.clone(), msg.clone(), *settings).await?;
        return Err("State not found".into());
    };

    let Some(pool) = state.pool else {
        error_handler_with_message(bot.clone(), user_dialogue.clone(), msg.clone(), *settings, t8x!(Ru, "Ошибка при получении пула, попробуйте снова", &ln)).await?;
        return Err("Pool not found".into());
    };

    Ok((state, pool))
}

#[derive(Clone)]
pub struct SelectionConfig {
    pub header_no_search: String,
    pub header_with_search: String,
    pub search_hint: String,
    pub not_found_text: String,
    pub select_callback: Arc<dyn Fn(usize) -> GetProxyCallback + Send + Sync>,
    pub page_callback: Arc<dyn Fn(usize) -> GetProxyCallback + Send + Sync>,
    pub select_all_callback: GetProxyCallback,
    pub clear_search_callback: GetProxyCallback,
    pub confirm_callback: GetProxyCallback,
}



pub async fn show_selection_filtered(
    bot: &Bot,
    message: &Message,
    pool: PoolType,
    page: usize,
    items: &Vec<SelectableLocation>,
    search_query: &str,
    settings: &HandlerSettings,
    config: SelectionConfig,
) -> HandlerResult {
    log::trace!("show_selection_filtered");
    let ln = settings.language;
    
    let mut sorted_items: Vec<_> = items.iter().collect();
    sorted_items.sort_by(|a, b| a.location.name.cmp(&b.location.name));
    
    if !search_query.is_empty() {
        let search_lower = search_query.to_lowercase();
        sorted_items.retain(|item| {
            item.location.name.to_lowercase().contains(&search_lower) ||
            item.location.code.to_lowercase().contains(&search_lower)
        });
    }
    
    let start_idx = page * ITEMS_PER_PAGE;
    let end_idx = std::cmp::min(start_idx + ITEMS_PER_PAGE, sorted_items.len());
    let total_pages = sorted_items.len().div_ceil(ITEMS_PER_PAGE);
    
    let mut keyboard = InlineKeyboardMarkup::default();
    
    let selected_count = items.iter().filter(|item| item.selected).count();
    
    let header = if search_query.is_empty() {
        strfmt!(&config.header_no_search,
            pool_name => pool.display_name(&ln),
            selected_count => selected_count
        )?
    } else {
        strfmt!(&config.header_with_search,
            query => search_query.to_string(),
            found_count => sorted_items.len(),
            selected_count => selected_count
        )?
    };
    
    let mut item_buttons = Vec::new();
    for (i, item) in sorted_items[start_idx..end_idx].iter().enumerate() {
        let prefix = if item.selected { "✅ " } else { "" };
        let button_text = format!("{}{} ({})", prefix, item.location.name, item.location.count);
        
        let button = InlineKeyboardButton::callback(
            button_text,
            (config.select_callback)(i).to_string()
        );
        item_buttons.push(button);
    }

    for row_buttons in item_buttons.chunks(ITEMS_PER_ROW) {
        keyboard = keyboard.append_row(row_buttons.to_vec());
    }
    
    if total_pages > 1 {
        let mut pagination_row = Vec::new();
        
        if page > 0 {
            pagination_row.push(InlineKeyboardButton::callback(
                "⬅️",
                (config.page_callback)(page - 1).to_string()
            ));
        }
        
        pagination_row.push(InlineKeyboardButton::callback(
            format!("{}/{}", page + 1, total_pages),
            Callback::Noop.to_string()
        ));
        
        if page < total_pages - 1 {
            pagination_row.push(InlineKeyboardButton::callback(
                "➡️",
                (config.page_callback)(page + 1).to_string()
            ));
        }
        
        keyboard = keyboard.append_row(pagination_row);
    }
    
    let mut action_buttons = vec![
        InlineKeyboardButton::callback(
            t8x!(Ru, "🌍Все", &ln),
            config.select_all_callback.to_string()
        ),
        InlineKeyboardButton::callback(
            t8x!(Ru, "✅Подтвердить", &ln),
            config.confirm_callback.to_string()
        ),
    ];

    if !search_query.is_empty() {
        action_buttons.insert(0, InlineKeyboardButton::callback(
            t8x!(Ru, "🗑️Очистить поиск", &ln),
            config.clear_search_callback.to_string()
        ));
    }

    keyboard = keyboard.append_row(action_buttons);
    
    log::debug!("message_id: {:?}, search_query: {:?}", message.id, search_query);
    
    bot.edit_message_text(message.chat.id, message.id, header)
        .reply_markup(keyboard)
        .await?;
    
    Ok(())
}

fn country_selection_config(ln: Language) -> SelectionConfig {
    SelectionConfig {
        header_no_search: t8x!(Ru, r#"
                🌐 <b>Выбор стран для пула</b> <code>{pool_name}</code>:
                ────────────────────
                📍 <b>Выбрано:</b> <code>{selected_count} стран</code>

                💡 <b>Отправьте название страны для поиска</b>
                ✨ <b>Пример:</b> <code>USA</code>, <code>Germany</code>, <code>Japan</code>

                🔍 Вы можете выбрать несколько стран
            "#, &ln).to_string(),
        header_with_search: t8x!(Ru, r#"
                Поиск: <code>{query}</code>
                Найдено стран: <code>{found_count}</code> | Выбрано: <code>{selected_count}</code>
            "#, &ln).to_string(),
        search_hint: "💡 Отправьте название страны для поиска".to_string(),
        not_found_text: "Страны не найдены".to_string(),
        select_callback: Arc::new(|index| GetProxyCallback::ProxyCountrySelect { index }),
        page_callback: Arc::new(|page| GetProxyCallback::ProxyCountryPage { page }),
        select_all_callback: GetProxyCallback::ProxySelectAll,
        clear_search_callback: GetProxyCallback::ProxyClearSearch,
        confirm_callback: GetProxyCallback::ProxyCountryConfirm,
    }
}

fn state_selection_config(ln: Language) -> SelectionConfig {
    SelectionConfig {
        header_no_search: t8x!(Ru, r#"
                Выберите штаты для пула {pool_name}:
                Выбрано: {selected_count}
            
                💡 Отправьте название штата для поиска
            "#, &ln).to_string(),
        header_with_search: t8x!(Ru, r#"
                Поиск: <code>{query}</code>
                Найдено штатов: <code>{found_count}</code> | Выбрано: <code>{selected_count}</code>
            "#, &ln).to_string(),
        search_hint: "💡 Отправьте название штата для поиска".to_string(),
        not_found_text: "Штаты не найдены".to_string(),
        select_callback: Arc::new(|index| GetProxyCallback::ProxyStateSelect { index }),
        page_callback: Arc::new(|page| GetProxyCallback::ProxyStatePage { page }),
        select_all_callback: GetProxyCallback::ProxySelectAllStates,
        clear_search_callback: GetProxyCallback::ProxyStateClearSearch,
        confirm_callback: GetProxyCallback::ProxyStateConfirm,
    }
}

fn city_selection_config(ln: Language) -> SelectionConfig {
    SelectionConfig {
        header_no_search: t8x!(Ru, r#"
                Выберите города для пула {pool_name}:
                Выбрано: {selected_count}
            
                💡 Отправьте название города для поиска
            "#, &ln).to_string(),
        header_with_search: t8x!(Ru, r#"
                Поиск: <code>{query}</code>
                Найдено городов: <code>{found_count}</code> | Выбрано: <code>{selected_count}</code>
            "#, &ln).to_string(),
        search_hint: "💡 Отправьте название города для поиска".to_string(),
        not_found_text: "Города не найдены".to_string(),
        select_callback: Arc::new(|index| GetProxyCallback::ProxyCitySelect { index }),
        page_callback: Arc::new(|page| GetProxyCallback::ProxyCityPage { page }),
        select_all_callback: GetProxyCallback::ProxySelectAllCities,
        clear_search_callback: GetProxyCallback::ProxyCityClearSearch,
        confirm_callback: GetProxyCallback::ProxyCityConfirm,
    }
}

fn asn_selection_config(ln: Language) -> SelectionConfig {
    SelectionConfig {
        header_no_search: t8x!(Ru, r#"
                Выберите ASN для пула {pool_name}:
                Выбрано: {selected_count}
            
                💡 Отправьте номер или название ASN для поиска
            "#, &ln).to_string(),
        header_with_search: t8x!(Ru, r#"
                Поиск: <code>{query}</code>
                Найдено ASN: <code>{found_count}</code> | Выбрано: <code>{selected_count}</code>
            "#, &ln).to_string(),
        search_hint: "💡 Отправьте номер или название ASN для поиска".to_string(),
        not_found_text: "ASN не найдены".to_string(),
        select_callback: Arc::new(|index| GetProxyCallback::ProxyASNSelect { index }),
        page_callback: Arc::new(|page| GetProxyCallback::ProxyASNPage { page }),
        select_all_callback: GetProxyCallback::ProxySelectAllASNs,
        clear_search_callback: GetProxyCallback::ProxyASNClearSearch,
        confirm_callback: GetProxyCallback::ProxyASNConfirm,
    }
}

fn excluded_asn_selection_config(ln: Language) -> SelectionConfig {
    SelectionConfig {
        header_no_search: t8x!(Ru, r#"
                Выберите ASN для исключения из пула {pool_name}:
                Выбрано: {selected_count}
            
                💡 Отправьте номер или название ASN для поиска
            "#, &ln).to_string(),
        header_with_search: t8x!(Ru, r#"
                Поиск: <code>{query}</code>
                Найдено ASN: <code>{found_count}</code> | Выбрано: <code>{selected_count}</code>
            "#, &ln).to_string(),
        search_hint: "💡 Отправьте номер или название ASN для поиска".to_string(),
        not_found_text: "ASN не найдены".to_string(),
        select_callback: Arc::new(|index| GetProxyCallback::ProxyExcludedASNSelect { index }),
        page_callback: Arc::new(|page| GetProxyCallback::ProxyExcludedASNPage { page }),
        select_all_callback: GetProxyCallback::ProxySelectAllExcludedASNs,
        clear_search_callback: GetProxyCallback::ProxyExcludedASNClearSearch,
        confirm_callback: GetProxyCallback::ProxyExcludedASNConfirm,
    }
}

fn zip_code_selection_config(ln: Language) -> SelectionConfig {
    SelectionConfig {
        header_no_search: t8x!(Ru, r#"
                Выберите ZIP-коды для пула {pool_name}:
                Выбрано: {selected_count}
            
                💡 Отправьте ZIP-код для поиска
            "#, &ln).to_string(),
        header_with_search: t8x!(Ru, r#"
                Поиск: <code>{query}</code>
                Найдено ZIP-кодов: <code>{found_count}</code> | Выбрано: <code>{selected_count}</code>
            "#, &ln).to_string(),
        search_hint: "💡 Отправьте ZIP-код для поиска".to_string(),
        not_found_text: "ZIP-коды не найдены".to_string(),
        select_callback: Arc::new(|index| GetProxyCallback::ProxyZipCodeSelect { index }),
        page_callback: Arc::new(|page| GetProxyCallback::ProxyZipCodePage { page }),
        select_all_callback: GetProxyCallback::ProxySelectAllZipCodes,
        clear_search_callback: GetProxyCallback::ProxyZipCodeClearSearch,
        confirm_callback: GetProxyCallback::ProxyZipCodeConfirm,
    }
}
