mod dev_vlog;
pub use dev_vlog::*;

mod broadcast;
pub use broadcast::*;

mod schema;
pub use schema::*;

mod callback_handler;
pub use callback_handler::*;

mod withdraw;
pub use withdraw::*;

mod promo;
pub use promo::*;

mod fix_i18n;
pub use fix_i18n::*;

mod filter;
pub use filter::*;

mod reseller_traffic;
pub use reseller_traffic::*;

use rust_shared::i18n::Language;
use crate::ui::prelude::*;
use chrono::{Days, FixedOffset, NaiveDate, NaiveTime};
use lazy_static::lazy_static;
use std::fmt::{self, Display, Formatter};
use std::sync::Arc;
use strum_macros::EnumString;

lazy_static! {
    static ref UTC_PLUS_3: FixedOffset =
        FixedOffset::east_opt(3 * 3600).expect("Invalid offset");
}

#[derive(Clone, EnumString, PartialEq, Eq)]
pub enum LoadTarget {
    #[strum(serialize = "i18n")]
    I18n,
}

#[derive(BotCommands, Clone)]
#[command(rename_rule = "snake_case")]
pub enum Command {
    StartAdmin,
    Admin,
    Promo,
    Profile,
    Stats,
    Load(LoadTarget),
    FixI18n,
}

impl Display for AdminCallback {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", serde_json::to_string(self).unwrap())
    }
}

pub async fn admin_entrypoint(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    settings: HandlerSettings,
) -> HandlerResult {
    let message = "Админ панель";

    let ln = &settings.language;

    let buttons = vec![
        vec![t8x!(Ru, "🎫 Промокоды",  ln, "menu-button"), t8x!(Ru, "📈 Статистика",      ln, "menu-button")],
        vec![t8x!(Ru, "‍🤝‍ Ссылки",     ln, "menu-button"), t8x!(Ru, "⚙️ Настройки",       ln, "menu-button")],
        vec![t8x!(Ru, "📢 Рассылка",   ln, "menu-button"), t8x!(Ru, "🚩 Баннеры",         ln, "menu-button")],
        vec![t8x!(Ru, "💾 Бекап",      ln, "menu-button"), t8x!(Ru, "💳 Заявки на вывод", ln, "menu-button")],
        vec![t8x!(Ru, "📖 Разработка", ln, "menu-button")],
    ];

    let keyboard = make_keyboard_from_vec(buttons);

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

    dialogue.update_kind(MasterStateKind::Admin).await?;

    Ok(())
}

async fn show_banner_menu(
    bot: Bot,
    _dialogue: MasterDialogue,
    msg: Message,
    settings: HandlerSettings,
) -> HandlerResult {
    let message = "🚩 <b>Управление баннерами</b>\n\nВыберите действие:";
    let ln = &settings.language;

    let buttons = vec![
        vec![t8x!(Ru, "🚩Создать",         ln, "menu-button")],
        vec![t8x!(Ru, "🚩Редактировать",   ln, "menu-button")],
        vec![t8x!(Ru, "🔙 Назад в панель", ln, "menu-button")],
    ];

    let keyboard = make_keyboard_from_vec(buttons);

    bot.send_message(msg.chat.id, message)
        .parse_mode(teloxide::types::ParseMode::Html)
        .reply_markup(keyboard)
        .await?;

    Ok(())
}

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

    let percent_info = match api::get_referral_percent().await {
        Ok(percent) => format!("{}", percent * 100.0),
        Err(e) => {
            log::error!("Failed to get referral percent: {}", e);
            "ошибка загрузки".to_string()
        }
    };
    
    let message = format!(
        indoc!{r#"
            ⚙️ <b>Настройки системы</b>
            
            📊 Текущий реферальный процент: <b>{}%</b>
            
            Выберите действие:
        "#},
        percent_info
    );

    let buttons = vec![
        vec![t8x!(Ru, "💸Cтоимость",           ln, "menu-button")],
        vec![t8x!(Ru, "⚙️Реферальный %",       ln, "menu-button")],
        vec![t8x!(Ru, "💳 Платаёжные системы", ln, "menu-button")],
        vec![t8x!(Ru, "🔙 Назад в панель",     ln, "menu-button")],
    ];

    let keyboard = make_keyboard_from_vec(buttons);

    bot.send_message(msg.chat.id, message)
        .parse_mode(teloxide::types::ParseMode::Html)
        .reply_markup(keyboard)
        .await?;

    dialogue.update_admin_kind(AdminStateKind::Menu).await?;

    Ok(())
}

