import math
from datetime import UTC, datetime, timedelta
from typing import Any

import structlog
from aiogram import Dispatcher, F, types
from aiogram.fsm.context import FSMContext
from sqlalchemy.ext.asyncio import AsyncSession

from app.config import settings
from app.database.crud.server_squad import (
    count_active_users_for_squad,
    get_all_server_squads,
    get_server_squad_by_uuid,
)
from app.database.models import User
from app.keyboards.admin import (
    get_admin_remnawave_keyboard,
    get_node_management_keyboard,
    get_squad_edit_keyboard,
    get_squad_management_keyboard,
)
from app.localization.texts import get_texts
from app.services.remnawave_service import RemnaWaveConfigurationError, RemnaWaveService
from app.services.remnawave_sync_service import (
    RemnaWaveAutoSyncStatus,
    remnawave_sync_service,
)
from app.services.system_settings_service import bot_configuration_service
from app.states import (
    RemnaWaveSyncStates,
    SquadCreateStates,
    SquadMigrationStates,
    SquadRenameStates,
)
from app.utils.decorators import admin_required, error_handler
from app.utils.formatters import format_bytes, format_datetime


logger = structlog.get_logger(__name__)

squad_inbound_selections = {}
squad_create_data = {}

MIGRATION_PAGE_SIZE = 8


def _format_duration(seconds: float) -> str:
    if seconds < 1:
        return 'менее 1с'

    minutes, sec = divmod(int(seconds), 60)
    if minutes:
        if sec:
            return f'{minutes} мин {sec} с'
        return f'{minutes} мин'
    return f'{sec} с'


def _format_user_stats(stats: dict[str, Any] | None) -> str:
    if not stats:
        return '—'

    created = stats.get('created', 0)
    updated = stats.get('updated', 0)
    deleted = stats.get('deleted', stats.get('deactivated', 0))
    errors = stats.get('errors', 0)

    return f'• Создано: {created}\n• Обновлено: {updated}\n• Деактивировано: {deleted}\n• Ошибок: {errors}'


def _format_server_stats(stats: dict[str, Any] | None) -> str:
    if not stats:
        return '—'

    created = stats.get('created', 0)
    updated = stats.get('updated', 0)
    removed = stats.get('removed', 0)
    total = stats.get('total', 0)

    return f'• Создано: {created}\n• Обновлено: {updated}\n• Удалено: {removed}\n• Всего в панели: {total}'


def _build_auto_sync_view(status: RemnaWaveAutoSyncStatus) -> tuple[str, types.InlineKeyboardMarkup]:
    times_text = ', '.join(t.strftime('%H:%M') for t in status.times) if status.times else '—'
    next_run_text = format_datetime(status.next_run) if status.next_run else '—'

    if status.last_run_finished_at:
        finished_text = format_datetime(status.last_run_finished_at)
        started_text = format_datetime(status.last_run_started_at) if status.last_run_started_at else '—'
        duration = status.last_run_finished_at - status.last_run_started_at if status.last_run_started_at else None
        duration_text = f' ({_format_duration(duration.total_seconds())})' if duration else ''
        reason_map = {
            'manual': 'вручную',
            'auto': 'по расписанию',
            'immediate': 'при включении',
        }
        reason_text = reason_map.get(status.last_run_reason or '', '—')
        result_icon = '✅' if status.last_run_success else '❌'
        result_label = 'успешно' if status.last_run_success else 'с ошибками'
        error_block = f'\n⚠️ Ошибка: {status.last_run_error}' if status.last_run_error else ''
        last_run_text = (
            f'{result_icon} {result_label}\n'
            f'• Старт: {started_text}\n'
            f'• Завершено: {finished_text}{duration_text}\n'
            f'• Причина запуска: {reason_text}{error_block}'
        )
    elif status.last_run_started_at:
        last_run_text = (
            '⏳ Синхронизация началась, но еще не завершилась'
            if status.is_running
            else f'ℹ️ Последний запуск: {format_datetime(status.last_run_started_at)}'
        )
    else:
        last_run_text = '—'

    running_text = '⏳ Выполняется сейчас' if status.is_running else 'Ожидание'
    toggle_text = '❌ Отключить' if status.enabled else '✅ Включить'

    text = f"""🔄 <b>Автосинхронизация RemnaWave</b>

⚙️ <b>Статус:</b> {'✅ Включена' if status.enabled else '❌ Отключена'}
🕒 <b>Расписание:</b> {times_text}
📅 <b>Следующий запуск:</b> {next_run_text if status.enabled else '—'}
⏱️ <b>Состояние:</b> {running_text}

📊 <b>Последний запуск:</b>
{last_run_text}

👥 <b>Пользователи:</b>
{_format_user_stats(status.last_user_stats)}

🌐 <b>Серверы:</b>
{_format_server_stats(status.last_server_stats)}
"""

    keyboard = types.InlineKeyboardMarkup(
        inline_keyboard=[
            [
                types.InlineKeyboardButton(
                    text='🔁 Запустить сейчас',
                    callback_data='remnawave_auto_sync_run',
                )
            ],
            [
                types.InlineKeyboardButton(
                    text=toggle_text,
                    callback_data='remnawave_auto_sync_toggle',
                )
            ],
            [
                types.InlineKeyboardButton(
                    text='🕒 Изменить расписание',
                    callback_data='remnawave_auto_sync_times',
                )
            ],
            [
                types.InlineKeyboardButton(
                    text='⬅️ Назад',
                    callback_data='admin_rw_sync',
                )
            ],
        ]
    )

    return text, keyboard


def _format_migration_server_label(texts, server) -> str:
    status = (
        texts.t('ADMIN_SQUAD_MIGRATION_STATUS_AVAILABLE', '✅ Доступен')
        if getattr(server, 'is_available', True)
        else texts.t('ADMIN_SQUAD_MIGRATION_STATUS_UNAVAILABLE', '🚫 Недоступен')
    )
    return texts.t(
        'ADMIN_SQUAD_MIGRATION_SERVER_LABEL',
        '{name} — 👥 {users} ({status})',
    ).format(name=server.display_name, users=server.current_users, status=status)


def _build_migration_keyboard(
    texts,
    squads,
    page: int,
    total_pages: int,
    stage: str,
    *,
    exclude_uuid: str = None,
):
    prefix = 'admin_migration_source' if stage == 'source' else 'admin_migration_target'
    rows = []
    has_items = False

    button_template = texts.t(
        'ADMIN_SQUAD_MIGRATION_SQUAD_BUTTON',
        '🌍 {name} — 👥 {users} ({status})',
    )

    for squad in squads:
        if exclude_uuid and squad.squad_uuid == exclude_uuid:
            continue

        has_items = True
        status = (
            texts.t('ADMIN_SQUAD_MIGRATION_STATUS_AVAILABLE_SHORT', '✅')
            if getattr(squad, 'is_available', True)
            else texts.t('ADMIN_SQUAD_MIGRATION_STATUS_UNAVAILABLE_SHORT', '🚫')
        )
        rows.append(
            [
                types.InlineKeyboardButton(
                    text=button_template.format(
                        name=squad.display_name,
                        users=squad.current_users,
                        status=status,
                    ),
                    callback_data=f'{prefix}_{squad.squad_uuid}',
                )
            ]
        )

    if total_pages > 1:
        nav_buttons = []
        if page > 1:
            nav_buttons.append(
                types.InlineKeyboardButton(
                    text='⬅️',
                    callback_data=f'{prefix}_page_{page - 1}',
                )
            )
        nav_buttons.append(
            types.InlineKeyboardButton(
                text=texts.t(
                    'ADMIN_SQUAD_MIGRATION_PAGE',
                    'Стр. {page}/{pages}',
                ).format(page=page, pages=total_pages),
                callback_data='admin_migration_page_info',
            )
        )
        if page < total_pages:
            nav_buttons.append(
                types.InlineKeyboardButton(
                    text='➡️',
                    callback_data=f'{prefix}_page_{page + 1}',
                )
            )
        rows.append(nav_buttons)

    rows.append(
        [
            types.InlineKeyboardButton(
                text=texts.CANCEL,
                callback_data='admin_migration_cancel',
            )
        ]
    )

    return types.InlineKeyboardMarkup(inline_keyboard=rows), has_items


async def _fetch_migration_page(
    db: AsyncSession,
    page: int,
):
    squads, total = await get_all_server_squads(
        db,
        page=max(1, page),
        limit=MIGRATION_PAGE_SIZE,
    )
    total_pages = max(1, math.ceil(total / MIGRATION_PAGE_SIZE))

    page = max(page, 1)
    if page > total_pages:
        page = total_pages
        squads, total = await get_all_server_squads(
            db,
            page=page,
            limit=MIGRATION_PAGE_SIZE,
        )
        total_pages = max(1, math.ceil(total / MIGRATION_PAGE_SIZE))

    return squads, page, total_pages


@admin_required
@error_handler
async def show_squad_migration_menu(
    callback: types.CallbackQuery,
    db_user: User,
    db: AsyncSession,
    state: FSMContext,
):
    texts = get_texts(db_user.language)

    await state.clear()

    squads, page, total_pages = await _fetch_migration_page(db, page=1)
    keyboard, has_items = _build_migration_keyboard(
        texts,
        squads,
        page,
        total_pages,
        'source',
    )

    message = (
        texts.t('ADMIN_SQUAD_MIGRATION_TITLE', '🚚 <b>Переезд сквадов</b>')
        + '\n\n'
        + texts.t(
            'ADMIN_SQUAD_MIGRATION_SELECT_SOURCE',
            'Выберите сквад, из которого нужно переехать:',
        )
    )

    if not has_items:
        message += '\n\n' + texts.t(
            'ADMIN_SQUAD_MIGRATION_NO_OPTIONS',
            'Нет доступных сквадов. Добавьте новые или отмените операцию.',
        )

    await state.set_state(SquadMigrationStates.selecting_source)

    await callback.message.edit_text(
        message,
        reply_markup=keyboard,
        disable_web_page_preview=True,
    )
    await callback.answer()


@admin_required
@error_handler
async def paginate_migration_source(
    callback: types.CallbackQuery,
    db_user: User,
    db: AsyncSession,
    state: FSMContext,
):
    if await state.get_state() != SquadMigrationStates.selecting_source:
        await callback.answer()
        return

    try:
        page = int(callback.data.split('_page_')[-1])
    except (ValueError, IndexError):
        await callback.answer()
        return

    squads, page, total_pages = await _fetch_migration_page(db, page=page)
    texts = get_texts(db_user.language)
    keyboard, has_items = _build_migration_keyboard(
        texts,
        squads,
        page,
        total_pages,
        'source',
    )

    message = (
        texts.t('ADMIN_SQUAD_MIGRATION_TITLE', '🚚 <b>Переезд сквадов</b>')
        + '\n\n'
        + texts.t(
            'ADMIN_SQUAD_MIGRATION_SELECT_SOURCE',
            'Выберите сквад, из которого нужно переехать:',
        )
    )

    if not has_items:
        message += '\n\n' + texts.t(
            'ADMIN_SQUAD_MIGRATION_NO_OPTIONS',
            'Нет доступных сквадов. Добавьте новые или отмените операцию.',
        )

    await callback.message.edit_text(
        message,
        reply_markup=keyboard,
        disable_web_page_preview=True,
    )
    await callback.answer()


@admin_required
@error_handler
async def handle_migration_source_selection(
    callback: types.CallbackQuery,
    db_user: User,
    db: AsyncSession,
    state: FSMContext,
):
    if await state.get_state() != SquadMigrationStates.selecting_source:
        await callback.answer()
        return

    if '_page_' in callback.data:
        await callback.answer()
        return

    source_uuid = callback.data.replace('admin_migration_source_', '', 1)

    texts = get_texts(db_user.language)
    server = await get_server_squad_by_uuid(db, source_uuid)

    if not server:
        await callback.answer(
            texts.t(
                'ADMIN_SQUAD_MIGRATION_SQUAD_NOT_FOUND',
                'Сквад не найден или недоступен.',
            ),
            show_alert=True,
        )
        return

    await state.update_data(
        source_uuid=server.squad_uuid,
        source_display=_format_migration_server_label(texts, server),
    )

    squads, page, total_pages = await _fetch_migration_page(db, page=1)
    keyboard, has_items = _build_migration_keyboard(
        texts,
        squads,
        page,
        total_pages,
        'target',
        exclude_uuid=server.squad_uuid,
    )

    message = (
        texts.t('ADMIN_SQUAD_MIGRATION_TITLE', '🚚 <b>Переезд сквадов</b>')
        + '\n\n'
        + texts.t(
            'ADMIN_SQUAD_MIGRATION_SELECTED_SOURCE',
            'Источник: {source}',
        ).format(source=_format_migration_server_label(texts, server))
        + '\n\n'
        + texts.t(
            'ADMIN_SQUAD_MIGRATION_SELECT_TARGET',
            'Выберите сквад, в который нужно переехать:',
        )
    )

    if not has_items:
        message += '\n\n' + texts.t(
            'ADMIN_SQUAD_MIGRATION_TARGET_EMPTY',
            'Нет других сквадов для переезда. Отмените операцию или создайте новые сквады.',
        )

    await state.set_state(SquadMigrationStates.selecting_target)

    await callback.message.edit_text(
        message,
        reply_markup=keyboard,
        disable_web_page_preview=True,
    )
    await callback.answer()