async fn change_referral_percent_handler(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
) -> HandlerResult {
    let message_text = indoc! {r#"
        ⚙️ <b>Изменение процента реферальной программы</b>
        
        📝 Введите новый процент реферальной программы (например: 0.05, что будет 5%):
        
        💡 <i>Текущий процент будет заменен на указанный</i>
    "#};

    bot.send_message(msg.chat.id, message_text)
        .parse_mode(teloxide::types::ParseMode::Html)
        .reply_markup(make_inline_keyboard(
            vec![("🔙 Отмена", AdminCallback::AdminMenu).into()],
            1,
        ))
        .await?;

    dialogue.update_admin_kind(AdminStateKind::ReceiveReferralPercent).await?;

    Ok(())
}

async fn change_price_handler(
    bot: Bot,
    msg: Message,
    _dialogue: MasterDialogue,
    _settings: HandlerSettings,
) -> HandlerResult {
    let loading_msg = bot.send_message(msg.chat.id, "⏳ Загружаю текущие цены...")
        .await?;
    
    let mut pool_buttons = Vec::new();
    let mut price_summary = String::from("💰 <b>Текущие цены за гигабайт:</b>\n\n");
    let all_pools = PoolType::all();
    
    for pool in all_pools {
        let (price_text, button_price_text) = match api::get_current_price(pool).await {
            Ok(price) => {
                let formatted_price = format!("${:.2}", price);
                (formatted_price.clone(), format!(" ({})", formatted_price))
            },
            Err(_) => {
                ("не установлена".to_string(), " (не установлена)".to_string())
            }
        };
        
        price_summary.push_str(&format!("• {} - {}\n", pool_type_to_display_name(&pool), price_text));
        
        let button_text = format!("{}{}", pool_type_to_display_name(&pool), button_price_text);
        pool_buttons.push((button_text, AdminCallback::ChangePricePool { pool }));
    }
    
    price_summary.push_str("\n📝 Выберите пул для изменения цены:");
    
    pool_buttons.push(("🔙 Отмена".to_string(), AdminCallback::AdminMenu));
    
    let keyboard = make_inline_keyboard(
        pool_buttons.into_iter().map(|(text, callback)| (text, callback).into()).collect(),
        2,
    );

    bot.edit_message_text(loading_msg.chat.id, loading_msg.id, price_summary)
        .parse_mode(teloxide::types::ParseMode::Html)
        .reply_markup(keyboard)
        .await?;
    
    Ok(())
}

async fn stats_handler(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
) -> HandlerResult {
    let stats = api::get_stats().await?;

    let mut message = format!(
        indoc!(
            "
            Всего пользователей: <b>{}</b>
            Пользователей на сайте: <b>{}</b>
            Aктиваций юзеров: <b>{}</b>
            Пользователей, хоть раз купившие трафик: <b>{}</b>
            Баланс: <b>{:.2}</b>
            Общее количество реф. баланса: <b>{:.2}</b>
            💰Сумма всех покупок: <b>{:.2}$</b>
            🛒Количество покупок: <b>{}</b>
            💳Часто покупаемое количество:
            "
        ),
        stats.total_users,
        stats.web_users,
        stats.activated_subusers,
        stats.users_with_purchases,
        stats.di_balance,
        stats.ref_balance,
        stats.paid_total,
        stats.purchases_total,
    );

    for entry in stats.amount_top {
        message.push_str(&format!(
            "<b>{} GB</b> ({} раз)\n",
            entry.gb, entry.purchases
        ));
    }

    message.push_str(indoc!(
        "
        ➖➖➖➖➖➖➖➖➖➖
        😎Активные покупатели:
        "
    ));

    for entry in stats.users_top.iter().take(5) {
        let ending = if entry.purchases % 10 < 2 || entry.purchases % 10 > 4 {
            "раз"
        } else {
            "раза"
        };

        message.push_str(&format!(
            "{}: \t\t купил {} {}\n",
            entry.user_id, entry.purchases, ending
        ));
    }

    message.push_str(indoc!(
        "
        ➖➖➖➖➖➖➖➖➖➖
        💡 <b>Для просмотра статистики отдельного пользователя, введите его id</b>
        "
    ));

    bot.send_message(msg.chat.id, message)
        .reply_markup(stats_reply_markup())
        .await?;

    dialogue.update_admin_kind(AdminStateKind::StatsReceiveUserId { user_id: None }).await?;
    Ok(())
}

async fn backup_handler(
    bot: Bot,
    _dialogue: MasterDialogue,
    msg: Message,
) -> HandlerResult {
    let Ok(prod_db_url) = std::env::var("PROD_DB_URL") else {
      return Err("cannot read prod db url".into());
    };

    let output = std::process::Command::new("pg_dump")
        .arg(format!("--dbname={}", prod_db_url)) // adjust
        .output()
        .expect("failed to run pg_dump");

    if output.status.success() {
        let file = InputFile::memory(output.stdout).file_name("dump.sql");
        bot.send_document(msg.chat.id, file).await?;
    } else {
        bot.send_message(msg.chat.id, "pg_dump failed").await?;
    }

    Ok(())
}

async fn stats_command_handler(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
) -> HandlerResult {
    daily_stats_handler(bot, dialogue, msg, true).await?;
    Ok(())
}

async fn profile_command_handler(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
) -> HandlerResult {
    let text = msg.text().unwrap_or("");
    
    if text.trim() == "/profile" {
        show_profile_help_message(bot, msg).await?;
        return Ok(());
    }
    
    let args: Vec<&str> = text.split_whitespace().collect();
    if args.len() != 2 {
        show_profile_help_message(bot, msg).await?;
        return Ok(());
    }
    
    let user_id = args[1];
    
    bot.send_message(msg.chat.id, "⏳ Загрузка профиля пользователя...")
        .await?;

    match api::get_profile_with_optional_headers(user_id.to_string(), Some(user_id.to_string()), Some(user_id.to_string())).await {
        Ok(profile) => {
            let user_id = profile.id.clone();
            dialogue.update_admin_kind(AdminStateKind::StatsReceiveUserId { user_id: Some(user_id.clone()) }).await?;

            let personal_referral_percent = profile.personal_referral_percent.clone().unwrap_or("не указан".to_string());
            let mut message = format!(
                indoc!(
                    "
                    👤 <b>Профиль пользователя</b>
                    
                    <b>🆔 ID:</b> {id}
                    <b>👤 Имя пользователя:</b> {username}
                    <b>🛒 Количество покупок:</b> {purchases}
                    <b>🛒 Количество ГБ куплено:</b> {purchase_sum_gb}
                    <b>💰 Реферальный баланс:</b> {referral_balance:.2}
                    <b>💰 Реферальный процент:</b> {personal_referral_percent}
                    
                    <b>💾 Остатки по пулам:</b>
                    "
                ),
                id = user_id,
                username = profile.username.as_deref().unwrap_or("Не указано"),
                purchases = profile.purchase_count,
                purchase_sum_gb = profile.purchase_sum_gb,
                referral_balance = profile.referral_balance,
                personal_referral_percent = personal_referral_percent,
            );

            for pool_type in PoolType::all() {
                let pool_info = profile.get_pool_or_default(&pool_type);
                let pool_name = pool_type.display_name(&Language::Ru);
                message.push_str(&format!(
                    "  • {}: {}\n",
                    pool_name,
                    pool_info.balance_format
                ));
            }

            let pools = profile.pools.keys().cloned().collect::<Vec<_>>();

            let mut keyboard = InlineKeyboardMarkup::default();
            
            keyboard = keyboard.append_row(vec![
                InlineKeyboardButton::callback(
                    "⚙️ % Реф. баланса",
                    AdminCallback::ChangeReferralPercent.to_string(),
                )
            ]);
            
            keyboard = keyboard.append_row(vec![
                InlineKeyboardButton::callback(
                    "📕 + Трафик",
                    AdminCallback::AddTraffic {
                        pool: None,
                    }.to_string(),
                ),
                InlineKeyboardButton::callback(
                    "🗑️ Сбросить баланс",
                    AdminCallback::DropBalance {
                        pool: None,
                    }.to_string(),
                ),
            ]);
            
            keyboard = keyboard.append_row(vec![
                InlineKeyboardButton::callback(
                    "📅 Нед. статистика",
                    AdminCallback::UserWeeklyStatsPoolChoice {
                        pools: pools.iter().map(|p| (*p).into()).collect(),
                    }.to_string(),
                ),
                InlineKeyboardButton::callback(
                    "📅 Мес. статистика",
                    AdminCallback::UserMonthlyStatsPoolChoice {
                        pools: pools.iter().map(|p| (*p).into()).collect(),
                    }.to_string(),
                ),
            ]);
            
            keyboard = keyboard.append_row(vec![
                InlineKeyboardButton::callback(
                    "🔙 Назад",
                    AdminCallback::AdminMenu.to_string(),
                )
            ]);

            bot.send_message(msg.chat.id, message)
                .parse_mode(teloxide::types::ParseMode::Html)
                .reply_markup(keyboard)
                .await?;
        }
        Err(e) => {
            bot.send_message(
                msg.chat.id,
                format!("❌ Ошибка загрузки профиля: {}", e),
            )
            .await?;
        }
    }

    Ok(())
}

async fn show_profile_help_message(bot: Bot, msg: Message) -> HandlerResult {
    let profile_help_message = indoc!(
        r#"
        ❓ Использование команды <code>/profile</code>:
        
        <code>/profile &lt;user_id&gt;</code> - показать профиль пользователя
        
        <b>Примеры:</b>
        <code>/profile 12345</code> - показать профиль пользователя с ID 12345
        <code>/profile user123</code> - показать профиль пользователя с ID user123
        
        💡 <i>Команда покажет детальную информацию о пользователе, включая балансы по пулам, статистику покупок и реферальную информацию</i>
        "#
    );

    bot.send_message(msg.chat.id, profile_help_message)
        .parse_mode(teloxide::types::ParseMode::Html)
        .await?;
    
    Ok(())
}

async fn stats_receive_user_id_handler(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
) -> HandlerResult {
    let user_id = match msg.text() {
        Some(id) => id.to_string(),
        None => {
            bot.send_message(msg.chat.id, "Произошла ошибка при вводе id").await?;
            return Ok(());
        }
    };

    dialogue.update_admin_kind(AdminStateKind::StatsReceiveUserId { user_id: Some(user_id.clone()) }).await?;

    user_stats_handler(bot, dialogue, msg, user_id).await?;

    Ok(())
}

async fn user_stats_handler(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    user_id: String,
) -> HandlerResult {
    bot.send_message(msg.chat.id, "⏳ Загрузка профиля пользователя...")
        .await?;

    match api::get_profile_with_optional_headers(user_id.clone(), Some(user_id.clone()), Some(user_id.clone())).await {
        Ok(profile) => {
            log::debug!("state: {:#?}", dialogue.get().await?);
            let user_id = profile.id.clone();
            dialogue.update_admin_kind(AdminStateKind::StatsReceiveUserId { user_id: Some(user_id.clone()) }).await?;

            let personal_referral_percent = profile.personal_referral_percent.clone().unwrap_or("не указан".to_string());
            let mut message = format!(
                indoc!(
                    "
                    👤 <b>Профиль пользователя</b>
                    
                    <b>🆔 ID:</b> {id}
                    <b>👤 Имя пользователя:</b> {username}
                    <b>🛒 Количество покупок:</b> {purchases}
                    <b>🛒 Количество ГБ куплено:</b> {purchase_sum_gb}
                    <b>💰 Реферальный баланс:</b> {referral_balance:.2}
                    <b>💰 Реферальный процент:</b> {personal_referral_percent}
                    
                    <b>💾 Остатки по пулам:</b>
                    "
                ),
                id = user_id,
                username = profile.username.as_deref().unwrap_or("Не указано"),
                purchases = profile.purchase_count,
                purchase_sum_gb = profile.purchase_sum_gb,
                referral_balance = profile.referral_balance,
                personal_referral_percent = personal_referral_percent,
            );

            for pool_type in PoolType::all() {
                let pool_info = profile.get_pool_or_default(&pool_type);
                let pool_name = pool_type.display_name(&Language::Ru);
                message.push_str(&format!(
                    "  • {}: {}\n",
                    pool_name,
                    pool_info.balance_format
                ));
            }

            let pools = profile.pools.keys().cloned().collect::<Vec<_>>();

            let mut keyboard = InlineKeyboardMarkup::default();
            
            keyboard = keyboard.append_row(vec![
                InlineKeyboardButton::callback(
                    "⚙️ % Реф. баланса",
                    AdminCallback::ChangeReferralPercent.to_string(),
                )
            ]);
            
            keyboard = keyboard.append_row(vec![
                InlineKeyboardButton::callback(
                    "📕 + Трафик",
                    AdminCallback::AddTraffic {
                        pool: None,
                    }.to_string(),
                ),
                InlineKeyboardButton::callback(
                    "🗑️ Сбросить баланс",
                    AdminCallback::DropBalance {
                        pool: None,
                    }.to_string(),
                ),
            ]);
            
            keyboard = keyboard.append_row(vec![
                InlineKeyboardButton::callback(
                    "📅 Нед. статистика",
                    AdminCallback::UserWeeklyStatsPoolChoice {
                        pools: pools.iter().map(|p| (*p).into()).collect(),
                    }.to_string(),
                ),
                InlineKeyboardButton::callback(
                    "📅 Мес. статистика",
                    AdminCallback::UserMonthlyStatsPoolChoice {
                        pools: pools.iter().map(|p| (*p).into()).collect(),
                    }.to_string(),
                ),
            ]);

            keyboard = keyboard.append_row(vec![
                InlineKeyboardButton::callback(
                    "Изменить реселлер трафик",
                    AdminCallback::ChangeResellerTraffic{ user_id }.to_string(),
                )
            ]);
            
            keyboard = keyboard.append_row(vec![
                InlineKeyboardButton::callback(
                    "🔙 Назад",
                    AdminCallback::AdminMenu.to_string(),
                )
            ]);

            bot.send_message(msg.chat.id, message)
                .parse_mode(teloxide::types::ParseMode::Html)
                .reply_markup(keyboard)
                .await?;
        }
        Err(e) => {
            bot.send_message(
                msg.chat.id,
                format!("❌ Ошибка загрузки профиля: {}", e),
            )
            .await?;
        }
    }

    Ok(())
}

fn stats_reply_markup() -> InlineKeyboardMarkup {
    let buttons = vec![
        ("📅Статистика за сегодня📅", AdminCallback::StatsToday).into(),
        ("📅Статистика по дате📅", AdminCallback::StatsByDate).into(),
        ("🔙Вернуться в меню🔙", AdminCallback::AdminMenu).into(),
    ];
    make_inline_keyboard(buttons, 1)
}

async fn daily_stats_handler(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    today_only: bool,
) -> HandlerResult {
    if today_only {
        api::send_daily_stats(None, None, msg.chat.id.0).await?;
    } else {
        let message = indoc!(
            "
                Введите дату или период в одном из форматов:
                • YYYY-MM-DD (для статистики за конкретный день)
                • YYYY-MM-DD/YYYY-MM-DD (для статистики за период)
            "
        );

        let keyboard = make_inline_keyboard(
            vec![("🔙Назад🔙", AdminCallback::AdminMenu).into()],
            1,
        );

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

        dialogue.update_admin_kind(AdminStateKind::StatsReceivePeriod).await?;
    }

    Ok(())
}

async fn stats_period_handler(bot: Bot, msg: Message) -> HandlerResult {
    let period_start;
    let period_end;

    if let Some(message) = msg.text() {
        if message.contains("/") {
            let dates: Vec<&str> = message.split('/').collect();

            if dates.len() != 2 {
                bot.send_message(
                    msg.chat.id,
                    "Неверный формат. Используйте YYYY-MM-DD/YYYY-MM-DD.",
                )
                .await?;
                return Ok(());
            }

            period_start = match NaiveDate::parse_from_str(dates[0], "%Y-%m-%d")
            {
                Ok(date) => date
                    .and_time(
                        NaiveTime::from_hms_opt(0, 0, 0)
                            .expect("Invalid time parameters"),
                    )
                    .and_local_timezone(*UTC_PLUS_3)
                    .unwrap(),
                Err(_) => {
                    bot.send_message(
                        msg.chat.id,
                        "Invalid start date format. Use YYYY-MM-DD.",
                    )
                    .await?;
                    return Ok(());
                }
            };

            period_end = match NaiveDate::parse_from_str(dates[1], "%Y-%m-%d") {
                Ok(date) => date
                    .and_time(
                        NaiveTime::from_hms_opt(0, 0, 0)
                            .expect("Invalid time parameters"),
                    )
                    .and_local_timezone(*UTC_PLUS_3)
                    .unwrap(),
                Err(_) => {
                    bot.send_message(
                        msg.chat.id,
                        "Invalid end date format. Use YYYY-MM-DD.",
                    )
                    .await?;
                    return Ok(());
                }
            };
        } else {
            match message.trim().parse::<NaiveDate>() {
                Ok(date) => {
                    period_start = date
                        .and_time(
                            NaiveTime::from_hms_opt(0, 0, 0)
                                .expect("Invalid time parameters"),
                        )
                        .and_local_timezone(*UTC_PLUS_3)
                        .unwrap();

                    period_end = period_start
                        .checked_add_days(Days::new(1))
                        .expect("always should be able to add one day");
                }
                Err(_) => {
                    let keyboard = make_inline_keyboard(
                        vec![("🔙Назад🔙", AdminCallback::AdminMenu).into()],
                        1,
                    );

                    bot.send_message(
                        msg.chat.id,
                        "Дата введена неверно. Повторите ввод. Формат: YYYY-MM-DD",
                    )
                    .reply_markup(keyboard)
                    .await?;

                    return Ok(());
                }
            }
        }
    } else {
        let keyboard = make_inline_keyboard(
            vec![("🔙Назад🔙", AdminCallback::AdminMenu).into()],
            1,
        );

        bot.send_message(msg.chat.id, "Неверный период. Повторите ввод")
            .reply_markup(keyboard)
            .await?;

        return Ok(());
    }

    if period_start > period_end {
        bot.send_message(
            msg.chat.id,
            "Дата начала периода должна быть раньше даты окончания",
        )
        .await?;

        return Ok(());
    }

    api::send_daily_stats(Some(period_start), Some(period_end), msg.chat.id.0).await?;

    Ok(())
}

async fn handle_user_detailed_stats(
    bot: Bot,
    msg: Message,
    pool: PoolType,
    dialogue: MasterDialogue,
    period: &str,
) -> HandlerResult {
    let Some(state) = dialogue.get().await? else {
        return Err("No state found in dialogue".into());
    };
    let AdminStateKind::StatsReceiveUserId { user_id } = state.admin_kind else {
        return Err("Expected StatsReceiveUserId state".into());
    };
    let Some(user_id) = user_id else {
        return Err("User ID is None".into());
    };

    log::trace!("{}", format!("handle_user_detailed_stats: user_id: {}, pool: {}, period: {}", user_id, pool, period).bright_black());
    
    bot.edit_message_text(
        msg.chat.id,
        msg.id,
        format!("⏳ Загрузка {} статистики для пользователя {}...", 
                if period == "week" { "недельной" } else { "месячной" }, 
                user_id),
    )
    .await?;

    match api::get_detailed_user_stats(
        user_id.clone(),
        pool.to_string(),
        period.to_string(),
        Some(10000), // Large limit to get all data
        Some(0),     // Start from beginning
        Some("minute".to_string()), // Minute-level aggregation
    )
    .await
    {
        Ok(stats) => {
            let json_content = serde_json::to_string_pretty(&stats)
                .map_err(|e| format!("Failed to serialize stats to JSON: {}", e))?;

            let pool_name = pool.display_name(&Language::Ru);
            let filename = format!(
                "user_{}_{}_{}_stats_{}.json",
                user_id,
                pool.to_string().to_lowercase(),
                period,
                chrono::Utc::now().format("%Y%m%d_%H%M%S")
            );

            let success_message = format!(
                indoc!{r#"
                    📊 <b>{period} статистика пользователя {user_id}</b>
                    
                    <b>Пул:</b> {pool_name}
                    <b>Период:</b> {period_name}
                    <b>Общее количество записей:</b> {total_count}
                    <b>Записей в файле:</b> {len}
                    
                    📁 JSON файл отправлен отдельным сообщением
                "#},
                period = if period == "week" { "Недельная" } else { "Месячная" },
                user_id = user_id.clone(),
                pool_name = pool_name.clone(),
                period_name = if period == "week" { "последняя неделя" } else { "последний месяц" },
                total_count = stats.total_count,
                len = stats.usage.len()
            );

            log::debug!("success_message: {}", success_message);

            bot.edit_message_text(msg.chat.id, msg.id, success_message)
                .reply_markup(make_inline_keyboard(
                    vec![("🔙 Вернуться в меню", AdminCallback::AdminMenu).into()],
                    1,
                ))
                .await?;

            let file = teloxide::types::InputFile::memory(json_content.into_bytes())
                .file_name(filename);

            let caption = format!(indoc! {r#"
                    📊 Детальная статистика:
                    • Пользователь: {}
                    • Пул: {}
                    • Период: {}
                    • Записей: {}
                "#},
                user_id,
                pool_name,
                if period == "week" { "неделя" } else { "месяц" },
                stats.usage.len()
            );

            log::debug!("caption: {}", caption);

            bot.send_document(msg.chat.id, file)
                .caption(&caption)
                .await?;
        }
        Err(e) => {
            log::error!("handle_user_detailed_stats: error: {}", e);

            let error_message = "❌ Ошибка загрузки статистики".to_string();

            bot.edit_message_text(msg.chat.id, msg.id, error_message)
                .reply_markup(make_inline_keyboard(
                    vec![("🔙 Вернуться в меню", AdminCallback::AdminMenu).into()],
                    1,
                ))
                .await?;
        }
    }

    Ok(())
}

async fn add_traffic_receive_amount_handler(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    amount: String,
    _settings: HandlerSettings,
) -> HandlerResult {
    let traffic_text = amount.trim();

    let Some(state) = dialogue.get().await? else {
        bot.send_message(msg.chat.id, "❌ Ошибка получения состояния").await?;
        return Err("State not found".into());
    };

    let MasterState { admin_kind: AdminStateKind::AddTrafficReceiveAmount { user_id, pool }, .. } = state else {
        bot.send_message(msg.chat.id, "❌ Ошибка получения состояния").await?;
        return Err("State not found".into());
    };
    
    let traffic_gb = match traffic_text.parse::<i32>() {
        Ok(gb) if gb > 0 && gb <= 10000 => gb,
        Ok(_) => {
            bot.send_message(
                msg.chat.id,
                "❌ Количество трафика должно быть от 1 до 10000 ГБ"
            )
            .reply_markup(make_inline_keyboard(
                vec![("🔙 Отмена", AdminCallback::AdminMenu).into()],
                1,
            ))
            .await?;
            return Ok(());
        }
        Err(_) => {
            bot.send_message(
                msg.chat.id,
                "❌ Введите корректное число ГБ трафика"
            )
            .reply_markup(make_inline_keyboard(
                vec![("🔙 Отмена", AdminCallback::AdminMenu).into()],
                1,
            ))
            .await?;
            return Ok(());
        }
    };
    
    let loading_msg = bot.send_message(
        msg.chat.id,
        format!("⏳ Добавление {} ГБ трафика пользователю {}...", traffic_gb, user_id)
    )
    .await?;
    
    match api::add_traffic_to_user(user_id.clone(), traffic_gb, pool.to_string()).await {
        Ok(response) => {
            let success_message = format!(
                indoc! {r#"
                    ✅ <b>Трафик успешно добавлен!</b>
                    
                    <b>Пользователь:</b> <code>{}</code>
                    <b>Пул:</b> <code>{}</code>
                    <b>Добавлено трафика:</b> <code>{} ГБ</code>
                    <b>Баланс до добавления:</b> <code>{}</code>
                    <b>ID сабюзера:</b> <code>{}</code>
                "#},
                response.user_id,
                response.pool,
                response.traffic_added_gb,
                response.balance_before,
                response.subuser_id
            );
            
            bot.edit_message_text(loading_msg.chat.id, loading_msg.id, success_message)
                .parse_mode(teloxide::types::ParseMode::Html)
                .reply_markup(make_inline_keyboard(
                    vec![("🔙 Вернуться в меню", AdminCallback::AdminMenu).into()],
                    1,
                ))
                .await?;
        }
        Err(e) => {
            let error_message = format!(
                "❌ <b>Ошибка добавления трафика</b>\n\n<b>Пользователь:</b> <code>{}</code>\n<b>Ошибка:</b> <code>{}</code>",
                user_id, e
            );
            
            bot.edit_message_text(loading_msg.chat.id, loading_msg.id, error_message)
                .parse_mode(teloxide::types::ParseMode::Html)
                .reply_markup(make_inline_keyboard(
                    vec![
                        ("🔄 Попробовать снова", AdminCallback::AddTraffic { 
                            pool: Some(pool) 
                        }).into(),
                        ("🔙 Вернуться в меню", AdminCallback::AdminMenu).into()
                    ],
                    1,
                ))
                .await?;
        }
    }
    
    dialogue.update_admin_kind(AdminStateKind::Menu).await?;
    Ok(())
}

async fn handle_change_price_pool(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    pool_type: PoolType,
) -> HandlerResult {
    let current_price = match api::get_current_price(pool_type).await {
        Ok(price) => format!(" (текущая цена: ${:.2})", price),
        Err(_) => String::new(),
    };

    let message_text = format!(
        "💰 <b>Изменение цены для пула {}</b>\n\n📋 Введите новую стоимость за гигабайт в USD{}:",
        pool_type_to_display_name(&pool_type),
        current_price
    );

    bot.edit_message_text(msg.chat.id, msg.id, message_text)
        .parse_mode(teloxide::types::ParseMode::Html)
        .reply_markup(make_inline_keyboard(
            vec![("🔙 Отмена", AdminCallback::AdminMenu).into()],
            1,
        ))
        .await?;

    dialogue
        .update_admin_kind(AdminStateKind::ReceivePoolPrice { pool_type })
        .await?;

    Ok(())
}

async fn receive_pool_price_handler(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    pool_type: PoolType,
) -> HandlerResult {
    if let Some(text) = msg.text() {
        match text.parse::<f64>() {
            Ok(new_price) => {
                if new_price <= 0.0 {
                    bot.send_message(msg.chat.id, "❌ Цена должна быть положительным числом. Попробуйте еще раз:")
                        .await?;
                    return Ok(());
                }

                if new_price > 1000.0 {
                    bot.send_message(msg.chat.id, "❌ Цена слишком высокая. Максимальная цена: $1000. Попробуйте еще раз:")
                        .await?;
                    return Ok(());
                }

                let user_id = msg.from.unwrap().id.0 as i64;
                let pool_string = pool_type_to_string(&pool_type);

                log::info!(
                    "Admin {} attempting to update price for pool '{}' to ${:.2}",
                    user_id,
                    pool_string,
                    new_price
                );

                match api::update_pool_price(pool_string, new_price, user_id).await {
                    Ok(()) => {
                        bot.send_message(
                            msg.chat.id,
                            format!(
                                "✅ Успешно! Стоимость пула {} обновлена на ${:.2} за гигабайт",
                                pool_type_to_display_name(&pool_type),
                                new_price
                            ),
                        )
                        .await?;

                        log::info!(
                            "Admin {} changed price for pool {} to ${:.2}",
                            user_id,
                            pool_type_to_string(&pool_type),
                            new_price
                        );

                        dialogue.update_admin_kind(AdminStateKind::Menu).await?;
                    }
                    Err(e) => {
                        log::error!(
                            "Failed to update price for pool '{}' to ${:.2} by admin {}: {}",
                            pool_type_to_string(&pool_type),
                            new_price,
                            user_id,
                            e
                        );
                        
                        bot.send_message(
                            msg.chat.id,
                            format!("❌ Ошибка при обновлении цены: {}", e),
                        )
                        .await?;
                    }
                }
            }
            Err(_) => {
                bot.send_message(
                    msg.chat.id,
                    "❌ Неверный формат цены. Введите число (например: 2.5):",
                )
                .await?;
            }
        }
    }

    Ok(())
}

fn pool_type_to_display_name(pool_type: &PoolType) -> &'static str {
    match pool_type {
        PoolType::Residential => "🖥 Residential",
        PoolType::ResidentialPremium => "🖥 Residential Premium",
        PoolType::Mobile => "📱 Mobile",
        PoolType::Datacenter => "📺 Datacenter",
    }
}

fn pool_type_to_string(pool_type: &PoolType) -> String {
    match pool_type {
        PoolType::Residential => "residential".to_string(),
        PoolType::ResidentialPremium => "residential_premium".to_string(),
        PoolType::Mobile => "mobile".to_string(),
        PoolType::Datacenter => "datacenter".to_string(),
    }
}

async fn handle_drop_balance_confirmation(
    bot: Bot,
    msg: Message,
    user_id: String,
    pool: PoolType,
) -> HandlerResult {
    let confirmation_message = format!(
        indoc! {r#"
            ⚠️ <b>Подтверждение сброса баланса</b>
            
            <b>Пользователь:</b> <code>{}</code>
            <b>Пул:</b> <code>{}</code>
            
            ❗️ <b>ВНИМАНИЕ!</b> Это действие удалит весь трафик пользователя в выбранном пуле без возможности восстановления.
            
            Вы уверены, что хотите продолжить?
        "#},
        user_id,
        pool_type_to_display_name(&pool)
    );

    let keyboard = make_inline_keyboard(
        vec![
            (
                "✅ Да, сбросить баланс",
                AdminCallback::ConfirmDropBalance {
                    user_id: user_id.clone(),
                    pool,
                },
            ).into(),
            ("❌ Отмена", AdminCallback::AdminMenu).into(),
        ],
        1,
    );

    bot.edit_message_text(msg.chat.id, msg.id, confirmation_message)
        .parse_mode(teloxide::types::ParseMode::Html)
        .reply_markup(keyboard)
        .await?;

    Ok(())
}

async fn handle_confirm_drop_balance(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    user_id: String,
    pool: PoolType,
) -> HandlerResult {
    let loading_msg = bot.edit_message_text(
        msg.chat.id,
        msg.id,
        format!(
            "⏳ <b>Сброс баланса пользователя...</b>\n\n<b>Пользователь:</b> <code>{}</code>\n<b>Пул:</b> <code>{}</code>",
            user_id,
            pool_type_to_display_name(&pool)
        )
    )
    .parse_mode(teloxide::types::ParseMode::Html)
    .await?;

    match api::drop_balance_user(user_id.clone(), pool_type_to_string(&pool)).await {
        Ok(()) => {
            let success_message = format!(
                indoc! {r#"
                    ✅ <b>Баланс успешно сброшен!</b>
                    
                    <b>Пользователь:</b> <code>{}</code>
                    <b>Пул:</b> <code>{}</code>
                    
                    Весь трафик в данном пуле удален.
                "#},
                user_id,
                pool_type_to_display_name(&pool)
            );
            
            bot.edit_message_text(loading_msg.chat.id, loading_msg.id, success_message)
                .parse_mode(teloxide::types::ParseMode::Html)
                .reply_markup(make_inline_keyboard(
                    vec![("🔙 Вернуться в меню", AdminCallback::AdminMenu).into()],
                    1,
                ))
                .await?;
        }
        Err(e) => {
            let error_message = format!(
                "❌ <b>Ошибка сброса баланса</b>\n\n<b>Пользователь:</b> <code>{}</code>\n<b>Пул:</b> <code>{}</code>\n<b>Ошибка:</b> <code>{}</code>",
                user_id,
                pool_type_to_display_name(&pool),
                e
            );
            
            bot.edit_message_text(loading_msg.chat.id, loading_msg.id, error_message)
                .parse_mode(teloxide::types::ParseMode::Html)
                .reply_markup(make_inline_keyboard(
                    vec![
                        ("🔄 Попробовать снова", AdminCallback::DropBalance { 
                            pool: Some(pool) 
                        }).into(),
                        ("🔙 Вернуться в меню", AdminCallback::AdminMenu).into()
                    ],
                    1,
                ))
                .await?;
        }
    }
    
    dialogue.update_admin_kind(AdminStateKind::Menu).await?;
    Ok(())
}

async fn show_source_links(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
) -> HandlerResult {
    show_source_links_page(bot, dialogue, msg, 0, None).await
}

async fn show_source_links_page(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    page: usize,
    search_query: Option<String>,
) -> HandlerResult {
    let source_links = match api::get_source_links().await {
        Ok(links) => links,
        Err(e) => {
            log::error!("Failed to fetch source links: {}", e);
            bot.send_message(msg.chat.id, "❌ Ошибка при загрузке ссылок")
                .await?;
            return Ok(());
        }
    };

    let config = PaginationConfig::telegram_default();
    let mut paginator = SearchablePaginator::with_config(source_links, config.clone());
    
    let search_str = search_query.as_deref().unwrap_or("");
    if !search_str.is_empty() {
        paginator.search(search_str);
    }
    
    let result = paginator.paginate(page);
    let found_count = paginator.filtered_count();
    let total_count = paginator.total_count();

    let header = if search_str.is_empty() {
        format!(
            indoc! {r#"
                🔗 <b>Реферальные ссылки</b>
                
                📊 Всего ссылок: <b>{}</b>
                📄 Страница: <b>{}/{}</b>
                
                💡 Отправьте название ссылки для поиска
                
                Статистика по источникам:
            "#},
            total_count,
            result.current_page + 1,
            result.total_pages.max(1)
        )
    } else {
        format!(
            indoc! {r#"
                🔗 <b>Реферальные ссылки</b>
                
                🔍 Поиск: <code>{}</code>
                📊 Найдено: <b>{}</b> из <b>{}</b>
                📄 Страница: <b>{}/{}</b>
                
                Статистика по источникам:
            "#},
            search_str,
            found_count,
            total_count,
            result.current_page + 1,
            result.total_pages.max(1)
        )
    };

    let keyboard_builder = PaginationKeyboardBuilder::new(
        result,
        config,
        Arc::new({
            move |page| AdminCallback::SourceLinksPage { page }.to_string()
        }),
        AdminCallback::AdminMenu.to_string(), // No-op callback for page info
    );

    let mut keyboard = keyboard_builder.build_simple(|_index, source_link| {
        let bot_count = source_link.bot_count();
        let web_count = source_link.web_count();
        
        let button_text = format!(
            "{} | 🤖{} 🌐{}",
            source_link.name,
            bot_count,
            web_count
        );
        
        let callback_data = AdminCallback::SourceLinkDetails { 
            link_value: source_link.value.clone() 
        }.to_string();
        (button_text, callback_data)
    });

    let create_row = vec![
        teloxide::types::InlineKeyboardButton::callback(
            "➕ Создать ссылку",
            AdminCallback::SourceLinksCreate.to_string()
        )
    ];
    keyboard = keyboard.append_row(create_row);

    let mut bottom_row = vec![];
    
    if !search_str.is_empty() {
        bottom_row.push(teloxide::types::InlineKeyboardButton::callback(
            "🗑️ Очистить поиск",
            AdminCallback::SourceLinksClearSearch.to_string()
        ));
    }
    
    bottom_row.push(teloxide::types::InlineKeyboardButton::callback(
        "🔙 Вернуться в меню",
        AdminCallback::AdminMenu.to_string()
    ));
    
    keyboard = keyboard.append_row(bottom_row);

    let result = bot.edit_message_text(msg.chat.id, msg.id, header.clone())
        .parse_mode(teloxide::types::ParseMode::Html)
        .reply_markup(keyboard.clone())
        .await;

    match result {
        Ok(_) => {
            dialogue.update_admin_kind(AdminStateKind::SourceLinks { page, search_query }).await?;
        }
        Err(_) => {
            bot.send_message(msg.chat.id, header)
                .parse_mode(teloxide::types::ParseMode::Html)
                .reply_markup(keyboard)
                .await?;
            dialogue.update_admin_kind(AdminStateKind::SourceLinks { page, search_query }).await?;
        }
    }

    Ok(())
}

async fn source_links_state_handler(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    (page, search_query): (usize, Option<String>),
) -> HandlerResult {
    if let Some(text) = msg.clone().text() {
        let search_text = text.trim();
        
        let _ = bot.delete_message(msg.chat.id, msg.id).await;
        
        if search_text.is_empty() {
            show_source_links_page(bot, dialogue, msg, 0, None).await
        } else {
            show_source_links_page(bot, dialogue, msg, 0, Some(search_text.to_string())).await
        }
    } else {
        show_source_links_page(bot, dialogue, msg, page, search_query).await
    }
}

async fn handle_source_links_page(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    page: usize,
) -> HandlerResult {
    let search_query = if let Some(state) = dialogue.get().await? {
        if let AdminStateKind::SourceLinks { search_query, .. } = state.admin_kind {
            search_query
        } else {
            None
        }
    } else {
        None
    };
    
    show_source_links_page(bot, dialogue, msg, page, search_query).await
}

async fn handle_source_links_clear_search(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
) -> HandlerResult {
    show_source_links_page(bot, dialogue, msg, 0, None).await
}

async fn handle_source_link_details(
    bot: Bot,
    _dialogue: MasterDialogue,
    msg: Message,
    link_value: String,
) -> HandlerResult {
    let source_links = match api::get_source_links().await {
        Ok(links) => links,
        Err(e) => {
            log::error!("Failed to fetch source links: {}", e);
            bot.send_message(msg.chat.id, "❌ Ошибка при загрузке ссылок")
                .await?;
            return Ok(());
        }
    };

    let source_link = source_links.iter()
        .find(|link| link.value == link_value);

    let Some(link) = source_link else {
        bot.send_message(msg.chat.id, "❌ Ссылка не найдена")
            .await?;
        return Ok(());
    };

    let total_count = link.total_count();
    let bot_count = link.bot_count();
    let web_count = link.web_count();

    let details_message = format!(
        indoc! {r#"
            🔗 <b>Информация о реферальной ссылке</b>
            
            <b>Название:</b> {}
            <b>Код:</b> <code>{}</code>
            <b>Количество переходов:</b> {} | 🤖{} 🌐{}
            
            <b>Ссылки:</b>
            • Telegram: <code>https://t.me/V_Proxy_bot?start={}</code>
            • Website: <code>https://{}?reffery={}</code>
        "#},
        link.name,
        link.value,
        total_count,
        bot_count,
        web_count,
        link.value,
        *WEBSITE_DOMAIN,
        link.value
    );

    let keyboard = teloxide::types::InlineKeyboardMarkup::default()
        .append_row(vec![
            teloxide::types::InlineKeyboardButton::callback(
                "📁 JSON",
                AdminCallback::SourceLinkExportJson { link_value: link.value.clone() }.to_string()
            ),
            teloxide::types::InlineKeyboardButton::callback(
                "📊 CSV",
                AdminCallback::SourceLinkExportCsv { link_value: link.value.clone() }.to_string()
            )
        ])
        .append_row(vec![
            teloxide::types::InlineKeyboardButton::callback(
                "🔙 К списку ссылок",
                AdminCallback::SourceLinksMenu.to_string()
            )
        ]);

    let result = bot.edit_message_text(msg.chat.id, msg.id, details_message.clone())
        .parse_mode(teloxide::types::ParseMode::Html)
        .reply_markup(keyboard.clone())
        .await;

    match result {
        Ok(_) => {},
        Err(_) => {
            bot.send_message(msg.chat.id, details_message)
                .parse_mode(teloxide::types::ParseMode::Html)
                .reply_markup(keyboard)
                .await?;
        }
    }

    Ok(())
}

async fn handle_source_link_export_json(
    bot: Bot,
    _dialogue: MasterDialogue,
    msg: Message,
    link_value: String,
) -> HandlerResult {
    bot.edit_message_text(
        msg.chat.id, 
        msg.id, 
        "🔄 Генерируем файл с детальной информацией..."
    )
    .await?;

    let json_data = match api::get_ref_link_details(Some(link_value.clone())).await {
        Ok(data) => data,
        Err(e) => {
            log::error!("Failed to fetch ref link details: {}", e);
            bot.edit_message_text(
                msg.chat.id,
                msg.id,
                "❌ Ошибка при получении данных"
            )
            .await?;
            return Ok(());
        }
    };

    let json_string = match serde_json::to_string_pretty(&json_data) {
        Ok(json) => json,
        Err(e) => {
            log::error!("Failed to serialize JSON: {}", e);
            bot.edit_message_text(
                msg.chat.id,
                msg.id,
                "❌ Ошибка при формировании JSON"
            )
            .await?;
            return Ok(());
        }
    };

    let timestamp = chrono::Utc::now().format("%Y%m%d_%H%M%S");
    let filename = format!("ref_link_details_{}_{}.json", link_value, timestamp);

    let document = teloxide::types::InputFile::memory(json_string.into_bytes())
        .file_name(filename);

    let result = bot.send_document(msg.chat.id, document)
        .caption(format!(
            "📁 <b>Детальная информация по реферальной ссылке</b>\n\n<b>Код:</b> <code>{}</code>\n<b>Экспорт:</b> {}",
            link_value,
            chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC")
        ))
        .parse_mode(teloxide::types::ParseMode::Html)
        .await;

    match result {
        Ok(_) => {
            let keyboard = teloxide::types::InlineKeyboardMarkup::default()
                .append_row(vec![
                    teloxide::types::InlineKeyboardButton::callback(
                        "🔙 К деталям ссылки",
                        AdminCallback::SourceLinkDetails { link_value }.to_string()
                    )
                ]);

            bot.edit_message_text(
                msg.chat.id,
                msg.id,
                "✅ Файл с данными успешно отправлен!"
            )
            .reply_markup(keyboard)
            .await?;
        }
        Err(e) => {
            log::error!("Failed to send document: {}", e);
            bot.edit_message_text(
                msg.chat.id,
                msg.id,
                "❌ Ошибка при отправке файла"
            )
            .await?;
        }
    }

    Ok(())
}

async fn handle_source_link_export_csv(
    bot: Bot,
    _dialogue: MasterDialogue,
    msg: Message,
    link_value: String,
) -> HandlerResult {
    bot.edit_message_text(
        msg.chat.id, 
        msg.id, 
        "🔄 Генерируем CSV файл с детальной информацией..."
    )
    .await?;

    let json_data = match api::get_ref_link_details(Some(link_value.clone())).await {
        Ok(data) => data,
        Err(e) => {
            log::error!("Failed to fetch ref link details: {}", e);
            bot.edit_message_text(
                msg.chat.id,
                msg.id,
                "❌ Ошибка при получении данных"
            )
            .await?;
            return Ok(());
        }
    };

    let csv_content = match json_to_csv(&json_data, &link_value) {
        Ok(csv) => csv,
        Err(e) => {
            log::error!("Failed to convert JSON to CSV: {}", e);
            bot.edit_message_text(
                msg.chat.id,
                msg.id,
                "❌ Ошибка при формировании CSV"
            )
            .await?;
            return Ok(());
        }
    };

    let timestamp = chrono::Utc::now().format("%Y%m%d_%H%M%S");
    let filename = format!("ref_link_users_{}_{}.csv", link_value, timestamp);

    let document = teloxide::types::InputFile::memory(csv_content.into_bytes())
        .file_name(filename);

    let result = bot.send_document(msg.chat.id, document)
        .caption(format!(
            "📊 <b>CSV экспорт пользователей реферальной ссылки</b>\n\n<b>Код:</b> <code>{}</code>\n<b>Экспорт:</b> {}",
            link_value,
            chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC")
        ))
        .parse_mode(teloxide::types::ParseMode::Html)
        .await;

    match result {
        Ok(_) => {
            let keyboard = teloxide::types::InlineKeyboardMarkup::default()
                .append_row(vec![
                    teloxide::types::InlineKeyboardButton::callback(
                        "🔙 К деталям ссылки",
                        AdminCallback::SourceLinkDetails { link_value }.to_string()
                    )
                ]);

            bot.edit_message_text(
                msg.chat.id,
                msg.id,
                "✅ CSV файл успешно отправлен!"
            )
            .reply_markup(keyboard)
            .await?;
        }
        Err(e) => {
            log::error!("Failed to send document: {}", e);
            bot.edit_message_text(
                msg.chat.id,
                msg.id,
                "❌ Ошибка при отправке файла"
            )
            .await?;
        }
    }

    Ok(())
}

fn json_to_csv(ref_link_details: &RefLinkDetails, ref_code: &str) -> Result<String, HandlerError> {
    let mut csv_content = String::new();
    
    csv_content.push_str("ref_code,ref_display_name,user_id,source,registered_at,has_paid,residential_gb,residential_premium_gb,datacenter_gb,mobile_gb\n");
    
    for user in &ref_link_details.users {
        let user_id = user.user_id.as_deref().unwrap_or("");
        let source = &user.source;
        let registered_at = ""; // Add this field to RefLinkUser if needed
        let has_paid = user.has_paid;
        
        let residential_gb = user.traffic_pools.get(&PoolType::Residential)
            .map(|gb| gb.balance_format.to_owned())
            .unwrap_or("0 bytes".to_string());
        
        let residential_premium_gb = user.traffic_pools.get(&PoolType::ResidentialPremium)
            .map(|gb| gb.balance_format.to_owned())
            .unwrap_or("0 bytes".to_string());
        
        let datacenter_gb = user.traffic_pools.get(&PoolType::Datacenter)
            .map(|gb| gb.balance_format.to_owned())
            .unwrap_or("0 bytes".to_string());
        
        let mobile_gb = user.traffic_pools.get(&PoolType::Mobile)
            .map(|gb| gb.balance_format.to_owned())
            .unwrap_or("0 bytes".to_string());
        
        let escaped_ref_display_name = escape_csv_field(&ref_link_details.display_name);
        
        csv_content.push_str(&format!(
            "{},{},{},{},{},{},{},{},{},{}\n",
            ref_code,
            escaped_ref_display_name,
            user_id,
            source,
            registered_at,
            has_paid,
            residential_gb,
            residential_premium_gb,
            datacenter_gb,
            mobile_gb,
        ));
    }
    
    Ok(csv_content)
}

fn escape_csv_field(field: &str) -> String {
    if field.contains(',') || field.contains('"') || field.contains('\n') {
        format!("\"{}\"", field.replace('"', "\"\""))
    } else {
        field.to_string()
    }
}

async fn handle_source_links_create(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
) -> HandlerResult {
    let create_message = indoc! {r#"
        ➕ <b>Создание новой реферальной ссылки</b>
        
        📝 Введите код ссылки (только буквы, цифры, подчеркивания и дефисы):
        
        <i>Пример: my_special_link</i>
    "#};

    let result = bot.edit_message_text(msg.chat.id, msg.id, create_message)
        .parse_mode(teloxide::types::ParseMode::Html)
        .reply_markup(teloxide::types::InlineKeyboardMarkup::default()
            .append_row(vec![
                teloxide::types::InlineKeyboardButton::callback(
                    "🔙 Отмена",
                    AdminCallback::SourceLinksMenu.to_string()
                )
            ]))
        .await;

    match result {
        Ok(_) => {},
        Err(_) => {
            bot.send_message(msg.chat.id, create_message)
                .parse_mode(teloxide::types::ParseMode::Html)
                .reply_markup(teloxide::types::InlineKeyboardMarkup::default()
                    .append_row(vec![
                        teloxide::types::InlineKeyboardButton::callback(
                            "🔙 Отмена",
                            AdminCallback::SourceLinksMenu.to_string()
                        )
                    ]))
                .await?;
        }
    }

    dialogue.update_admin_kind(AdminStateKind::CreateSourceLink).await?;

    Ok(())
}

async fn create_source_link_state_handler(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    _: (),
) -> HandlerResult {
    let Some(text) = msg.text() else {
        bot.send_message(msg.chat.id, "❌ Пожалуйста, отправьте текстовое сообщение")
            .await?;
        return Ok(());
    };

    let input = text.trim();

    if input.is_empty() {
        bot.send_message(msg.chat.id, "❌ Код ссылки не может быть пустым")
            .await?;
        return Ok(());
    }

    if !input.chars().all(|c| c.is_alphanumeric() || c == '_' || c == '-') {
        bot.send_message(
            msg.chat.id,
            "❌ Код ссылки может содержать только буквы, цифры, подчеркивания и дефисы"
        )
        .await?;
        return Ok(());
    }

    let _ = bot.delete_message(msg.chat.id, msg.id).await;

    let ref_code = input.to_string();
    
    let ask_display_name = format!(
        indoc! {r#"
            ➕ <b>Создание новой реферальной ссылки</b>
            
            ✅ Код ссылки: <code>{}</code>
            
            📝 Теперь введите отображаемое имя ссылки:
            
            <i>Пример: Моя особая ссылка</i>
        "#},
        ref_code
    );

    bot.send_message(msg.chat.id, ask_display_name)
        .parse_mode(teloxide::types::ParseMode::Html)
        .reply_markup(teloxide::types::InlineKeyboardMarkup::default()
            .append_row(vec![
                teloxide::types::InlineKeyboardButton::callback(
                    "🔙 Отмена",
                    AdminCallback::SourceLinksMenu.to_string()
                )
            ]))
        .await?;

    dialogue.update_admin_kind(AdminStateKind::CreateSourceLinkWithCode { ref_code }).await?;

    Ok(())
}

async fn create_source_link_with_code_state_handler(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    ref_code: String,
) -> HandlerResult {
    let Some(text) = msg.text() else {
        bot.send_message(msg.chat.id, "❌ Пожалуйста, отправьте текстовое сообщение")
            .await?;
        return Ok(());
    };

    let display_name = text.trim();

    if display_name.is_empty() {
        bot.send_message(msg.chat.id, "❌ Название ссылки не может быть пустым")
            .await?;
        return Ok(());
    }

    let _ = bot.delete_message(msg.chat.id, msg.id).await;

    create_referral_link(bot, dialogue, msg.clone(), ref_code, display_name.to_string()).await
}
  
async fn create_referral_link(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    ref_code: String,
    display_name: String,
) -> HandlerResult {
    let processing_msg = bot.send_message(
        msg.chat.id,
        "🔄 Создаем реферальную ссылку..."
    )
    .await?;

    match api::create_admin_ref(ref_code.clone(), display_name.clone()).await {
        Ok(_) => {
            let success_message = format!(
                indoc! {r#"
                    ✅ <b>Реферальная ссылка успешно создана!</b>
                    
                    📝 <b>Код:</b> <code>{}</code>
                    📛 <b>Название:</b> {}
                    
                    🔗 <b>Ссылки:</b>
                    • Telegram: <code>https://t.me/V_Proxy_bot?start={}</code>
                    • Website: <code>https://{}?source={}</code>
                "#},
                ref_code, display_name, ref_code, *WEBSITE_DOMAIN, ref_code
            );

            bot.edit_message_text(processing_msg.chat.id, processing_msg.id, success_message)
                .parse_mode(teloxide::types::ParseMode::Html)
                .reply_markup(teloxide::types::InlineKeyboardMarkup::default()
                    .append_row(vec![
                        teloxide::types::InlineKeyboardButton::callback(
                            "🔙 К списку ссылок",
                            AdminCallback::SourceLinksMenu.to_string()
                        )
                    ]))
                .await?;

            dialogue.update_admin_kind(AdminStateKind::Menu).await?;
        }
        Err(e) => {
            let error_message = if e.to_string().contains("CONFLICT") {
                format!(
                    "❌ <b>Ошибка создания ссылки</b>\n\nСсылка с кодом <code>{}</code> уже существует. Попробуйте другой код.",
                    ref_code
                )
            } else {
                format!(
                    "❌ <b>Ошибка создания ссылки</b>\n\n{}",
                    e
                )
            };

            bot.edit_message_text(processing_msg.chat.id, processing_msg.id, error_message)
                .parse_mode(teloxide::types::ParseMode::Html)
                .reply_markup(teloxide::types::InlineKeyboardMarkup::default()
                    .append_row(vec![
                        teloxide::types::InlineKeyboardButton::callback(
                            "🔄 Попробовать снова",
                            AdminCallback::SourceLinksCreate.to_string()
                        ),
                        teloxide::types::InlineKeyboardButton::callback(
                            "🔙 К списку ссылок",
                            AdminCallback::SourceLinksMenu.to_string()
                        )
                    ]))
                .await?;
        }
    }

    Ok(())
}

async fn receive_referral_percent_handler(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
) -> HandlerResult {
    if let Some(text) = msg.text() {
        match text.parse::<f64>() {
            Ok(new_percent) => {
                if new_percent < 0.0 {
                    bot.send_message(msg.chat.id, "❌ Процент не может быть отрицательным. Попробуйте еще раз:")
                        .await?;
                    return Ok(());
                }

                if new_percent > 1.0 {
                    bot.send_message(msg.chat.id, "❌ Процент не может быть больше 1. Попробуйте еще раз:")
                        .await?;
                    return Ok(());
                }

                let loading_msg = bot.send_message(
                    msg.chat.id,
                    format!("⏳ Обновляем реферальный процент на {}...", new_percent)
                )
                .await?;

                match api::update_referral_percent(new_percent).await {
                    Ok(()) => {
                        bot.edit_message_text(
                            loading_msg.chat.id,
                            loading_msg.id,
                            format!(
                                "✅ <b>Реферальный процент успешно обновлен!</b>\n\n<b>Новый процент:</b> {}",
                                new_percent
                            ),
                        )
                        .parse_mode(teloxide::types::ParseMode::Html)
                        .reply_markup(make_inline_keyboard(
                            vec![("🔙 Вернуться в меню", AdminCallback::AdminMenu).into()],
                            1,
                        ))
                        .await?;

                        log::info!("Referral percentage updated to {}", new_percent);
                        dialogue.update_admin_kind(AdminStateKind::Menu).await?;
                    }
                    Err(e) => {
                        log::error!("Failed to update referral percentage to {}: {}", new_percent, e);
                        
                        bot.edit_message_text(
                            loading_msg.chat.id,
                            loading_msg.id,
                            format!("❌ Ошибка при обновлении реферального процента: {}", e),
                        )
                        .reply_markup(make_inline_keyboard(
                            vec![("🔙 Вернуться в меню", AdminCallback::AdminMenu).into()],
                            1,
                        ))
                        .await?;
                        
                        dialogue.update_admin_kind(AdminStateKind::Menu).await?;
                    }
                }
            }
            Err(_) => {
                bot.send_message(
                    msg.chat.id,
                    "❌ Неверный формат процента. Введите число (например: 15.5):",
                )
                .await?;
            }
        }
    }

    Ok(())
}

async fn receive_user_referral_percent_handler(
    bot: Bot,
    msg: Message,
    dialogue: MasterDialogue,
    user_id: String,
) -> HandlerResult {
    if let Some(text) = msg.text() {
        match text.parse::<f64>() {
            Ok(new_percent) => {
                if new_percent < 0.0 {
                    bot.send_message(msg.chat.id, "❌ Процент не может быть отрицательным. Попробуйте еще раз:")
                        .await?;
                    return Ok(());
                }

                if new_percent > 1.0 {
                    bot.send_message(msg.chat.id, "❌ Процент не может быть больше 1. Попробуйте еще раз:")
                        .await?;
                    return Ok(());
                }

                let loading_msg = bot.send_message(
                    msg.chat.id,
                    format!("⏳ Обновляем реферальный процент для пользователя {} на {}...", user_id, new_percent)
                )
                .await?;

                match api::update_user_referral_percent(user_id.clone(), new_percent).await {
                    Ok(()) => {
                        bot.edit_message_text(
                            loading_msg.chat.id,
                            loading_msg.id,
                            format!(
                                "✅ <b>Реферальный процент для пользователя {} успешно обновлен!</b>\n\n<b>Новый процент:</b> {}",
                                user_id, new_percent
                            ),
                        )
                        .parse_mode(teloxide::types::ParseMode::Html)
                        .reply_markup(make_inline_keyboard(
                            vec![("🔙 Вернуться в меню", AdminCallback::AdminMenu).into()],
                            1,
                        ))
                        .await?;

                        log::info!("User {} referral percentage updated to {}", user_id, new_percent);
                        dialogue.update_admin_kind(AdminStateKind::Menu).await?;
                    }
                    Err(e) => {
                        log::error!("Failed to update user {} referral percentage to {}: {}", user_id, new_percent, e);
                        
                        bot.edit_message_text(
                            loading_msg.chat.id,
                            loading_msg.id,
                            format!("❌ Ошибка при обновлении реферального процента для пользователя {}: {}", user_id, e),
                        )
                        .reply_markup(make_inline_keyboard(
                            vec![("🔙 Вернуться в меню", AdminCallback::AdminMenu).into()],
                            1,
                        ))
                        .await?;
                        
                        dialogue.update_admin_kind(AdminStateKind::Menu).await?;
                    }
                }
            }
            Err(_) => {
                bot.send_message(
                    msg.chat.id,
                    format!("❌ Неверный формат процента для пользователя {}. Введите число (например: 15.5):", user_id),
                )
                .await?;
            }
        }
    }

    Ok(())
}

async fn show_payment_systems_menu(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
) -> HandlerResult {
    let loading_msg = bot.send_message(
        msg.chat.id,
        "⏳ Загружаем список платежных систем..."
    )
    .await?;

    match api::get_all_payment_systems().await {
        Ok(payment_systems) => {
            let message = format_payment_systems_message(&payment_systems);
            let keyboard = make_payment_systems_keyboard(&payment_systems);

            bot.edit_message_text(
                loading_msg.chat.id,
                loading_msg.id,
                message,
            )
            .parse_mode(teloxide::types::ParseMode::Html)
            .reply_markup(keyboard)
            .await?;

            dialogue.update_admin_kind(AdminStateKind::PaymentSystemsMenu).await?;
        }
        Err(e) => {
            log::error!("Failed to get payment systems: {}", e);
            bot.edit_message_text(
                loading_msg.chat.id,
                loading_msg.id,
                format!("❌ Ошибка загрузки платежных систем: {}", e),
            )
            .reply_markup(make_inline_keyboard(
                vec![("🔙 Вернуться в меню", AdminCallback::AdminMenu).into()],
                1,
            ))
            .await?;
        }
    }

    Ok(())
}

fn format_payment_systems_message(payment_systems: &[FullPaymentSystem]) -> String {
    let mut message = String::from("💳 <b>Управление платежными системами</b>\n\n");
    
    if payment_systems.is_empty() {
        message.push_str("❌ Нет доступных платежных систем");
        return message;
    }

    for (index, system) in payment_systems.iter().enumerate() {
        let status_icon = if system.is_enabled() { "🟢" } else { "🔴" };
        let status_text = if system.is_enabled() { "Включена" } else { "Отключена" };
        
        message.push_str(&format!(
            "{}. <b>{}</b>\n{} <i>{}</i>\n",
            index + 1,
            system.name,
            status_icon,
            status_text
        ));
        
        if let Some(description) = &system.description {
            if !description.is_empty() {
                message.push_str(&format!("📝 {}\n", description));
            }
        }
        
        message.push('\n');
    }

    message.push_str("Выберите платежную систему для редактирования:");
    message
}

fn make_payment_systems_keyboard(payment_systems: &[FullPaymentSystem]) -> teloxide::types::InlineKeyboardMarkup {
    let mut buttons = Vec::new();
    
    for system in payment_systems {
        let status_icon = if system.is_enabled() { "🟢" } else { "🔴" };
        let button_text = format!("{} {}", status_icon, system.name);
        
        buttons.push(vec![
            teloxide::types::InlineKeyboardButton::callback(
                button_text,
                AdminCallback::PaymentSystemToggle { name: system.name.clone() }.to_string()
            )
        ]);
    }
    
    buttons.push(vec![
        teloxide::types::InlineKeyboardButton::callback(
            "🔙 Вернуться в меню",
            AdminCallback::AdminMenu.to_string()
        )
    ]);
    
    teloxide::types::InlineKeyboardMarkup::new(buttons)
}

async fn handle_payment_system_toggle(
    bot: Bot,
    _dialogue: MasterDialogue,
    msg: Message,
    name: String,
) -> HandlerResult {
    let loading_msg = bot.edit_message_text(
        msg.chat.id,
        msg.id,
        format!("⏳ Загружаем информацию о платежной системе {}...", name),
    )
    .await?;

    match api::get_all_payment_systems().await {
        Ok(payment_systems) => {
            if let Some(system) = payment_systems.iter().find(|s| s.name == name) {
                let current_status = system.is_enabled();
                let new_status = !current_status;
                let action_text = if new_status { "включить" } else { "отключить" };
                let status_icon = if new_status { "🟢" } else { "🔴" };
                
                let message = format!(
                    "💳 <b>Управление платежной системой</b>\n\n\
                    📛 <b>Название:</b> {}\n\
                    {} <b>Текущий статус:</b> {}\n\n\
                    🔄 Хотите <b>{}</b> эту платежную систему?",
                    system.name,
                    if current_status { "🟢" } else { "🔴" },
                    if current_status { "Включена" } else { "Отключена" },
                    action_text
                );

                let keyboard = teloxide::types::InlineKeyboardMarkup::new(vec![
                    vec![
                        teloxide::types::InlineKeyboardButton::callback(
                            format!("{} {}", status_icon, action_text.chars().next().unwrap().to_uppercase().chain(action_text.chars().skip(1)).collect::<String>()),
                            AdminCallback::PaymentSystemConfirmToggle { name: name.clone(), enabled: new_status }.to_string()
                        )
                    ],
                    vec![
                        teloxide::types::InlineKeyboardButton::callback(
                            "📝 Изменить описание",
                            AdminCallback::PaymentSystemEditDescription { name: name.clone() }.to_string()
                        )
                    ],
                    vec![
                        teloxide::types::InlineKeyboardButton::callback(
                            "🔙 К списку систем",
                            AdminCallback::PaymentSystemsMenu.to_string()
                        )
                    ]
                ]);

                bot.edit_message_text(
                    loading_msg.chat.id,
                    loading_msg.id,
                    message,
                )
                .parse_mode(teloxide::types::ParseMode::Html)
                .reply_markup(keyboard)
                .await?;
            } else {
                bot.edit_message_text(
                    loading_msg.chat.id,
                    loading_msg.id,
                    format!("❌ Платежная система '{}' не найдена", name),
                )
                .reply_markup(make_inline_keyboard(
                    vec![("🔙 К списку систем", AdminCallback::PaymentSystemsMenu).into()],
                    1,
                ))
                .await?;
            }
        }
        Err(e) => {
            log::error!("Failed to get payment systems: {}", e);
            bot.edit_message_text(
                loading_msg.chat.id,
                loading_msg.id,
                format!("❌ Ошибка загрузки платежных систем: {}", e),
            )
            .reply_markup(make_inline_keyboard(
                vec![("🔙 К списку систем", AdminCallback::PaymentSystemsMenu).into()],
                1,
            ))
            .await?;
        }
    }

    Ok(())
}

async fn handle_payment_system_confirm_toggle(
    bot: Bot,
    _dialogue: MasterDialogue,
    msg: Message,
    name: String,
    enabled: bool,
) -> HandlerResult {
    let action_text = if enabled { "включение" } else { "отключение" };
    let loading_msg = bot.edit_message_text(
        msg.chat.id,
        msg.id,
        format!("⏳ Выполняем {} платежной системы {}...", action_text, name),
    )
    .await?;

    match api::update_payment_system_availability(name.clone(), enabled).await {
        Ok(()) => {
            let success_text = if enabled { "включена" } else { "отключена" };
            let status_icon = if enabled { "🟢" } else { "🔴" };
            
            bot.edit_message_text(
                loading_msg.chat.id,
                loading_msg.id,
                format!(
                    "✅ <b>Платежная система успешно {}!</b>\n\n\
                    📛 <b>Система:</b> {}\n\
                    {} <b>Статус:</b> {}",
                    success_text,
                    name,
                    status_icon,
                    success_text.chars().next().unwrap().to_uppercase().chain(success_text.chars().skip(1)).collect::<String>()
                ),
            )
            .parse_mode(teloxide::types::ParseMode::Html)
            .reply_markup(make_inline_keyboard(
                vec![
                    ("🔙 К списку систем", AdminCallback::PaymentSystemsMenu).into(),
                    ("🏠 Главное меню", AdminCallback::AdminMenu).into()
                ],
                1,
            ))
            .await?;
        }
        Err(e) => {
            log::error!("Failed to update payment system {}: {}", name, e);
            bot.edit_message_text(
                loading_msg.chat.id,
                loading_msg.id,
                format!("❌ Ошибка при обновлении платежной системы {}: {}", name, e),
            )
            .reply_markup(make_inline_keyboard(
                vec![("🔙 К списку систем", AdminCallback::PaymentSystemsMenu).into()],
                1,
            ))
            .await?;
        }
    }

    Ok(())
}

async fn handle_payment_system_edit_description(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    name: String,
) -> HandlerResult {
    let message = format!(
        "📝 <b>Редактирование описания</b>\n\n\
        📛 <b>Платежная система:</b> {}\n\n\
        💬 Отправьте новое описание для этой платежной системы:\n\n\
        💡 <i>Описание будет отображаться пользователям при выборе способа оплаты</i>",
        name
    );

    bot.edit_message_text(
        msg.chat.id,
        msg.id,
        message,
    )
    .parse_mode(teloxide::types::ParseMode::Html)
    .reply_markup(make_inline_keyboard(
        vec![
            ("❌ Отмена", AdminCallback::PaymentSystemsMenu).into()
        ],
        1,
    ))
    .await?;

    dialogue.update_admin_kind(AdminStateKind::PaymentSystemEditDescription { name }).await?;

    Ok(())
}

async fn handle_payment_system_description_input(
    bot: Bot,
    dialogue: MasterDialogue,
    msg: Message,
    name: String,
) -> HandlerResult {
    let Some(description) = msg.text() else {
        bot.send_message(msg.chat.id, "❌ Пожалуйста, отправьте текстовое сообщение с описанием")
            .await?;
        return Ok(());
    };

    let description = description.trim();
    if description.is_empty() {
        bot.send_message(msg.chat.id, "❌ Описание не может быть пустым. Попробуйте еще раз:")
            .await?;
        return Ok(());
    }

    if description.len() > 500 {
        bot.send_message(msg.chat.id, "❌ Описание слишком длинное (максимум 500 символов). Попробуйте еще раз:")
            .await?;
        return Ok(());
    }

    let loading_msg = bot.send_message(
        msg.chat.id,
        format!("⏳ Обновляем описание для платежной системы {}...", name),
    )
    .await?;

    let _ = bot.delete_message(msg.chat.id, msg.id).await;

    match api::update_payment_system_description(name.clone(), description.to_string()).await {
        Ok(()) => {
            bot.edit_message_text(
                loading_msg.chat.id,
                loading_msg.id,
                format!(
                    "✅ <b>Описание успешно обновлено!</b>\n\n\
                    📛 <b>Платежная система:</b> {}\n\
                    📝 <b>Новое описание:</b> {}",
                    name,
                    description
                ),
            )
            .parse_mode(teloxide::types::ParseMode::Html)
            .reply_markup(make_inline_keyboard(
                vec![
                    ("🔙 К списку систем", AdminCallback::PaymentSystemsMenu).into(),
                    ("🏠 Главное меню", AdminCallback::AdminMenu).into()
                ],
                1,
            ))
            .await?;

            dialogue.update_admin_kind(AdminStateKind::PaymentSystemsMenu).await?;
        }
        Err(e) => {
            log::error!("Failed to update payment system description for {}: {}", name, e);
            bot.edit_message_text(
                loading_msg.chat.id,
                loading_msg.id,
                format!("❌ Ошибка при обновлении описания: {}", e),
            )
            .reply_markup(make_inline_keyboard(
                vec![("🔙 К списку систем", AdminCallback::PaymentSystemsMenu).into()],
                1,
            ))
            .await?;
        }
    }

    Ok(())
}

async fn load_handler(
    bot: Bot,
    msg: Message,
    target: LoadTarget,
    _dialogue: MasterDialogue,
) -> HandlerResult {
    if target == LoadTarget::I18n {
        bot.send_message(msg.chat.id, "🔄 Обновление словаря переводов...")
            .await?;

        let res = tokio::task::spawn_blocking(|| {
            futures::executor::block_on(rust_shared::i18n::update_translation_dictionary())
        })
        .await;
        
        match res {
            Ok(_) => {
                bot.send_message(msg.chat.id, "✅ Словарь переводов успешно обновлен!")
                    .await?;
            }
            Err(e) => {
                log::error!("Failed to update translation dictionary: {}", e);
                bot.send_message(msg.chat.id, format!("❌ Ошибка при обновлении словаря: {}", e))
                    .await?;
            }
        }
    } else {
        bot.send_message(msg.chat.id, "❌ Неизвестная цель загрузки")
            .await?;
    }

    Ok(())
}