@admin_required
@error_handler
async def paginate_migration_target(
    callback: types.CallbackQuery,
    db_user: User,
    db: AsyncSession,
    state: FSMContext,
):
    if await state.get_state() != SquadMigrationStates.selecting_target:
        await callback.answer()
        return

    try:
        page = int(callback.data.split('_page_')[-1])
    except (ValueError, IndexError):
        await callback.answer()
        return

    data = await state.get_data()
    source_uuid = data.get('source_uuid')
    if not source_uuid:
        await callback.answer()
        return

    texts = get_texts(db_user.language)

    squads, page, total_pages = await _fetch_migration_page(db, page=page)
    keyboard, has_items = _build_migration_keyboard(
        texts,
        squads,
        page,
        total_pages,
        'target',
        exclude_uuid=source_uuid,
    )

    source_display = data.get('source_display') or source_uuid

    message = (
        texts.t('ADMIN_SQUAD_MIGRATION_TITLE', '🚚 <b>Переезд сквадов</b>')
        + '\n\n'
        + texts.t(
            'ADMIN_SQUAD_MIGRATION_SELECTED_SOURCE',
            'Источник: {source}',
        ).format(source=source_display)
        + '\n\n'
        + texts.t(
            'ADMIN_SQUAD_MIGRATION_SELECT_TARGET',
            'Выберите сквад, в который нужно переехать:',
        )
    )

    if not has_items:
        message += '\n\n' + texts.t(
            'ADMIN_SQUAD_MIGRATION_TARGET_EMPTY',
            'Нет других сквадов для переезда. Отмените операцию или создайте новые сквады.',
        )

    await callback.message.edit_text(
        message,
        reply_markup=keyboard,
        disable_web_page_preview=True,
    )
    await callback.answer()


@admin_required
@error_handler
async def handle_migration_target_selection(
    callback: types.CallbackQuery,
    db_user: User,
    db: AsyncSession,
    state: FSMContext,
):
    current_state = await state.get_state()
    if current_state != SquadMigrationStates.selecting_target:
        await callback.answer()
        return

    if '_page_' in callback.data:
        await callback.answer()
        return

    data = await state.get_data()
    source_uuid = data.get('source_uuid')

    if not source_uuid:
        await callback.answer()
        return

    target_uuid = callback.data.replace('admin_migration_target_', '', 1)

    texts = get_texts(db_user.language)

    if target_uuid == source_uuid:
        await callback.answer(
            texts.t(
                'ADMIN_SQUAD_MIGRATION_SAME_SQUAD',
                'Нельзя выбрать тот же сквад.',
            ),
            show_alert=True,
        )
        return

    target_server = await get_server_squad_by_uuid(db, target_uuid)
    if not target_server:
        await callback.answer(
            texts.t(
                'ADMIN_SQUAD_MIGRATION_SQUAD_NOT_FOUND',
                'Сквад не найден или недоступен.',
            ),
            show_alert=True,
        )
        return

    source_display = data.get('source_display') or source_uuid

    users_to_move = await count_active_users_for_squad(db, source_uuid)

    await state.update_data(
        target_uuid=target_server.squad_uuid,
        target_display=_format_migration_server_label(texts, target_server),
        migration_count=users_to_move,
    )

    await state.set_state(SquadMigrationStates.confirming)

    message_lines = [
        texts.t('ADMIN_SQUAD_MIGRATION_TITLE', '🚚 <b>Переезд сквадов</b>'),
        '',
        texts.t(
            'ADMIN_SQUAD_MIGRATION_CONFIRM_DETAILS',
            'Проверьте параметры переезда:',
        ),
        texts.t(
            'ADMIN_SQUAD_MIGRATION_CONFIRM_SOURCE',
            '• Из: {source}',
        ).format(source=source_display),
        texts.t(
            'ADMIN_SQUAD_MIGRATION_CONFIRM_TARGET',
            '• В: {target}',
        ).format(target=_format_migration_server_label(texts, target_server)),
        texts.t(
            'ADMIN_SQUAD_MIGRATION_CONFIRM_COUNT',
            '• Пользователей к переносу: {count}',
        ).format(count=users_to_move),
        '',
        texts.t(
            'ADMIN_SQUAD_MIGRATION_CONFIRM_PROMPT',
            'Подтвердите выполнение операции.',
        ),
    ]

    keyboard = types.InlineKeyboardMarkup(
        inline_keyboard=[
            [
                types.InlineKeyboardButton(
                    text=texts.t(
                        'ADMIN_SQUAD_MIGRATION_CONFIRM_BUTTON',
                        '✅ Подтвердить',
                    ),
                    callback_data='admin_migration_confirm',
                )
            ],
            [
                types.InlineKeyboardButton(
                    text=texts.t(
                        'ADMIN_SQUAD_MIGRATION_CHANGE_TARGET',
                        '🔄 Изменить сервер назначения',
                    ),
                    callback_data='admin_migration_change_target',
                )
            ],
            [
                types.InlineKeyboardButton(
                    text=texts.CANCEL,
                    callback_data='admin_migration_cancel',
                )
            ],
        ]
    )

    await callback.message.edit_text(
        '\n'.join(message_lines),
        reply_markup=keyboard,
        disable_web_page_preview=True,
    )
    await callback.answer()


@admin_required
@error_handler
async def change_migration_target(
    callback: types.CallbackQuery,
    db_user: User,
    db: AsyncSession,
    state: FSMContext,
):
    data = await state.get_data()
    source_uuid = data.get('source_uuid')

    if not source_uuid:
        await callback.answer()
        return

    await state.set_state(SquadMigrationStates.selecting_target)

    texts = get_texts(db_user.language)
    squads, page, total_pages = await _fetch_migration_page(db, page=1)
    keyboard, has_items = _build_migration_keyboard(
        texts,
        squads,
        page,
        total_pages,
        'target',
        exclude_uuid=source_uuid,
    )

    source_display = data.get('source_display') or source_uuid

    message = (
        texts.t('ADMIN_SQUAD_MIGRATION_TITLE', '🚚 <b>Переезд сквадов</b>')
        + '\n\n'
        + texts.t(
            'ADMIN_SQUAD_MIGRATION_SELECTED_SOURCE',
            'Источник: {source}',
        ).format(source=source_display)
        + '\n\n'
        + texts.t(
            'ADMIN_SQUAD_MIGRATION_SELECT_TARGET',
            'Выберите сквад, в который нужно переехать:',
        )
    )

    if not has_items:
        message += '\n\n' + texts.t(
            'ADMIN_SQUAD_MIGRATION_TARGET_EMPTY',
            'Нет других сквадов для переезда. Отмените операцию или создайте новые сквады.',
        )

    await callback.message.edit_text(
        message,
        reply_markup=keyboard,
        disable_web_page_preview=True,
    )
    await callback.answer()


@admin_required
@error_handler
async def confirm_squad_migration(
    callback: types.CallbackQuery,
    db_user: User,
    db: AsyncSession,
    state: FSMContext,
):
    current_state = await state.get_state()
    if current_state != SquadMigrationStates.confirming:
        await callback.answer()
        return

    data = await state.get_data()
    source_uuid = data.get('source_uuid')
    target_uuid = data.get('target_uuid')

    if not source_uuid or not target_uuid:
        await callback.answer()
        return

    texts = get_texts(db_user.language)
    remnawave_service = RemnaWaveService()

    await callback.answer(texts.t('ADMIN_SQUAD_MIGRATION_IN_PROGRESS', 'Запускаю переезд...'))

    try:
        result = await remnawave_service.migrate_squad_users(
            db,
            source_uuid=source_uuid,
            target_uuid=target_uuid,
        )
    except RemnaWaveConfigurationError as error:
        message = texts.t(
            'ADMIN_SQUAD_MIGRATION_API_ERROR',
            '❌ RemnaWave API не настроен: {error}',
        ).format(error=str(error))
        reply_markup = types.InlineKeyboardMarkup(
            inline_keyboard=[
                [
                    types.InlineKeyboardButton(
                        text=texts.t(
                            'ADMIN_SQUAD_MIGRATION_BACK_BUTTON',
                            '⬅️ В Remnawave',
                        ),
                        callback_data='admin_remnawave',
                    )
                ]
            ]
        )
        await callback.message.edit_text(message, reply_markup=reply_markup)
        await state.clear()
        return

    source_display = data.get('source_display') or source_uuid
    target_display = data.get('target_display') or target_uuid

    if not result.get('success'):
        error_message = result.get('message') or ''
        error_code = result.get('error') or 'unexpected'
        message = texts.t(
            'ADMIN_SQUAD_MIGRATION_ERROR',
            '❌ Не удалось выполнить переезд (код: {code}). {details}',
        ).format(code=error_code, details=error_message)
        reply_markup = types.InlineKeyboardMarkup(
            inline_keyboard=[
                [
                    types.InlineKeyboardButton(
                        text=texts.t(
                            'ADMIN_SQUAD_MIGRATION_BACK_BUTTON',
                            '⬅️ В Remnawave',
                        ),
                        callback_data='admin_remnawave',
                    )
                ],
                [
                    types.InlineKeyboardButton(
                        text=texts.t(
                            'ADMIN_SQUAD_MIGRATION_NEW_BUTTON',
                            '🔁 Новый переезд',
                        ),
                        callback_data='admin_rw_migration',
                    )
                ],
            ]
        )
        await callback.message.edit_text(message, reply_markup=reply_markup)
        await state.clear()
        return

    message_lines = [
        texts.t('ADMIN_SQUAD_MIGRATION_SUCCESS_TITLE', '✅ Переезд завершен'),
        '',
        texts.t('ADMIN_SQUAD_MIGRATION_CONFIRM_SOURCE', '• Из: {source}').format(source=source_display),
        texts.t('ADMIN_SQUAD_MIGRATION_CONFIRM_TARGET', '• В: {target}').format(target=target_display),
        '',
        texts.t(
            'ADMIN_SQUAD_MIGRATION_RESULT_TOTAL',
            'Найдено подписок: {count}',
        ).format(count=result.get('total', 0)),
        texts.t(
            'ADMIN_SQUAD_MIGRATION_RESULT_UPDATED',
            'Перенесено: {count}',
        ).format(count=result.get('updated', 0)),
    ]

    panel_updated = result.get('panel_updated', 0)
    panel_failed = result.get('panel_failed', 0)

    if panel_updated:
        message_lines.append(
            texts.t(
                'ADMIN_SQUAD_MIGRATION_RESULT_PANEL_UPDATED',
                'Обновлено в панели: {count}',
            ).format(count=panel_updated)
        )
    if panel_failed:
        message_lines.append(
            texts.t(
                'ADMIN_SQUAD_MIGRATION_RESULT_PANEL_FAILED',
                'Не удалось обновить в панели: {count}',
            ).format(count=panel_failed)
        )

    reply_markup = types.InlineKeyboardMarkup(
        inline_keyboard=[
            [
                types.InlineKeyboardButton(
                    text=texts.t(
                        'ADMIN_SQUAD_MIGRATION_NEW_BUTTON',
                        '🔁 Новый переезд',
                    ),
                    callback_data='admin_rw_migration',
                )
            ],
            [
                types.InlineKeyboardButton(
                    text=texts.t(
                        'ADMIN_SQUAD_MIGRATION_BACK_BUTTON',
                        '⬅️ В Remnawave',
                    ),
                    callback_data='admin_remnawave',
                )
            ],
        ]
    )

    await callback.message.edit_text(
        '\n'.join(message_lines),
        reply_markup=reply_markup,
        disable_web_page_preview=True,
    )
    await state.clear()


@admin_required
@error_handler
async def cancel_squad_migration(
    callback: types.CallbackQuery,
    db_user: User,
    db: AsyncSession,
    state: FSMContext,
):
    texts = get_texts(db_user.language)
    await state.clear()

    message = texts.t(
        'ADMIN_SQUAD_MIGRATION_CANCELLED',
        '❌ Переезд отменен.',
    )

    reply_markup = types.InlineKeyboardMarkup(
        inline_keyboard=[
            [
                types.InlineKeyboardButton(
                    text=texts.t(
                        'ADMIN_SQUAD_MIGRATION_BACK_BUTTON',
                        '⬅️ В Remnawave',
                    ),
                    callback_data='admin_remnawave',
                )
            ]
        ]
    )

    await callback.message.edit_text(message, reply_markup=reply_markup)
    await callback.answer()


@admin_required
@error_handler
async def handle_migration_page_info(
    callback: types.CallbackQuery,
    db_user: User,
    db: AsyncSession,
    state: FSMContext,
):
    texts = get_texts(db_user.language)
    await callback.answer(
        texts.t('ADMIN_SQUAD_MIGRATION_PAGE_HINT', 'Это текущая страница.'),
        show_alert=False,
    )


@admin_required
@error_handler
async def show_remnawave_menu(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    remnawave_service = RemnaWaveService()
    connection_test = await remnawave_service.test_api_connection()

    status = connection_test.get('status')
    if status == 'connected':
        status_emoji = '✅'
    elif status == 'not_configured':
        status_emoji = 'ℹ️'
    else:
        status_emoji = '❌'

    api_url_display = settings.REMNAWAVE_API_URL or '—'

    text = f"""
🖥️ <b>Управление Remnawave</b>

📡 <b>Соединение:</b> {status_emoji} {connection_test.get('message', 'Нет данных')}
🌐 <b>URL:</b> <code>{api_url_display}</code>

Выберите действие:
"""

    await callback.message.edit_text(text, reply_markup=get_admin_remnawave_keyboard(db_user.language))
    await callback.answer()


@admin_required
@error_handler
async def show_system_stats(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    remnawave_service = RemnaWaveService()
    stats = await remnawave_service.get_system_statistics()

    if 'error' in stats:
        await callback.message.edit_text(
            f'❌ Ошибка получения статистики: {stats["error"]}',
            reply_markup=types.InlineKeyboardMarkup(
                inline_keyboard=[[types.InlineKeyboardButton(text='⬅️ Назад', callback_data='admin_remnawave')]]
            ),
        )
        await callback.answer()
        return

    system = stats.get('system', {})
    users_by_status = stats.get('users_by_status', {})
    server_info = stats.get('server_info', {})
    bandwidth = stats.get('bandwidth', {})
    traffic_periods = stats.get('traffic_periods', {})
    nodes_realtime = stats.get('nodes_realtime', [])
    nodes_weekly = stats.get('nodes_weekly', [])

    memory_total = server_info.get('memory_total', 1)
    memory_used_percent = (server_info.get('memory_used', 0) / memory_total * 100) if memory_total > 0 else 0

    uptime_seconds = server_info.get('uptime_seconds', 0)
    uptime_days = int(uptime_seconds // 86400)
    uptime_hours = int((uptime_seconds % 86400) // 3600)
    uptime_str = f'{uptime_days}д {uptime_hours}ч'

    users_status_text = ''
    for status, count in users_by_status.items():
        status_emoji = {'ACTIVE': '✅', 'DISABLED': '❌', 'LIMITED': '⚠️', 'EXPIRED': '⏰'}.get(status, '❓')
        users_status_text += f'  {status_emoji} {status}: {count}\n'

    top_nodes_text = ''
    for i, node in enumerate(nodes_weekly[:3], 1):
        top_nodes_text += f'  {i}. {node["name"]}: {format_bytes(node["total_bytes"])}\n'

    realtime_nodes_text = ''
    for node in nodes_realtime[:3]:
        node_total = node.get('downloadBytes', 0) + node.get('uploadBytes', 0)
        if node_total > 0:
            realtime_nodes_text += f'  📡 {node.get("nodeName", "Unknown")}: {format_bytes(node_total)}\n'

    def format_traffic_change(difference_str):
        if not difference_str or difference_str == '0':
            return ''
        if difference_str.startswith('-'):
            return f' (🔻 {difference_str[1:]})'
        return f' (🔺 {difference_str})'

    text = f"""
📊 <b>Детальная статистика Remnawave</b>

🖥️ <b>Сервер:</b>
- CPU: {server_info.get('cpu_cores', 0)} ядер ({server_info.get('cpu_physical_cores', 0)} физ.)
- RAM: {format_bytes(server_info.get('memory_used', 0))} / {format_bytes(memory_total)} ({memory_used_percent:.1f}%)
- Свободно: {format_bytes(server_info.get('memory_available', 0))}
- Uptime: {uptime_str}

👥 <b>Пользователи ({system.get('total_users', 0)} всего):</b>
- 🟢 Онлайн сейчас: {system.get('users_online', 0)}
- 📅 За сутки: {system.get('users_last_day', 0)}
- 📊 За неделю: {system.get('users_last_week', 0)}
- 💤 Никогда не заходили: {system.get('users_never_online', 0)}

<b>Статусы пользователей:</b>
{users_status_text}

🌐 <b>Ноды ({system.get('nodes_online', 0)} онлайн):</b>"""

    if realtime_nodes_text:
        text += f"""
<b>Реалтайм активность:</b>
{realtime_nodes_text}"""

    if top_nodes_text:
        text += f"""
<b>Топ нод за неделю:</b>
{top_nodes_text}"""

    text += f"""

📈 <b>Общий трафик пользователей:</b> {format_bytes(system.get('total_user_traffic', 0))}

📊 <b>Трафик по периодам:</b>
- 2 дня: {format_bytes(traffic_periods.get('last_2_days', {}).get('current', 0))}{format_traffic_change(traffic_periods.get('last_2_days', {}).get('difference', ''))}
- 7 дней: {format_bytes(traffic_periods.get('last_7_days', {}).get('current', 0))}{format_traffic_change(traffic_periods.get('last_7_days', {}).get('difference', ''))}
- 30 дней: {format_bytes(traffic_periods.get('last_30_days', {}).get('current', 0))}{format_traffic_change(traffic_periods.get('last_30_days', {}).get('difference', ''))}
- Месяц: {format_bytes(traffic_periods.get('current_month', {}).get('current', 0))}{format_traffic_change(traffic_periods.get('current_month', {}).get('difference', ''))}
- Год: {format_bytes(traffic_periods.get('current_year', {}).get('current', 0))}{format_traffic_change(traffic_periods.get('current_year', {}).get('difference', ''))}
"""

    if bandwidth.get('realtime_total', 0) > 0:
        text += f"""
⚡ <b>Реалтайм трафик:</b>
- Скачивание: {format_bytes(bandwidth.get('realtime_download', 0))}
- Загрузка: {format_bytes(bandwidth.get('realtime_upload', 0))}
- Итого: {format_bytes(bandwidth.get('realtime_total', 0))}
"""

    text += f"""
🕒 <b>Обновлено:</b> {format_datetime(stats.get('last_updated', datetime.now(UTC)))}
"""

    keyboard = [
        [types.InlineKeyboardButton(text='🔄 Обновить', callback_data='admin_rw_system')],
        [
            types.InlineKeyboardButton(text='📈 Ноды', callback_data='admin_rw_nodes'),
            types.InlineKeyboardButton(text='👥 Синхронизация', callback_data='admin_rw_sync'),
        ],
        [types.InlineKeyboardButton(text='⬅️ Назад', callback_data='admin_remnawave')],
    ]

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))
    await callback.answer()


@admin_required
@error_handler
async def show_traffic_stats(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    remnawave_service = RemnaWaveService()

    try:
        async with remnawave_service.get_api_client() as api:
            bandwidth_stats = await api.get_bandwidth_stats()

            realtime_usage = await api.get_nodes_realtime_usage()

            nodes_stats = await api.get_nodes_statistics()

    except Exception as e:
        await callback.message.edit_text(
            f'❌ Ошибка получения статистики трафика: {e!s}',
            reply_markup=types.InlineKeyboardMarkup(
                inline_keyboard=[[types.InlineKeyboardButton(text='⬅️ Назад', callback_data='admin_remnawave')]]
            ),
        )
        await callback.answer()
        return

    def parse_bandwidth(bandwidth_str):
        return remnawave_service._parse_bandwidth_string(bandwidth_str)

    total_realtime_download = sum(node.get('downloadBytes', 0) for node in realtime_usage)
    total_realtime_upload = sum(node.get('uploadBytes', 0) for node in realtime_usage)
    total_realtime = total_realtime_download + total_realtime_upload

    total_download_speed = sum(node.get('downloadSpeedBps', 0) for node in realtime_usage)
    total_upload_speed = sum(node.get('uploadSpeedBps', 0) for node in realtime_usage)

    periods = {
        'last_2_days': bandwidth_stats.get('bandwidthLastTwoDays', {}),
        'last_7_days': bandwidth_stats.get('bandwidthLastSevenDays', {}),
        'last_30_days': bandwidth_stats.get('bandwidthLast30Days', {}),
        'current_month': bandwidth_stats.get('bandwidthCalendarMonth', {}),
        'current_year': bandwidth_stats.get('bandwidthCurrentYear', {}),
    }

    def format_change(diff_str):
        if not diff_str or diff_str == '0':
            return ''
        if diff_str.startswith('-'):
            return f' 🔻 {diff_str[1:]}'
        return f' 🔺 {diff_str}'

    text = f"""
📊 <b>Статистика трафика Remnawave</b>

⚡ <b>Реалтайм данные:</b>
- Скачивание: {format_bytes(total_realtime_download)}
- Загрузка: {format_bytes(total_realtime_upload)}
- Общий трафик: {format_bytes(total_realtime)}

🚀 <b>Текущие скорости:</b>
- Скорость скачивания: {format_bytes(total_download_speed)}/с
- Скорость загрузки: {format_bytes(total_upload_speed)}/с
- Общая скорость: {format_bytes(total_download_speed + total_upload_speed)}/с

📈 <b>Статистика по периодам:</b>

<b>За 2 дня:</b>
- Текущий: {format_bytes(parse_bandwidth(periods['last_2_days'].get('current', '0')))}
- Предыдущий: {format_bytes(parse_bandwidth(periods['last_2_days'].get('previous', '0')))}
- Изменение:{format_change(periods['last_2_days'].get('difference', ''))}

<b>За 7 дней:</b>
- Текущий: {format_bytes(parse_bandwidth(periods['last_7_days'].get('current', '0')))}
- Предыдущий: {format_bytes(parse_bandwidth(periods['last_7_days'].get('previous', '0')))}
- Изменение:{format_change(periods['last_7_days'].get('difference', ''))}

<b>За 30 дней:</b>
- Текущий: {format_bytes(parse_bandwidth(periods['last_30_days'].get('current', '0')))}
- Предыдущий: {format_bytes(parse_bandwidth(periods['last_30_days'].get('previous', '0')))}
- Изменение:{format_change(periods['last_30_days'].get('difference', ''))}

<b>Текущий месяц:</b>
- Текущий: {format_bytes(parse_bandwidth(periods['current_month'].get('current', '0')))}
- Предыдущий: {format_bytes(parse_bandwidth(periods['current_month'].get('previous', '0')))}
- Изменение:{format_change(periods['current_month'].get('difference', ''))}

<b>Текущий год:</b>
- Текущий: {format_bytes(parse_bandwidth(periods['current_year'].get('current', '0')))}
- Предыдущий: {format_bytes(parse_bandwidth(periods['current_year'].get('previous', '0')))}
- Изменение:{format_change(periods['current_year'].get('difference', ''))}
"""

    if realtime_usage:
        text += '\n🌐 <b>Трафик по нодам (реалтайм):</b>\n'
        for node in sorted(realtime_usage, key=lambda x: x.get('totalBytes', 0), reverse=True):
            node_total = node.get('totalBytes', 0)
            if node_total > 0:
                text += f'- {node.get("nodeName", "Unknown")}: {format_bytes(node_total)}\n'

    if nodes_stats.get('lastSevenDays'):
        text += '\n📊 <b>Топ нод за 7 дней:</b>\n'

        nodes_weekly = {}
        for day_data in nodes_stats['lastSevenDays']:
            node_name = day_data['nodeName']
            if node_name not in nodes_weekly:
                nodes_weekly[node_name] = 0
            nodes_weekly[node_name] += int(day_data['totalBytes'])

        sorted_nodes = sorted(nodes_weekly.items(), key=lambda x: x[1], reverse=True)
        for i, (node_name, total_bytes) in enumerate(sorted_nodes[:5], 1):
            text += f'{i}. {node_name}: {format_bytes(total_bytes)}\n'

    text += f'\n🕒 <b>Обновлено:</b> {format_datetime(datetime.now(UTC))}'

    keyboard = [
        [types.InlineKeyboardButton(text='🔄 Обновить', callback_data='admin_rw_traffic')],
        [
            types.InlineKeyboardButton(text='📈 Ноды', callback_data='admin_rw_nodes'),
            types.InlineKeyboardButton(text='📊 Система', callback_data='admin_rw_system'),
        ],
        [types.InlineKeyboardButton(text='⬅️ Назад', callback_data='admin_remnawave')],
    ]

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))
    await callback.answer()


@admin_required
@error_handler
async def show_nodes_management(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    remnawave_service = RemnaWaveService()
    nodes = await remnawave_service.get_all_nodes()

    if not nodes:
        await callback.message.edit_text(
            '🖥️ Ноды не найдены или ошибка подключения',
            reply_markup=types.InlineKeyboardMarkup(
                inline_keyboard=[[types.InlineKeyboardButton(text='⬅️ Назад', callback_data='admin_remnawave')]]
            ),
        )
        await callback.answer()
        return

    text = '🖥️ <b>Управление нодами</b>\n\n'
    keyboard = []

    for node in nodes:
        status_emoji = '🟢' if node['is_node_online'] else '🔴'
        connection_emoji = '📡' if node['is_connected'] else '📵'

        text += f'{status_emoji} {connection_emoji} <b>{node["name"]}</b>\n'
        text += f'🌍 {node["country_code"]} • {node["address"]}\n'
        text += f'👥 Онлайн: {node["users_online"] or 0}\n\n'

        keyboard.append(
            [types.InlineKeyboardButton(text=f'⚙️ {node["name"]}', callback_data=f'admin_node_manage_{node["uuid"]}')]
        )

    keyboard.extend(
        [
            [types.InlineKeyboardButton(text='🔄 Перезагрузить все', callback_data='admin_restart_all_nodes')],
            [types.InlineKeyboardButton(text='⬅️ Назад', callback_data='admin_remnawave')],
        ]
    )

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))
    await callback.answer()


@admin_required
@error_handler
async def show_node_details(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    node_uuid = callback.data.split('_')[-1]

    remnawave_service = RemnaWaveService()
    node = await remnawave_service.get_node_details(node_uuid)

    if not node:
        await callback.answer('❌ Нода не найдена', show_alert=True)
        return

    status_emoji = '🟢' if node['is_node_online'] else '🔴'
    xray_emoji = '✅' if node['is_xray_running'] else '❌'

    status_change = format_datetime(node['last_status_change']) if node.get('last_status_change') else '—'
    created_at = format_datetime(node['created_at']) if node.get('created_at') else '—'
    updated_at = format_datetime(node['updated_at']) if node.get('updated_at') else '—'
    notify_percent = f'{node["notify_percent"]}%' if node.get('notify_percent') is not None else '—'
    cpu_info = node.get('cpu_model') or '—'
    if node.get('cpu_count'):
        cpu_info = f'{node["cpu_count"]}x {cpu_info}'

    text = f"""
🖥️ <b>Нода: {node['name']}</b>

<b>Статус:</b>
- Онлайн: {status_emoji} {'Да' if node['is_node_online'] else 'Нет'}
- Xray: {xray_emoji} {'Запущен' if node['is_xray_running'] else 'Остановлен'}
- Подключена: {'📡 Да' if node['is_connected'] else '📵 Нет'}
- Отключена: {'❌ Да' if node['is_disabled'] else '✅ Нет'}
- Изменение статуса: {status_change}
- Сообщение: {node.get('last_status_message') or '—'}
- Uptime Xray: {node.get('xray_uptime') or '—'}

<b>Информация:</b>
- Адрес: {node['address']}
- Страна: {node['country_code']}
- Пользователей онлайн: {node['users_online']}
- CPU: {cpu_info}
- RAM: {node.get('total_ram') or '—'}
- Провайдер: {node.get('provider_uuid') or '—'}

<b>Трафик:</b>
- Использовано: {format_bytes(node['traffic_used_bytes'])}
- Лимит: {format_bytes(node['traffic_limit_bytes']) if node['traffic_limit_bytes'] else 'Без лимита'}
- Трекинг: {'✅ Активен' if node.get('is_traffic_tracking_active') else '❌ Отключен'}
- День сброса: {node.get('traffic_reset_day') or '—'}
- Уведомления: {notify_percent}
- Множитель: {node.get('consumption_multiplier') or 1}

<b>Метаданные:</b>
- Создана: {created_at}
- Обновлена: {updated_at}
"""

    await callback.message.edit_text(text, reply_markup=get_node_management_keyboard(node_uuid, db_user.language))
    await callback.answer()


@admin_required
@error_handler
async def manage_node(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    action, node_uuid = callback.data.split('_')[1], callback.data.split('_')[-1]

    remnawave_service = RemnaWaveService()
    success = await remnawave_service.manage_node(node_uuid, action)

    if success:
        action_text = {'enable': 'включена', 'disable': 'отключена', 'restart': 'перезагружена'}
        await callback.answer(f'✅ Нода {action_text.get(action, "обработана")}')
    else:
        await callback.answer('❌ Ошибка выполнения действия', show_alert=True)

    await show_node_details(callback, db_user, db)


@admin_required
@error_handler
async def show_node_statistics(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    node_uuid = callback.data.split('_')[-1]

    remnawave_service = RemnaWaveService()

    node = await remnawave_service.get_node_details(node_uuid)

    if not node:
        await callback.answer('❌ Нода не найдена', show_alert=True)
        return

    try:
        end_date = datetime.now(UTC)
        start_date = end_date - timedelta(days=7)

        node_usage = await remnawave_service.get_node_user_usage_by_range(node_uuid, start_date, end_date)

        realtime_stats = await remnawave_service.get_nodes_realtime_usage()

        node_realtime = None
        for stats in realtime_stats:
            if stats.get('nodeUuid') == node_uuid:
                node_realtime = stats
                break

        status_change = format_datetime(node['last_status_change']) if node.get('last_status_change') else '—'
        created_at = format_datetime(node['created_at']) if node.get('created_at') else '—'
        updated_at = format_datetime(node['updated_at']) if node.get('updated_at') else '—'
        notify_percent = f'{node["notify_percent"]}%' if node.get('notify_percent') is not None else '—'
        cpu_info = node.get('cpu_model') or '—'
        if node.get('cpu_count'):
            cpu_info = f'{node["cpu_count"]}x {cpu_info}'

        status_emoji = '🟢' if node['is_node_online'] else '🔴'
        xray_emoji = '✅' if node['is_xray_running'] else '❌'

        text = f"""
📊 <b>Статистика ноды: {node['name']}</b>

<b>Статус:</b>
- Онлайн: {status_emoji} {'Да' if node['is_node_online'] else 'Нет'}
- Xray: {xray_emoji} {'Запущен' if node['is_xray_running'] else 'Остановлен'}
- Пользователей онлайн: {node['users_online'] or 0}
- Изменение статуса: {status_change}
- Сообщение: {node.get('last_status_message') or '—'}
- Uptime Xray: {node.get('xray_uptime') or '—'}

<b>Ресурсы:</b>
- CPU: {cpu_info}
- RAM: {node.get('total_ram') or '—'}
- Провайдер: {node.get('provider_uuid') or '—'}

<b>Трафик:</b>
- Использовано: {format_bytes(node['traffic_used_bytes'] or 0)}
- Лимит: {format_bytes(node['traffic_limit_bytes']) if node['traffic_limit_bytes'] else 'Без лимита'}
- Трекинг: {'✅ Активен' if node.get('is_traffic_tracking_active') else '❌ Отключен'}
- День сброса: {node.get('traffic_reset_day') or '—'}
- Уведомления: {notify_percent}
- Множитель: {node.get('consumption_multiplier') or 1}

<b>Метаданные:</b>
- Создана: {created_at}
- Обновлена: {updated_at}
"""

        if node_realtime:
            text += f"""
<b>Реалтайм статистика:</b>
- Скачано: {format_bytes(node_realtime.get('downloadBytes', 0))}
- Загружено: {format_bytes(node_realtime.get('uploadBytes', 0))}
- Общий трафик: {format_bytes(node_realtime.get('totalBytes', 0))}
- Скорость скачивания: {format_bytes(node_realtime.get('downloadSpeedBps', 0))}/с
- Скорость загрузки: {format_bytes(node_realtime.get('uploadSpeedBps', 0))}/с
"""

        if node_usage:
            text += '\n<b>Статистика за 7 дней:</b>\n'
            total_usage = 0
            for usage in node_usage[-5:]:
                daily_usage = usage.get('total', 0)
                total_usage += daily_usage
                text += f'- {usage.get("date", "N/A")}: {format_bytes(daily_usage)}\n'

            text += f'\n<b>Общий трафик за 7 дней:</b> {format_bytes(total_usage)}'
        else:
            text += '\n<b>Статистика за 7 дней:</b> Данные недоступны'

        keyboard = types.InlineKeyboardMarkup(
            inline_keyboard=[
                [types.InlineKeyboardButton(text='🔄 Обновить', callback_data=f'node_stats_{node_uuid}')],
                [types.InlineKeyboardButton(text='⬅️ Назад', callback_data=f'admin_node_manage_{node_uuid}')],
            ]
        )

        await callback.message.edit_text(text, reply_markup=keyboard)
        await callback.answer()

    except Exception as e:
        logger.error('Ошибка получения статистики ноды', node_uuid=node_uuid, error=e)

        text = f"""
📊 <b>Статистика ноды: {node['name']}</b>

<b>Статус:</b>
- Онлайн: {status_emoji} {'Да' if node['is_node_online'] else 'Нет'}
- Xray: {xray_emoji} {'Запущен' if node['is_xray_running'] else 'Остановлен'}
- Пользователей онлайн: {node['users_online'] or 0}
- Изменение статуса: {format_datetime(node.get('last_status_change')) if node.get('last_status_change') else '—'}
- Сообщение: {node.get('last_status_message') or '—'}
- Uptime Xray: {node.get('xray_uptime') or '—'}

<b>Трафик:</b>
- Использовано: {format_bytes(node['traffic_used_bytes'] or 0)}
- Лимит: {format_bytes(node['traffic_limit_bytes']) if node['traffic_limit_bytes'] else 'Без лимита'}
- Трекинг: {'✅ Активен' if node.get('is_traffic_tracking_active') else '❌ Отключен'}
- День сброса: {node.get('traffic_reset_day') or '—'}
- Уведомления: {node.get('notify_percent') or '—'}
- Множитель: {node.get('consumption_multiplier') or 1}

⚠️ <b>Детальная статистика временно недоступна</b>
Возможные причины:
• Проблемы с подключением к API
• Нода недавно добавлена
• Недостаточно данных для отображения

<b>Обновлено:</b> {format_datetime('now')}
"""

        keyboard = types.InlineKeyboardMarkup(
            inline_keyboard=[
                [types.InlineKeyboardButton(text='🔄 Попробовать снова', callback_data=f'node_stats_{node_uuid}')],
                [types.InlineKeyboardButton(text='⬅️ Назад', callback_data=f'admin_node_manage_{node_uuid}')],
            ]
        )

        await callback.message.edit_text(text, reply_markup=keyboard)
        await callback.answer()


@admin_required
@error_handler
async def show_squad_details(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    squad_uuid = callback.data.split('_')[-1]

    remnawave_service = RemnaWaveService()
    squad = await remnawave_service.get_squad_details(squad_uuid)

    if not squad:
        await callback.answer('❌ Сквад не найден', show_alert=True)
        return

    text = f"""
🌐 <b>Сквад: {squad['name']}</b>

<b>Информация:</b>
- UUID: <code>{squad['uuid']}</code>
- Участников: {squad['members_count']}
- Инбаундов: {squad['inbounds_count']}

<b>Инбаунды:</b>
"""

    if squad.get('inbounds'):
        for inbound in squad['inbounds']:
            text += f'- {inbound["tag"]} ({inbound["type"]})\n'
    else:
        text += 'Нет активных инбаундов'

    await callback.message.edit_text(text, reply_markup=get_squad_management_keyboard(squad_uuid, db_user.language))
    await callback.answer()


@admin_required
@error_handler
async def manage_squad_action(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    parts = callback.data.split('_')
    action = parts[1]
    squad_uuid = parts[-1]

    remnawave_service = RemnaWaveService()

    if action == 'add_users':
        success = await remnawave_service.add_all_users_to_squad(squad_uuid)
        if success:
            await callback.answer('✅ Задача добавления пользователей в очередь')
        else:
            await callback.answer('❌ Ошибка добавления пользователей', show_alert=True)

    elif action == 'remove_users':
        success = await remnawave_service.remove_all_users_from_squad(squad_uuid)
        if success:
            await callback.answer('✅ Задача удаления пользователей в очередь')
        else:
            await callback.answer('❌ Ошибка удаления пользователей', show_alert=True)

    elif action == 'delete':
        success = await remnawave_service.delete_squad(squad_uuid)
        if success:
            await callback.message.edit_text(
                '✅ Сквад успешно удален',
                reply_markup=types.InlineKeyboardMarkup(
                    inline_keyboard=[[types.InlineKeyboardButton(text='⬅️ К сквадам', callback_data='admin_rw_squads')]]
                ),
            )
        else:
            await callback.answer('❌ Ошибка удаления сквада', show_alert=True)
        return

    refreshed_callback = callback.model_copy(update={'data': f'admin_squad_manage_{squad_uuid}'}).as_(callback.bot)

    await show_squad_details(refreshed_callback, db_user, db)


@admin_required
@error_handler
async def show_squad_edit_menu(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    squad_uuid = callback.data.split('_')[-1]

    remnawave_service = RemnaWaveService()
    squad = await remnawave_service.get_squad_details(squad_uuid)

    if not squad:
        await callback.answer('❌ Сквад не найден', show_alert=True)
        return

    text = f"""
✏️ <b>Редактирование сквада: {squad['name']}</b>

<b>Текущие инбаунды:</b>
"""

    if squad.get('inbounds'):
        for inbound in squad['inbounds']:
            text += f'✅ {inbound["tag"]} ({inbound["type"]})\n'
    else:
        text += 'Нет активных инбаундов\n'

    text += '\n<b>Доступные действия:</b>'

    await callback.message.edit_text(text, reply_markup=get_squad_edit_keyboard(squad_uuid, db_user.language))
    await callback.answer()


@admin_required
@error_handler
async def show_squad_inbounds_selection(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    squad_uuid = callback.data.split('_')[-1]

    remnawave_service = RemnaWaveService()

    squad = await remnawave_service.get_squad_details(squad_uuid)
    all_inbounds = await remnawave_service.get_all_inbounds()

    if not squad:
        await callback.answer('❌ Сквад не найден', show_alert=True)
        return

    if not all_inbounds:
        await callback.answer('❌ Нет доступных инбаундов', show_alert=True)
        return

    if squad_uuid not in squad_inbound_selections:
        squad_inbound_selections[squad_uuid] = {inbound['uuid'] for inbound in squad.get('inbounds', [])}

    text = f"""
🔧 <b>Изменение инбаундов</b>

<b>Сквад:</b> {squad['name']}
<b>Текущих инбаундов:</b> {len(squad_inbound_selections[squad_uuid])}

<b>Доступные инбаунды:</b>
"""

    keyboard = []

    for i, inbound in enumerate(all_inbounds[:15]):
        is_selected = inbound['uuid'] in squad_inbound_selections[squad_uuid]
        emoji = '✅' if is_selected else '☐'

        keyboard.append(
            [
                types.InlineKeyboardButton(
                    text=f'{emoji} {inbound["tag"]} ({inbound["type"]})', callback_data=f'sqd_tgl_{i}_{squad_uuid[:8]}'
                )
            ]
        )

    if len(all_inbounds) > 15:
        text += f'\n⚠️ Показано первые 15 из {len(all_inbounds)} инбаундов'

    keyboard.extend(
        [
            [types.InlineKeyboardButton(text='💾 Сохранить изменения', callback_data=f'sqd_save_{squad_uuid[:8]}')],
            [types.InlineKeyboardButton(text='⬅️ Назад', callback_data=f'sqd_edit_{squad_uuid[:8]}')],
        ]
    )

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))
    await callback.answer()


@admin_required
@error_handler
async def show_squad_rename_form(callback: types.CallbackQuery, db_user: User, db: AsyncSession, state: FSMContext):
    squad_uuid = callback.data.split('_')[-1]

    remnawave_service = RemnaWaveService()
    squad = await remnawave_service.get_squad_details(squad_uuid)

    if not squad:
        await callback.answer('❌ Сквад не найден', show_alert=True)
        return

    await state.update_data(squad_uuid=squad_uuid, squad_name=squad['name'])
    await state.set_state(SquadRenameStates.waiting_for_new_name)

    text = f"""
✏️ <b>Переименование сквада</b>

<b>Текущее название:</b> {squad['name']}

📝 <b>Введите новое название сквада:</b>

<i>Требования к названию:</i>
• От 2 до 20 символов
• Только буквы, цифры, дефисы и подчеркивания
• Без пробелов и специальных символов

Отправьте сообщение с новым названием или нажмите "Отмена" для выхода.
"""

    keyboard = [[types.InlineKeyboardButton(text='❌ Отмена', callback_data=f'cancel_rename_{squad_uuid}')]]

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))
    await callback.answer()


@admin_required
@error_handler
async def cancel_squad_rename(callback: types.CallbackQuery, db_user: User, db: AsyncSession, state: FSMContext):
    squad_uuid = callback.data.split('_')[-1]

    await state.clear()

    refreshed_callback = callback.model_copy(update={'data': f'squad_edit_{squad_uuid}'}).as_(callback.bot)

    await show_squad_edit_menu(refreshed_callback, db_user, db)


@admin_required
@error_handler
async def process_squad_new_name(message: types.Message, db_user: User, db: AsyncSession, state: FSMContext):
    data = await state.get_data()
    squad_uuid = data.get('squad_uuid')
    old_name = data.get('squad_name')

    if not squad_uuid:
        await message.answer('❌ Ошибка: сквад не найден')
        await state.clear()
        return

    new_name = message.text.strip()

    if not new_name:
        await message.answer('❌ Название не может быть пустым. Попробуйте еще раз:')
        return

    if len(new_name) < 2 or len(new_name) > 20:
        await message.answer('❌ Название должно быть от 2 до 20 символов. Попробуйте еще раз:')
        return

    import re

    if not re.match(r'^[A-Za-z0-9_-]+$', new_name):
        await message.answer(
            '❌ Название может содержать только буквы, цифры, дефисы и подчеркивания. Попробуйте еще раз:'
        )
        return

    if new_name == old_name:
        await message.answer('❌ Новое название совпадает с текущим. Введите другое название:')
        return

    remnawave_service = RemnaWaveService()
    success = await remnawave_service.rename_squad(squad_uuid, new_name)

    if success:
        await message.answer(
            f'✅ <b>Сквад успешно переименован!</b>\n\n'
            f'<b>Старое название:</b> {old_name}\n'
            f'<b>Новое название:</b> {new_name}',
            reply_markup=types.InlineKeyboardMarkup(
                inline_keyboard=[
                    [
                        types.InlineKeyboardButton(
                            text='📋 Детали сквада', callback_data=f'admin_squad_manage_{squad_uuid}'
                        )
                    ],
                    [types.InlineKeyboardButton(text='⬅️ К сквадам', callback_data='admin_rw_squads')],
                ]
            ),
        )
        await state.clear()
    else:
        await message.answer(
            '❌ <b>Ошибка переименования сквада</b>\n\n'
            'Возможные причины:\n'
            '• Сквад с таким названием уже существует\n'
            '• Проблемы с подключением к API\n'
            '• Недостаточно прав\n\n'
            'Попробуйте другое название:',
            reply_markup=types.InlineKeyboardMarkup(
                inline_keyboard=[
                    [types.InlineKeyboardButton(text='❌ Отмена', callback_data=f'cancel_rename_{squad_uuid}')]
                ]
            ),
        )


@admin_required
@error_handler
async def toggle_squad_inbound(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    parts = callback.data.split('_')
    inbound_index = int(parts[2])
    short_squad_uuid = parts[3]

    remnawave_service = RemnaWaveService()
    squads = await remnawave_service.get_all_squads()

    full_squad_uuid = None
    for squad in squads:
        if squad['uuid'].startswith(short_squad_uuid):
            full_squad_uuid = squad['uuid']
            break

    if not full_squad_uuid:
        await callback.answer('❌ Сквад не найден', show_alert=True)
        return

    all_inbounds = await remnawave_service.get_all_inbounds()
    if inbound_index >= len(all_inbounds):
        await callback.answer('❌ Инбаунд не найден', show_alert=True)
        return

    selected_inbound = all_inbounds[inbound_index]

    if full_squad_uuid not in squad_inbound_selections:
        squad_inbound_selections[full_squad_uuid] = set()

    if selected_inbound['uuid'] in squad_inbound_selections[full_squad_uuid]:
        squad_inbound_selections[full_squad_uuid].remove(selected_inbound['uuid'])
        await callback.answer(f'➖ Убран: {selected_inbound["tag"]}')
    else:
        squad_inbound_selections[full_squad_uuid].add(selected_inbound['uuid'])
        await callback.answer(f'➕ Добавлен: {selected_inbound["tag"]}')

    text = f"""
🔧 <b>Изменение инбаундов</b>

<b>Сквад:</b> {squads[0]['name'] if squads else 'Неизвестно'}
<b>Выбрано инбаундов:</b> {len(squad_inbound_selections[full_squad_uuid])}

<b>Доступные инбаунды:</b>
"""

    keyboard = []
    for i, inbound in enumerate(all_inbounds[:15]):
        is_selected = inbound['uuid'] in squad_inbound_selections[full_squad_uuid]
        emoji = '✅' if is_selected else '☐'

        keyboard.append(
            [
                types.InlineKeyboardButton(
                    text=f'{emoji} {inbound["tag"]} ({inbound["type"]})',
                    callback_data=f'sqd_tgl_{i}_{short_squad_uuid}',
                )
            ]
        )

    keyboard.extend(
        [
            [types.InlineKeyboardButton(text='💾 Сохранить изменения', callback_data=f'sqd_save_{short_squad_uuid}')],
            [types.InlineKeyboardButton(text='⬅️ Назад', callback_data=f'sqd_edit_{short_squad_uuid}')],
        ]
    )

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))


@admin_required
@error_handler
async def save_squad_inbounds(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    short_squad_uuid = callback.data.split('_')[-1]

    remnawave_service = RemnaWaveService()
    squads = await remnawave_service.get_all_squads()

    full_squad_uuid = None
    squad_name = None
    for squad in squads:
        if squad['uuid'].startswith(short_squad_uuid):
            full_squad_uuid = squad['uuid']
            squad_name = squad['name']
            break

    if not full_squad_uuid:
        await callback.answer('❌ Сквад не найден', show_alert=True)
        return

    selected_inbounds = squad_inbound_selections.get(full_squad_uuid, set())

    try:
        success = await remnawave_service.update_squad_inbounds(full_squad_uuid, list(selected_inbounds))

        if success:
            squad_inbound_selections.pop(full_squad_uuid, None)

            await callback.message.edit_text(
                f'✅ <b>Инбаунды сквада обновлены</b>\n\n'
                f'<b>Сквад:</b> {squad_name}\n'
                f'<b>Количество инбаундов:</b> {len(selected_inbounds)}',
                reply_markup=types.InlineKeyboardMarkup(
                    inline_keyboard=[
                        [types.InlineKeyboardButton(text='⬅️ К сквадам', callback_data='admin_rw_squads')],
                        [
                            types.InlineKeyboardButton(
                                text='📋 Детали сквада', callback_data=f'admin_squad_manage_{full_squad_uuid}'
                            )
                        ],
                    ]
                ),
            )
            await callback.answer('✅ Изменения сохранены!')
        else:
            await callback.answer('❌ Ошибка сохранения изменений', show_alert=True)

    except Exception as e:
        logger.error('Error saving squad inbounds', error=e)
        await callback.answer('❌ Ошибка при сохранении', show_alert=True)


@admin_required
@error_handler
async def show_squad_edit_menu_short(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    short_squad_uuid = callback.data.split('_')[-1]

    remnawave_service = RemnaWaveService()
    squads = await remnawave_service.get_all_squads()

    full_squad_uuid = None
    for squad in squads:
        if squad['uuid'].startswith(short_squad_uuid):
            full_squad_uuid = squad['uuid']
            break

    if not full_squad_uuid:
        await callback.answer('❌ Сквад не найден', show_alert=True)
        return

    refreshed_callback = callback.model_copy(update={'data': f'squad_edit_{full_squad_uuid}'}).as_(callback.bot)

    await show_squad_edit_menu(refreshed_callback, db_user, db)


@admin_required
@error_handler
async def start_squad_creation(callback: types.CallbackQuery, db_user: User, db: AsyncSession, state: FSMContext):
    await state.set_state(SquadCreateStates.waiting_for_name)

    text = """
➕ <b>Создание нового сквада</b>

<b>Шаг 1 из 2: Название сквада</b>

📝 <b>Введите название для нового сквада:</b>

<i>Требования к названию:</i>
• От 2 до 20 символов
• Только буквы, цифры, дефисы и подчеркивания
• Без пробелов и специальных символов

Отправьте сообщение с названием или нажмите "Отмена" для выхода.
"""

    keyboard = [[types.InlineKeyboardButton(text='❌ Отмена', callback_data='cancel_squad_create')]]

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))
    await callback.answer()


@admin_required
@error_handler
async def process_squad_name(message: types.Message, db_user: User, db: AsyncSession, state: FSMContext):
    squad_name = message.text.strip()

    if not squad_name:
        await message.answer('❌ Название не может быть пустым. Попробуйте еще раз:')
        return

    if len(squad_name) < 2 or len(squad_name) > 20:
        await message.answer('❌ Название должно быть от 2 до 20 символов. Попробуйте еще раз:')
        return

    import re

    if not re.match(r'^[A-Za-z0-9_-]+$', squad_name):
        await message.answer(
            '❌ Название может содержать только буквы, цифры, дефисы и подчеркивания. Попробуйте еще раз:'
        )
        return

    await state.update_data(squad_name=squad_name)
    await state.set_state(SquadCreateStates.selecting_inbounds)

    user_id = message.from_user.id
    squad_create_data[user_id] = {'name': squad_name, 'selected_inbounds': set()}

    remnawave_service = RemnaWaveService()
    all_inbounds = await remnawave_service.get_all_inbounds()

    if not all_inbounds:
        await message.answer(
            '❌ <b>Нет доступных инбаундов</b>\n\nДля создания сквада необходимо иметь хотя бы один инбаунд.',
            reply_markup=types.InlineKeyboardMarkup(
                inline_keyboard=[[types.InlineKeyboardButton(text='⬅️ К сквадам', callback_data='admin_rw_squads')]]
            ),
        )
        await state.clear()
        return

    text = f"""
➕ <b>Создание сквада: {squad_name}</b>

<b>Шаг 2 из 2: Выбор инбаундов</b>

<b>Выбрано инбаундов:</b> 0

<b>Доступные инбаунды:</b>
"""

    keyboard = []

    for i, inbound in enumerate(all_inbounds[:15]):
        keyboard.append(
            [
                types.InlineKeyboardButton(
                    text=f'☐ {inbound["tag"]} ({inbound["type"]})', callback_data=f'create_tgl_{i}'
                )
            ]
        )

    if len(all_inbounds) > 15:
        text += f'\n⚠️ Показано первые 15 из {len(all_inbounds)} инбаундов'

    keyboard.extend(
        [
            [types.InlineKeyboardButton(text='✅ Создать сквад', callback_data='create_squad_finish')],
            [types.InlineKeyboardButton(text='❌ Отмена', callback_data='cancel_squad_create')],
        ]
    )

    await message.answer(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))


@admin_required
@error_handler
async def toggle_create_inbound(callback: types.CallbackQuery, db_user: User, db: AsyncSession, state: FSMContext):
    inbound_index = int(callback.data.split('_')[-1])
    user_id = callback.from_user.id

    if user_id not in squad_create_data:
        await callback.answer('❌ Ошибка: данные сессии не найдены', show_alert=True)
        await state.clear()
        return

    remnawave_service = RemnaWaveService()
    all_inbounds = await remnawave_service.get_all_inbounds()

    if inbound_index >= len(all_inbounds):
        await callback.answer('❌ Инбаунд не найден', show_alert=True)
        return

    selected_inbound = all_inbounds[inbound_index]
    selected_inbounds = squad_create_data[user_id]['selected_inbounds']

    if selected_inbound['uuid'] in selected_inbounds:
        selected_inbounds.remove(selected_inbound['uuid'])
        await callback.answer(f'➖ Убран: {selected_inbound["tag"]}')
    else:
        selected_inbounds.add(selected_inbound['uuid'])
        await callback.answer(f'➕ Добавлен: {selected_inbound["tag"]}')

    squad_name = squad_create_data[user_id]['name']

    text = f"""
➕ <b>Создание сквада: {squad_name}</b>

<b>Шаг 2 из 2: Выбор инбаундов</b>

<b>Выбрано инбаундов:</b> {len(selected_inbounds)}

<b>Доступные инбаунды:</b>
"""

    keyboard = []

    for i, inbound in enumerate(all_inbounds[:15]):
        is_selected = inbound['uuid'] in selected_inbounds
        emoji = '✅' if is_selected else '☐'

        keyboard.append(
            [
                types.InlineKeyboardButton(
                    text=f'{emoji} {inbound["tag"]} ({inbound["type"]})', callback_data=f'create_tgl_{i}'
                )
            ]
        )

    keyboard.extend(
        [
            [types.InlineKeyboardButton(text='✅ Создать сквад', callback_data='create_squad_finish')],
            [types.InlineKeyboardButton(text='❌ Отмена', callback_data='cancel_squad_create')],
        ]
    )

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))


@admin_required
@error_handler
async def finish_squad_creation(callback: types.CallbackQuery, db_user: User, db: AsyncSession, state: FSMContext):
    user_id = callback.from_user.id

    if user_id not in squad_create_data:
        await callback.answer('❌ Ошибка: данные сессии не найдены', show_alert=True)
        await state.clear()
        return

    squad_name = squad_create_data[user_id]['name']
    selected_inbounds = list(squad_create_data[user_id]['selected_inbounds'])

    if not selected_inbounds:
        await callback.answer('❌ Необходимо выбрать хотя бы один инбаунд', show_alert=True)
        return

    remnawave_service = RemnaWaveService()
    success = await remnawave_service.create_squad(squad_name, selected_inbounds)

    squad_create_data.pop(user_id, None)
    await state.clear()

    if success:
        await callback.message.edit_text(
            f'✅ <b>Сквад успешно создан!</b>\n\n'
            f'<b>Название:</b> {squad_name}\n'
            f'<b>Количество инбаундов:</b> {len(selected_inbounds)}\n\n'
            f'Сквад готов к использованию!',
            reply_markup=types.InlineKeyboardMarkup(
                inline_keyboard=[
                    [types.InlineKeyboardButton(text='📋 Список сквадов', callback_data='admin_rw_squads')],
                    [types.InlineKeyboardButton(text='⬅️ К панели Remnawave', callback_data='admin_remnawave')],
                ]
            ),
        )
        await callback.answer('✅ Сквад создан!')
    else:
        await callback.message.edit_text(
            f'❌ <b>Ошибка создания сквада</b>\n\n'
            f'<b>Название:</b> {squad_name}\n\n'
            f'Возможные причины:\n'
            f'• Сквад с таким названием уже существует\n'
            f'• Проблемы с подключением к API\n'
            f'• Недостаточно прав\n'
            f'• Некорректные инбаунды',
            reply_markup=types.InlineKeyboardMarkup(
                inline_keyboard=[
                    [types.InlineKeyboardButton(text='🔄 Попробовать снова', callback_data='admin_squad_create')],
                    [types.InlineKeyboardButton(text='⬅️ К сквадам', callback_data='admin_rw_squads')],
                ]
            ),
        )
        await callback.answer('❌ Ошибка создания сквада', show_alert=True)


@admin_required
@error_handler
async def cancel_squad_creation(callback: types.CallbackQuery, db_user: User, db: AsyncSession, state: FSMContext):
    user_id = callback.from_user.id

    squad_create_data.pop(user_id, None)
    await state.clear()

    await show_squads_management(callback, db_user, db)


@admin_required
@error_handler
async def restart_all_nodes(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    remnawave_service = RemnaWaveService()
    success = await remnawave_service.restart_all_nodes()

    if success:
        await callback.message.edit_text(
            '✅ Команда перезагрузки всех нод отправлена',
            reply_markup=types.InlineKeyboardMarkup(
                inline_keyboard=[[types.InlineKeyboardButton(text='⬅️ К нодам', callback_data='admin_rw_nodes')]]
            ),
        )
    else:
        await callback.message.edit_text(
            '❌ Ошибка перезагрузки нод',
            reply_markup=types.InlineKeyboardMarkup(
                inline_keyboard=[[types.InlineKeyboardButton(text='⬅️ К нодам', callback_data='admin_rw_nodes')]]
            ),
        )

    await callback.answer()


@admin_required
@error_handler
async def show_sync_options(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    status = remnawave_sync_service.get_status()
    times_text = ', '.join(t.strftime('%H:%M') for t in status.times) if status.times else '—'
    next_run_text = format_datetime(status.next_run) if status.next_run else '—'
    last_result = '—'

    if status.last_run_finished_at:
        result_icon = '✅' if status.last_run_success else '❌'
        result_label = 'успешно' if status.last_run_success else 'с ошибками'
        finished_text = format_datetime(status.last_run_finished_at)
        last_result = f'{result_icon} {result_label} ({finished_text})'
    elif status.last_run_started_at:
        last_result = f'⏳ Запущено {format_datetime(status.last_run_started_at)}'

    status_lines = [
        f'⚙️ Статус: {"✅ Включена" if status.enabled else "❌ Отключена"}',
        f'🕒 Расписание: {times_text}',
        f'📅 Следующий запуск: {next_run_text if status.enabled else "—"}',
        f'📊 Последний запуск: {last_result}',
    ]

    text = (
        '🔄 <b>Синхронизация с Remnawave</b>\n\n'
        '🔄 <b>Полная синхронизация выполняет:</b>\n'
        '• Создание новых пользователей из панели в боте\n'
        '• Обновление данных существующих пользователей\n'
        '• Деактивация подписок пользователей, отсутствующих в панели\n'
        '• Сохранение балансов пользователей\n'
        '• ⏱️ Время выполнения: 2-5 минут\n\n'
        '⚠️ <b>Важно:</b>\n'
        '• Во время синхронизации не выполняйте другие операции\n'
        '• При полной синхронизации подписки пользователей, отсутствующих в панели, будут деактивированы\n'
        '• Рекомендуется делать полную синхронизацию ежедневно\n'
        '• Баланс пользователей НЕ удаляется\n\n'
        '⬆️ <b>Обратная синхронизация:</b>\n'
        '• Отправляет активных пользователей из бота в панель\n'
        '• Используйте при сбоях панели или для восстановления данных\n\n' + '\n'.join(status_lines)
    )

    keyboard = [
        [
            types.InlineKeyboardButton(
                text='🔄 Запустить полную синхронизацию',
                callback_data='sync_all_users',
            )
        ],
        [
            types.InlineKeyboardButton(
                text='⬆️ Синхронизация в панель',
                callback_data='sync_to_panel',
            )
        ],
        [
            types.InlineKeyboardButton(
                text='⚙️ Настройки автосинхронизации',
                callback_data='admin_rw_auto_sync',
            )
        ],
        [types.InlineKeyboardButton(text='⬅️ Назад', callback_data='admin_remnawave')],
    ]

    await callback.message.edit_text(
        text,
        reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard),
    )
    await callback.answer()


@admin_required
@error_handler
async def show_auto_sync_settings(
    callback: types.CallbackQuery,
    db_user: User,
    db: AsyncSession,
    state: FSMContext,
):
    await state.clear()
    status = remnawave_sync_service.get_status()
    text, keyboard = _build_auto_sync_view(status)

    await callback.message.edit_text(
        text,
        reply_markup=keyboard,
        parse_mode='HTML',
    )
    await callback.answer()


@admin_required
@error_handler
async def toggle_auto_sync_setting(
    callback: types.CallbackQuery,
    db_user: User,
    db: AsyncSession,
    state: FSMContext,
):
    await state.clear()
    new_value = not bool(settings.REMNAWAVE_AUTO_SYNC_ENABLED)
    await bot_configuration_service.set_value(
        db,
        'REMNAWAVE_AUTO_SYNC_ENABLED',
        new_value,
    )
    await db.commit()

    status = remnawave_sync_service.get_status()
    text, keyboard = _build_auto_sync_view(status)

    await callback.message.edit_text(
        text,
        reply_markup=keyboard,
        parse_mode='HTML',
    )
    await callback.answer(f'Автосинхронизация {"включена" if new_value else "отключена"}')


@admin_required
@error_handler
async def prompt_auto_sync_schedule(
    callback: types.CallbackQuery,
    db_user: User,
    db: AsyncSession,
    state: FSMContext,
):
    status = remnawave_sync_service.get_status()
    current_schedule = ', '.join(t.strftime('%H:%M') for t in status.times) if status.times else '—'

    instructions = (
        '🕒 <b>Настройка расписания автосинхронизации</b>\n\n'
        'Укажите время запуска через запятую или с новой строки в формате HH:MM.\n'
        f'Текущее расписание: <code>{current_schedule}</code>\n\n'
        'Примеры: <code>03:00, 15:30</code> или <code>00:15\n06:00\n18:45</code>\n\n'
        'Отправьте <b>отмена</b>, чтобы вернуться без изменений.'
    )

    await state.set_state(RemnaWaveSyncStates.waiting_for_schedule)
    await state.update_data(
        auto_sync_message_id=callback.message.message_id,
        auto_sync_message_chat_id=callback.message.chat.id,
    )

    await callback.message.edit_text(
        instructions,
        parse_mode='HTML',
        reply_markup=types.InlineKeyboardMarkup(
            inline_keyboard=[
                [
                    types.InlineKeyboardButton(
                        text='❌ Отмена',
                        callback_data='remnawave_auto_sync_cancel',
                    )
                ]
            ]
        ),
    )
    await callback.answer()


@admin_required
@error_handler
async def cancel_auto_sync_schedule(
    callback: types.CallbackQuery,
    db_user: User,
    db: AsyncSession,
    state: FSMContext,
):
    await state.clear()
    status = remnawave_sync_service.get_status()
    text, keyboard = _build_auto_sync_view(status)

    await callback.message.edit_text(
        text,
        reply_markup=keyboard,
        parse_mode='HTML',
    )
    await callback.answer('Изменение расписания отменено')


@admin_required
@error_handler
async def run_auto_sync_now(
    callback: types.CallbackQuery,
    db_user: User,
    db: AsyncSession,
    state: FSMContext,
):
    if remnawave_sync_service.get_status().is_running:
        await callback.answer('Синхронизация уже выполняется', show_alert=True)
        return

    await state.clear()
    await callback.message.edit_text(
        '🔄 Запуск автосинхронизации...\n\nПодождите, это может занять несколько минут.',
        parse_mode='HTML',
    )
    await callback.answer('Автосинхронизация запущена')

    result = await remnawave_sync_service.run_sync_now(reason='manual')
    status = remnawave_sync_service.get_status()
    base_text, keyboard = _build_auto_sync_view(status)

    if not result.get('started'):
        await callback.message.edit_text(
            '⚠️ <b>Синхронизация уже выполняется</b>\n\n' + base_text,
            reply_markup=keyboard,
            parse_mode='HTML',
        )
        return

    if result.get('success'):
        user_stats = result.get('user_stats') or {}
        server_stats = result.get('server_stats') or {}
        summary = (
            '✅ <b>Синхронизация завершена</b>\n'
            f'👥 Пользователи: создано {user_stats.get("created", 0)}, обновлено {user_stats.get("updated", 0)}, '
            f'деактивировано {user_stats.get("deleted", user_stats.get("deactivated", 0))}, ошибок {user_stats.get("errors", 0)}\n'
            f'🌐 Серверы: создано {server_stats.get("created", 0)}, обновлено {server_stats.get("updated", 0)}, удалено {server_stats.get("removed", 0)}\n\n'
        )
        final_text = summary + base_text
        await callback.message.edit_text(
            final_text,
            reply_markup=keyboard,
            parse_mode='HTML',
        )
    else:
        error_text = result.get('error') or 'Неизвестная ошибка'
        summary = f'❌ <b>Синхронизация завершилась с ошибкой</b>\nПричина: {error_text}\n\n'
        await callback.message.edit_text(
            summary + base_text,
            reply_markup=keyboard,
            parse_mode='HTML',
        )


@admin_required
@error_handler
async def save_auto_sync_schedule(
    message: types.Message,
    db_user: User,
    db: AsyncSession,
    state: FSMContext,
):
    text = (message.text or '').strip()
    data = await state.get_data()

    if text.lower() in {'отмена', 'cancel'}:
        await state.clear()
        status = remnawave_sync_service.get_status()
        view_text, keyboard = _build_auto_sync_view(status)
        message_id = data.get('auto_sync_message_id')
        chat_id = data.get('auto_sync_message_chat_id', message.chat.id)
        if message_id:
            await message.bot.edit_message_text(
                view_text,
                chat_id=chat_id,
                message_id=message_id,
                reply_markup=keyboard,
                parse_mode='HTML',
            )
        else:
            await message.answer(
                view_text,
                reply_markup=keyboard,
                parse_mode='HTML',
            )
        await message.answer('Настройка расписания отменена')
        return

    parsed_times = settings.parse_daily_time_list(text)

    if not parsed_times:
        await message.answer(
            '❌ Не удалось распознать время. Используйте формат HH:MM, например 03:00 или 18:45.',
        )
        return

    normalized_value = ', '.join(t.strftime('%H:%M') for t in parsed_times)
    await bot_configuration_service.set_value(
        db,
        'REMNAWAVE_AUTO_SYNC_TIMES',
        normalized_value,
    )
    await db.commit()

    status = remnawave_sync_service.get_status()
    view_text, keyboard = _build_auto_sync_view(status)
    message_id = data.get('auto_sync_message_id')
    chat_id = data.get('auto_sync_message_chat_id', message.chat.id)

    if message_id:
        await message.bot.edit_message_text(
            view_text,
            chat_id=chat_id,
            message_id=message_id,
            reply_markup=keyboard,
            parse_mode='HTML',
        )
    else:
        await message.answer(
            view_text,
            reply_markup=keyboard,
            parse_mode='HTML',
        )

    await state.clear()
    await message.answer('✅ Расписание автосинхронизации обновлено')


@admin_required
@error_handler
async def sync_all_users(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    """Выполняет полную синхронизацию всех пользователей"""

    progress_text = """
🔄 <b>Выполняется полная синхронизация...</b>

📋 Этапы:
• Загрузка ВСЕХ пользователей из панели Remnawave
• Создание новых пользователей в боте
• Обновление существующих пользователей
• Деактивация подписок отсутствующих пользователей
• Сохранение балансов

⏳ Пожалуйста, подождите...
"""

    await callback.message.edit_text(progress_text, reply_markup=None)

    remnawave_service = RemnaWaveService()
    stats = await remnawave_service.sync_users_from_panel(db, 'all')

    total_operations = stats['created'] + stats['updated'] + stats.get('deleted', 0)

    if stats['errors'] == 0:
        status_emoji = '✅'
        status_text = 'успешно завершена'
    elif stats['errors'] < total_operations:
        status_emoji = '⚠️'
        status_text = 'завершена с предупреждениями'
    else:
        status_emoji = '❌'
        status_text = 'завершена с ошибками'

    text = f"""
{status_emoji} <b>Полная синхронизация {status_text}</b>

📊 <b>Результат:</b>
• 🆕 Создано: {stats['created']}
• 🔄 Обновлено: {stats['updated']}
• 🗑️ Деактивировано: {stats.get('deleted', 0)}
• ❌ Ошибок: {stats['errors']}
"""

    if stats.get('deleted', 0) > 0:
        text += """

🗑️ <b>Деактивированные подписки:</b>
Деактивированы подписки пользователей, которые
отсутствуют в панели Remnawave.
💰 Балансы пользователей сохранены.
"""

    if stats['errors'] > 0:
        text += """

⚠️ <b>Внимание:</b>
Некоторые операции завершились с ошибками.
Проверьте логи для получения подробной информации.
"""

    text += """

💡 <b>Рекомендации:</b>
• Полная синхронизация выполнена
• Рекомендуется запускать раз в день
• Все пользователи из панели синхронизированы
"""

    keyboard = []

    if stats['errors'] > 0:
        keyboard.append([types.InlineKeyboardButton(text='🔄 Повторить синхронизацию', callback_data='sync_all_users')])

    keyboard.extend(
        [
            [
                types.InlineKeyboardButton(text='📊 Статистика системы', callback_data='admin_rw_system'),
                types.InlineKeyboardButton(text='🌐 Ноды', callback_data='admin_rw_nodes'),
            ],
            [types.InlineKeyboardButton(text='⬅️ Назад', callback_data='admin_remnawave')],
        ]
    )

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))
    await callback.answer()


@admin_required
@error_handler
async def sync_users_to_panel(
    callback: types.CallbackQuery,
    db_user: User,
    db: AsyncSession,
):
    await callback.message.edit_text(
        '⬆️ Выполняется синхронизация данных бота в панель Remnawave...\n\nЭто может занять несколько минут.',
        reply_markup=None,
    )

    remnawave_service = RemnaWaveService()
    stats = await remnawave_service.sync_users_to_panel(db)

    if stats['errors'] == 0:
        status_emoji = '✅'
        status_text = 'успешно завершена'
    else:
        status_emoji = '⚠️' if (stats['created'] + stats['updated']) > 0 else '❌'
        status_text = 'завершена с предупреждениями' if status_emoji == '⚠️' else 'завершена с ошибками'

    text = (
        f'{status_emoji} <b>Синхронизация в панель {status_text}</b>\n\n'
        '📊 <b>Результаты:</b>\n'
        f'• 🆕 Создано: {stats["created"]}\n'
        f'• 🔄 Обновлено: {stats["updated"]}\n'
        f'• ❌ Ошибок: {stats["errors"]}'
    )

    keyboard = [
        [types.InlineKeyboardButton(text='🔄 Повторить', callback_data='sync_to_panel')],
        [types.InlineKeyboardButton(text='🔄 Полная синхронизация', callback_data='sync_all_users')],
        [types.InlineKeyboardButton(text='⬅️ К синхронизации', callback_data='admin_rw_sync')],
    ]

    await callback.message.edit_text(
        text,
        reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard),
    )
    await callback.answer()


@admin_required
@error_handler
async def show_sync_recommendations(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    await callback.message.edit_text('🔍 Анализируем состояние синхронизации...', reply_markup=None)

    remnawave_service = RemnaWaveService()
    recommendations = await remnawave_service.get_sync_recommendations(db)

    priority_emoji = {'low': '🟢', 'medium': '🟡', 'high': '🔴'}

    text = f"""
💡 <b>Рекомендации по синхронизации</b>

{priority_emoji.get(recommendations['priority'], '🟢')} <b>Приоритет:</b> {recommendations['priority'].upper()}
⏱️ <b>Время выполнения:</b> {recommendations['estimated_time']}

<b>Рекомендуемое действие:</b>
"""

    if recommendations['sync_type'] == 'all':
        text += '🔄 Полная синхронизация'
    elif recommendations['sync_type'] == 'update_only':
        text += '📈 Обновление данных'
    elif recommendations['sync_type'] == 'new_only':
        text += '🆕 Синхронизация новых'
    else:
        text += '✅ Синхронизация не требуется'

    text += '\n\n<b>Причины:</b>\n'
    for reason in recommendations['reasons']:
        text += f'• {reason}\n'

    keyboard = []

    if recommendations['should_sync'] and recommendations['sync_type'] != 'none':
        keyboard.append(
            [
                types.InlineKeyboardButton(
                    text='✅ Выполнить рекомендацию',
                    callback_data=f'sync_{recommendations["sync_type"]}_users'
                    if recommendations['sync_type'] != 'update_only'
                    else 'sync_update_data',
                )
            ]
        )

    keyboard.extend(
        [
            [types.InlineKeyboardButton(text='🔄 Другие опции', callback_data='admin_rw_sync')],
            [types.InlineKeyboardButton(text='⬅️ Назад', callback_data='admin_remnawave')],
        ]
    )

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))
    await callback.answer()


@admin_required
@error_handler
async def validate_subscriptions(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    await callback.message.edit_text(
        '🔍 Выполняется валидация подписок...\n\nПроверяем данные, может занять несколько минут.', reply_markup=None
    )

    remnawave_service = RemnaWaveService()
    stats = await remnawave_service.validate_and_fix_subscriptions(db)

    if stats['errors'] == 0:
        status_emoji = '✅'
        status_text = 'успешно завершена'
    else:
        status_emoji = '⚠️'
        status_text = 'завершена с ошибками'

    text = f"""
{status_emoji} <b>Валидация {status_text}</b>

📊 <b>Результаты:</b>
• 🔍 Проверено подписок: {stats['checked']}
• 🔧 Исправлено подписок: {stats['fixed']}
• ⚠️ Найдено проблем: {stats['issues_found']}
• ❌ Ошибок: {stats['errors']}
"""

    if stats['fixed'] > 0:
        text += '\n✅ <b>Исправленные проблемы:</b>\n'
        text += '• Статусы просроченных подписок\n'
        text += '• Отсутствующие данные Remnawave\n'
        text += '• Некорректные лимиты трафика\n'
        text += '• Настройки устройств\n'

    if stats['errors'] > 0:
        text += '\n⚠️ Обнаружены ошибки при обработке.\nПроверьте логи для подробной информации.'

    keyboard = [
        [types.InlineKeyboardButton(text='🔄 Повторить валидацию', callback_data='sync_validate')],
        [types.InlineKeyboardButton(text='🔄 Полная синхронизация', callback_data='sync_all_users')],
        [types.InlineKeyboardButton(text='⬅️ К синхронизации', callback_data='admin_rw_sync')],
    ]

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))
    await callback.answer()


@admin_required
@error_handler
async def cleanup_subscriptions(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    await callback.message.edit_text(
        '🧹 Выполняется очистка неактуальных подписок...\n\nУдаляем подписки пользователей, отсутствующих в панели.',
        reply_markup=None,
    )

    remnawave_service = RemnaWaveService()
    stats = await remnawave_service.cleanup_orphaned_subscriptions(db)

    if stats['errors'] == 0:
        status_emoji = '✅'
        status_text = 'успешно завершена'
    else:
        status_emoji = '⚠️'
        status_text = 'завершена с ошибками'

    text = f"""
{status_emoji} <b>Очистка {status_text}</b>

📊 <b>Результаты:</b>
• 🔍 Проверено подписок: {stats['checked']}
• 🗑️ Деактивировано: {stats['deactivated']}
• ❌ Ошибок: {stats['errors']}
"""

    if stats['deactivated'] > 0:
        text += '\n🗑️ <b>Деактивированные подписки:</b>\n'
        text += 'Отключены подписки пользователей, которые\n'
        text += 'отсутствуют в панели Remnawave.\n'
    else:
        text += '\n✅ Все подписки актуальны!\nНеактуальных подписок не найдено.'

    if stats['errors'] > 0:
        text += '\n⚠️ Обнаружены ошибки при обработке.\nПроверьте логи для подробной информации.'

    keyboard = [
        [types.InlineKeyboardButton(text='🔄 Повторить очистку', callback_data='sync_cleanup')],
        [types.InlineKeyboardButton(text='🔍 Валидация', callback_data='sync_validate')],
        [types.InlineKeyboardButton(text='⬅️ К синхронизации', callback_data='admin_rw_sync')],
    ]

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))
    await callback.answer()


@admin_required
@error_handler
async def force_cleanup_all_orphaned_users(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    await callback.message.edit_text(
        '🗑️ Выполняется принудительная очистка всех пользователей, отсутствующих в панели...\n\n'
        '⚠️ ВНИМАНИЕ: Это полностью удалит ВСЕ данные пользователей!\n'
        '📊 Включая: транзакции, реферальные доходы, промокоды, серверы, балансы\n\n'
        '⏳ Пожалуйста, подождите...',
        reply_markup=None,
    )

    remnawave_service = RemnaWaveService()
    stats = await remnawave_service.cleanup_orphaned_subscriptions(db)

    if stats['errors'] == 0:
        status_emoji = '✅'
        status_text = 'успешно завершена'
    else:
        status_emoji = '⚠️'
        status_text = 'завершена с ошибками'

    text = f"""
{status_emoji} <b>Принудительная очистка {status_text}</b>

📊 <b>Результаты:</b>
• 🔍 Проверено подписок: {stats['checked']}
• 🗑️ Полностью очищено: {stats['deactivated']}
• ❌ Ошибок: {stats['errors']}
"""

    if stats['deactivated'] > 0:
        text += """

🗑️ <b>Полностью очищенные данные:</b>
• Подписки сброшены к начальному состоянию
• Удалены ВСЕ транзакции пользователей
• Удалены ВСЕ реферальные доходы
• Удалены использования промокодов
• Сброшены балансы к нулю
• Удалены подключенные серверы
• Сброшены HWID устройства в Remnawave
• Очищены Remnawave UUID
"""
    else:
        text += '\n✅ Неактуальных подписок не найдено!\nВсе пользователи синхронизированы с панелью.'

    if stats['errors'] > 0:
        text += '\n⚠️ Обнаружены ошибки при обработке.\nПроверьте логи для подробной информации.'

    keyboard = [
        [types.InlineKeyboardButton(text='🔄 Повторить очистку', callback_data='force_cleanup_orphaned')],
        [types.InlineKeyboardButton(text='🔄 Полная синхронизация', callback_data='sync_all_users')],
        [types.InlineKeyboardButton(text='⬅️ К синхронизации', callback_data='admin_rw_sync')],
    ]

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))
    await callback.answer()


@admin_required
@error_handler
async def confirm_force_cleanup(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    text = """
⚠️ <b>ВНИМАНИЕ! ОПАСНАЯ ОПЕРАЦИЯ!</b>

🗑️ <b>Принудительная очистка полностью удалит:</b>
• ВСЕ транзакции пользователей отсутствующих в панели
• ВСЕ реферальные доходы и связи
• ВСЕ использования промокодов
• ВСЕ подключенные серверы подписок
• ВСЕ балансы (сброс к нулю)
• ВСЕ HWID устройства в Remnawave
• ВСЕ Remnawave UUID и ссылки

⚡ <b>Это действие НЕОБРАТИМО!</b>

Используйте только если:
• Обычная синхронизация не помогает
• Нужно полностью очистить "мусорные" данные
• После массового удаления пользователей из панели

❓ <b>Вы действительно хотите продолжить?</b>
"""

    keyboard = [
        [types.InlineKeyboardButton(text='🗑️ ДА, ОЧИСТИТЬ ВСЕ', callback_data='force_cleanup_orphaned')],
        [types.InlineKeyboardButton(text='❌ Отмена', callback_data='admin_rw_sync')],
    ]

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))
    await callback.answer()


@admin_required
@error_handler
async def sync_users(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    sync_type = callback.data.split('_')[-2] + '_' + callback.data.split('_')[-1]

    progress_text = '🔄 Выполняется синхронизация...\n\n'

    if sync_type == 'all_users':
        progress_text += '📋 Тип: Полная синхронизация\n'
        progress_text += '• Создание новых пользователей\n'
        progress_text += '• Обновление существующих\n'
        progress_text += '• Удаление неактуальных подписок\n'
    elif sync_type == 'new_users':
        progress_text += '📋 Тип: Только новые пользователи\n'
        progress_text += '• Создание пользователей из панели\n'
    elif sync_type == 'update_data':
        progress_text += '📋 Тип: Обновление данных\n'
        progress_text += '• Обновление информации о трафике\n'
        progress_text += '• Синхронизация подписок\n'

    progress_text += '\n⏳ Пожалуйста, подождите...'

    await callback.message.edit_text(progress_text, reply_markup=None)

    remnawave_service = RemnaWaveService()

    sync_map = {'all_users': 'all', 'new_users': 'new_only', 'update_data': 'update_only'}

    stats = await remnawave_service.sync_users_from_panel(db, sync_map.get(sync_type, 'all'))

    total_operations = stats['created'] + stats['updated'] + stats.get('deleted', 0)
    stats['created'] + stats['updated'] + stats.get('deleted', 0)

    if stats['errors'] == 0:
        status_emoji = '✅'
        status_text = 'успешно завершена'
    elif stats['errors'] < total_operations:
        status_emoji = '⚠️'
        status_text = 'завершена с предупреждениями'
    else:
        status_emoji = '❌'
        status_text = 'завершена с ошибками'

    text = f"""
{status_emoji} <b>Синхронизация {status_text}</b>

📊 <b>Результат:</b>
"""

    if sync_type == 'all_users':
        text += f'• 🆕 Создано: {stats["created"]}\n'
        text += f'• 🔄 Обновлено: {stats["updated"]}\n'
        if 'deleted' in stats:
            text += f'• 🗑️ Удалено: {stats["deleted"]}\n'
        text += f'• ❌ Ошибок: {stats["errors"]}\n'
    elif sync_type == 'new_users':
        text += f'• 🆕 Создано: {stats["created"]}\n'
        text += f'• ❌ Ошибок: {stats["errors"]}\n'
        if stats['created'] == 0 and stats['errors'] == 0:
            text += '\n💡 Новых пользователей не найдено'
    elif sync_type == 'update_data':
        text += f'• 🔄 Обновлено: {stats["updated"]}\n'
        text += f'• ❌ Ошибок: {stats["errors"]}\n'
        if stats['updated'] == 0 and stats['errors'] == 0:
            text += '\n💡 Все данные актуальны'

    if stats['errors'] > 0:
        text += '\n⚠️ <b>Внимание:</b>\n'
        text += 'Некоторые операции завершились с ошибками.\n'
        text += 'Проверьте логи для получения подробной информации.'

    if sync_type == 'all_users' and 'deleted' in stats and stats['deleted'] > 0:
        text += '\n🗑️ <b>Удаленные подписки:</b>\n'
        text += 'Деактивированы подписки пользователей,\n'
        text += 'которые отсутствуют в панели Remnawave.'

    text += '\n\n💡 <b>Рекомендации:</b>\n'
    if sync_type == 'all_users':
        text += '• Полная синхронизация выполнена\n'
        text += '• Рекомендуется запускать раз в день\n'
    elif sync_type == 'new_users':
        text += '• Синхронизация новых пользователей\n'
        text += '• Используйте при массовом добавлении\n'
    elif sync_type == 'update_data':
        text += '• Обновление данных о трафике\n'
        text += '• Запускайте для актуализации статистики\n'

    keyboard = []

    if stats['errors'] > 0:
        keyboard.append([types.InlineKeyboardButton(text='🔄 Повторить синхронизацию', callback_data=callback.data)])

    if sync_type != 'all_users':
        keyboard.append([types.InlineKeyboardButton(text='🔄 Полная синхронизация', callback_data='sync_all_users')])

    keyboard.extend(
        [
            [
                types.InlineKeyboardButton(text='📊 Статистика системы', callback_data='admin_rw_system'),
                types.InlineKeyboardButton(text='🌐 Ноды', callback_data='admin_rw_nodes'),
            ],
            [types.InlineKeyboardButton(text='⬅️ Назад', callback_data='admin_remnawave')],
        ]
    )

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))
    await callback.answer()


@admin_required
@error_handler
async def show_squads_management(callback: types.CallbackQuery, db_user: User, db: AsyncSession):
    remnawave_service = RemnaWaveService()
    squads = await remnawave_service.get_all_squads()

    text = '🌍 <b>Управление сквадами</b>\n\n'
    keyboard = []

    if squads:
        for squad in squads:
            text += f'🔹 <b>{squad["name"]}</b>\n'
            text += f'👥 Участников: {squad["members_count"]}\n'
            text += f'📡 Инбаундов: {squad["inbounds_count"]}\n\n'

            keyboard.append(
                [
                    types.InlineKeyboardButton(
                        text=f'⚙️ {squad["name"]}', callback_data=f'admin_squad_manage_{squad["uuid"]}'
                    )
                ]
            )
    else:
        text += 'Сквады не найдены'

    keyboard.extend(
        [
            [types.InlineKeyboardButton(text='➕ Создать сквад', callback_data='admin_squad_create')],
            [types.InlineKeyboardButton(text='⬅️ Назад', callback_data='admin_remnawave')],
        ]
    )

    await callback.message.edit_text(text, reply_markup=types.InlineKeyboardMarkup(inline_keyboard=keyboard))
    await callback.answer()


def register_handlers(dp: Dispatcher):
    dp.callback_query.register(show_remnawave_menu, F.data == 'admin_remnawave')
    dp.callback_query.register(show_system_stats, F.data == 'admin_rw_system')
    dp.callback_query.register(show_traffic_stats, F.data == 'admin_rw_traffic')
    dp.callback_query.register(show_nodes_management, F.data == 'admin_rw_nodes')
    dp.callback_query.register(show_node_details, F.data.startswith('admin_node_manage_'))
    dp.callback_query.register(show_node_statistics, F.data.startswith('node_stats_'))
    dp.callback_query.register(manage_node, F.data.startswith('node_enable_'))
    dp.callback_query.register(manage_node, F.data.startswith('node_disable_'))
    dp.callback_query.register(manage_node, F.data.startswith('node_restart_'))
    dp.callback_query.register(restart_all_nodes, F.data == 'admin_restart_all_nodes')
    dp.callback_query.register(show_sync_options, F.data == 'admin_rw_sync')
    dp.callback_query.register(show_auto_sync_settings, F.data == 'admin_rw_auto_sync')
    dp.callback_query.register(toggle_auto_sync_setting, F.data == 'remnawave_auto_sync_toggle')
    dp.callback_query.register(prompt_auto_sync_schedule, F.data == 'remnawave_auto_sync_times')
    dp.callback_query.register(cancel_auto_sync_schedule, F.data == 'remnawave_auto_sync_cancel')
    dp.callback_query.register(run_auto_sync_now, F.data == 'remnawave_auto_sync_run')
    dp.callback_query.register(sync_all_users, F.data == 'sync_all_users')
    dp.callback_query.register(sync_users_to_panel, F.data == 'sync_to_panel')
    dp.callback_query.register(show_squad_migration_menu, F.data == 'admin_rw_migration')
    dp.callback_query.register(paginate_migration_source, F.data.startswith('admin_migration_source_page_'))
    dp.callback_query.register(handle_migration_source_selection, F.data.startswith('admin_migration_source_'))
    dp.callback_query.register(paginate_migration_target, F.data.startswith('admin_migration_target_page_'))
    dp.callback_query.register(handle_migration_target_selection, F.data.startswith('admin_migration_target_'))
    dp.callback_query.register(change_migration_target, F.data == 'admin_migration_change_target')
    dp.callback_query.register(confirm_squad_migration, F.data == 'admin_migration_confirm')
    dp.callback_query.register(cancel_squad_migration, F.data == 'admin_migration_cancel')
    dp.callback_query.register(handle_migration_page_info, F.data == 'admin_migration_page_info')
    dp.callback_query.register(show_squads_management, F.data == 'admin_rw_squads')
    dp.callback_query.register(show_squad_details, F.data.startswith('admin_squad_manage_'))
    dp.callback_query.register(manage_squad_action, F.data.startswith('squad_add_users_'))
    dp.callback_query.register(manage_squad_action, F.data.startswith('squad_remove_users_'))
    dp.callback_query.register(manage_squad_action, F.data.startswith('squad_delete_'))
    dp.callback_query.register(
        show_squad_edit_menu, F.data.startswith('squad_edit_') & ~F.data.startswith('squad_edit_inbounds_')
    )
    dp.callback_query.register(show_squad_inbounds_selection, F.data.startswith('squad_edit_inbounds_'))
    dp.callback_query.register(show_squad_rename_form, F.data.startswith('squad_rename_'))
    dp.callback_query.register(cancel_squad_rename, F.data.startswith('cancel_rename_'))
    dp.callback_query.register(toggle_squad_inbound, F.data.startswith('sqd_tgl_'))
    dp.callback_query.register(save_squad_inbounds, F.data.startswith('sqd_save_'))
    dp.callback_query.register(show_squad_edit_menu_short, F.data.startswith('sqd_edit_'))
    dp.callback_query.register(start_squad_creation, F.data == 'admin_squad_create')
    dp.callback_query.register(cancel_squad_creation, F.data == 'cancel_squad_create')
    dp.callback_query.register(toggle_create_inbound, F.data.startswith('create_tgl_'))
    dp.callback_query.register(finish_squad_creation, F.data == 'create_squad_finish')

    dp.message.register(process_squad_new_name, SquadRenameStates.waiting_for_new_name, F.text)

    dp.message.register(process_squad_name, SquadCreateStates.waiting_for_name, F.text)

    dp.message.register(
        save_auto_sync_schedule,
        RemnaWaveSyncStates.waiting_for_schedule,
        F.text,
    )
