import datetime
import logging
import asyncio
import aiosqlite
from aiogram import Bot, Dispatcher, types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
from aiogram.utils import executor
from config import TOKEN, CHANNEL_ID, REQUIRED_CHANNELS, ADMINS

# Настройка логирования
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Инициализация бота и диспетчера
bot = Bot(token=TOKEN)
storage = MemoryStorage()
dp = Dispatcher(bot, storage=storage)

# Глобальная переменная для базы данных
db = None
db_path = 'lobbies.db'

# Словарь разрешенных пользователей {telegram_id: "pro_gamer_nickname"}
# ALLOWED_USERS = {
#     1164013363: "Creativ",
#     # другие пользователи...
# }

# Тексты для режимов игры
mode_text = {
    'mode_free_for_all': 'Против всех (DM)',
    'mode_escalation': 'Эскалация'
}

# Тексты для карт
map_text = {
    'map_sandstone': 'Sandstone',
    'map_rust': 'Rust',
    'map_province': 'Province',
    'map_breeze': 'Breeze',
    'map_dune': 'Dune',
    'map_sakura': 'Sakura',
    'map_zone7': 'Zone 7'
}

# Тексты для времени
time_text = {
    'time_5': '5 минут',
    'time_10': '10 минут',
    'time_20': '20 минут',
    'time_60': '60 минут'
}

# Тексты для хитбоксов
hitbox_text = {
    'hitbox_onlyhead': 'Только в голову',
    'hitbox_all': 'По всему телу'
}


class LobbyCreation(StatesGroup):
    question_gamemode = State()
    question_map = State()
    question_time = State()
    question_hitbox = State()
    question_lobbyurl = State()
    question_custom_name = State()  # Новое состояние для названия режима
    confirmation = State()


# Инициализация и подготовка базы данных
async def init_db():
    global db, ALLOWED_USERS
    db = await aiosqlite.connect(db_path)
    await db.execute("PRAGMA journal_mode=WAL;")
    await db.execute("PRAGMA synchronous=NORMAL;")
    await db.execute('''
        CREATE TABLE IF NOT EXISTS lobbies (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id INTEGER NOT NULL,
            channel_id INTEGER NOT NULL,
            channel_message_id INTEGER NOT NULL,
            user_message_id INTEGER NOT NULL,
            date_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
    ''')
    await db.execute('''
        CREATE TABLE IF NOT EXISTS allowed_users (
            user_id INTEGER PRIMARY KEY,
            nickname TEXT NOT NULL
        )
    ''')
    await db.commit()

    # Загружаем ALLOWED_USERS из БД
    ALLOWED_USERS = {}
    async with db.execute('SELECT user_id, nickname FROM allowed_users') as cursor:
        async for row in cursor:
            ALLOWED_USERS[row[0]] = row[1]

    # Если таблица пустая, можно добавить дефолтного пользователя (опционально)
    if not ALLOWED_USERS:
        # Добавляем пользователя Creativ по умолчанию
        await db.execute('INSERT OR IGNORE INTO allowed_users (user_id, nickname) VALUES (?, ?)', (1164013363, "Creativ1111"))
        await db.commit()
        ALLOWED_USERS[1164013363] = "Creativ1111"


# Универсальная функция выполнения запросов с повтором при блокировке
async def execute_with_retry(query, params=(), fetchone=False):
    retries = 5
    delay = 0.05
    for attempt in range(retries):
        try:
            async with db.execute(query, params) as cursor:
                if fetchone:
                    res = await cursor.fetchone()
                else:
                    res = None
            await db.commit()
            return res
        except aiosqlite.OperationalError as e:
            if 'database is locked' in str(e):
                await asyncio.sleep(delay * (2 ** attempt))  # Экспоненциальная задержка
                continue
            else:
                raise
    raise RuntimeError("DB locked, max retries reached")


async def check_user_subscriptions(user_id, bot):
    not_subscribed = []
    for channel in REQUIRED_CHANNELS:
        try:
            member = await bot.get_chat_member(chat_id=channel, user_id=user_id)
            if member.status in ['left', 'kicked']:
                not_subscribed.append(channel)
        except Exception as e:
            logger.error(f"Error checking subscription for {channel}: {e}")
    return not_subscribed


async def check_access(user_id: int) -> bool:
    not_subscribed = await check_user_subscriptions(user_id, bot)
    if not_subscribed:
        channels_list = "\n".join([f"• {channel}" for channel in not_subscribed])
        await bot.send_message(
            user_id,
            f"<b>🔓 Для использования бота необходимо подписаться канал, где будут выкладываться лобби:</b>\n{channels_list}",
            parse_mode="HTML"
        )
        return False

    if user_id not in ALLOWED_USERS:
        await bot.send_message(
            user_id,
            "<b>🚫 Доступ запрещен.\n\n</b> Ваш ID не найден в списке разрешенных пользователей.\n\n"
            "🎮 <b>Если вы про-игрок и хотите получить доступ</b> — напишите:@gr1mach\n",
            parse_mode="HTML"
        )
        return False

    return True


async def send_gamemode_menu(message: types.Message, state: FSMContext):
    keyboard = InlineKeyboardMarkup(row_width=1)
    buttons = [
        InlineKeyboardButton(text="🧠 Эскалация", callback_data="mode_escalation"),
        InlineKeyboardButton(text="💥 Против всех (DM)", callback_data="mode_free_for_all")
    ]
    keyboard.add(*buttons)
    await message.answer(
        "<b>🤔Выбери</b>, как хочешь <b>страдать</b> на пути к совершенству.\n"
        "Но не волнуйся — боль <b>временная</b>, а <b>скилл навсегда</b>.\n\n"
        "<b>🧠 Эскалация</b> — это когда пачка уже стоит, и вы вдвоём <b>ретейкаете</b> сайт.\n"
        "Тут важно не только <b>попадать</b>, но и <b>общаться</b>, <b>синхрониться</b> и брать <b>ответственность</b> за позиции.\n\n"
        "<b>💥 Против всех (DM)</b> — <b>мясной</b> бой на всех.\n"
        "<b>Беги</b>, <b>стреляй</b>, <b>умирай</b>, <b>повтори</b>. И так до тех пор, пока не начнёшь попадать <b>первым</b>, а не вторым.",
        reply_markup=keyboard,
        parse_mode="HTML"
    )
    await LobbyCreation.question_gamemode.set()


@dp.message_handler(commands='start', state='*')
async def start_command(message: types.Message, state: FSMContext):
    if not await check_access(message.from_user.id):
        return
    await send_gamemode_menu(message, state)


async def send_map_menu(message: types.Message, state: FSMContext):
    keyboard = InlineKeyboardMarkup(row_width=3)
    buttons = [
        InlineKeyboardButton(text="Sandstone", callback_data="map_sandstone"),
        InlineKeyboardButton(text="Rust", callback_data="map_rust"),
        InlineKeyboardButton(text="Province", callback_data="map_province"),
        InlineKeyboardButton(text="Breeze", callback_data="map_breeze"),
        InlineKeyboardButton(text="Dune", callback_data="map_dune"),
        InlineKeyboardButton(text="Sakura", callback_data="map_sakura"),
        InlineKeyboardButton(text="Zone 7", callback_data="map_zone7")
        # InlineKeyboardButton(text="Вернуться в меню", callback_data="return_to_menu")
    ]
    keyboard.add(*buttons)

    keyboard.row(
        InlineKeyboardButton(text="Вернуться назад", callback_data="return_back")
    )
    await message.answer(
        "<b>🗺️ Карта?</b> Выбирай любую. Главное — не заблудись среди фрагов.",
        reply_markup=keyboard,
        parse_mode="HTML"
    )
    await LobbyCreation.question_map.set()


async def send_time_menu(message: types.Message, state: FSMContext):
    keyboard = InlineKeyboardMarkup(row_width=2)
    buttons = [
        InlineKeyboardButton(text="5 минут", callback_data="time_5"),
        InlineKeyboardButton(text="10 минут", callback_data="time_10"),
        InlineKeyboardButton(text="20 минут", callback_data="time_20"),
        InlineKeyboardButton(text="60 минут", callback_data="time_60")
        # InlineKeyboardButton(text="Вернуться назад", callback_data="return_back"),
        # InlineKeyboardButton(text="Вернуться в меню", callback_data="return_to_menu")
    ]
    keyboard.add(*buttons)

    keyboard.row(
        InlineKeyboardButton(text="Вернуться назад", callback_data="return_back")
    )
    keyboard.row(
        InlineKeyboardButton(text="Вернуться в меню", callback_data="return_to_menu")
    )

    await message.answer(
        "<b>🕗 Сколько потеем?</b> Только не ставь «вечно» — сервер может не выдержать.",
        reply_markup=keyboard,
        parse_mode="HTML"
    )
    await LobbyCreation.question_time.set()


async def send_hitbox_menu(message: types.Message, state: FSMContext):
    keyboard = InlineKeyboardMarkup(row_width=2)
    buttons = [
        InlineKeyboardButton(text="Только в голову", callback_data="hitbox_onlyhead"),
        InlineKeyboardButton(text="По всему телу", callback_data="hitbox_all")
        # InlineKeyboardButton(text="Вернуться назад", callback_data="return_back")
        # InlineKeyboardButton(text="Вернуться в меню", callback_data="return_to_menu")
    ]
    keyboard.add(*buttons)

    keyboard.row(
        InlineKeyboardButton(text="Вернуться назад", callback_data="return_back")
    )
    keyboard.row(
        InlineKeyboardButton(text="Вернуться в меню", callback_data="return_to_menu")
    )
    await message.answer(
        "<b>🎯 Куда целимся, боец?</b>\n\n"
        "• <b>Только в голову</b> — играем по-мужски: урон засчитывается только за хэдшоты.\n\n"
        "• <b>По всему телу</b> — без церемоний: засчитывается любой урон, даже если попал по тапкам.",
        reply_markup=keyboard,
        parse_mode="HTML"
    )
    await LobbyCreation.question_hitbox.set()


async def send_lobbyurl_request(message: types.Message, state: FSMContext):
    await message.answer(
        "<b>🔗 Закинь ссылку на лобби</b> — пусть все знают, где будет жарко.\n"
        "Тренировка любит компанию, особенно если есть кого перестреливать.",
        parse_mode="HTML"
    )
    await LobbyCreation.question_lobbyurl.set()


async def send_custom_name_request(message: types.Message, state: FSMContext):
    await message.answer(
        "<b>✏️ Напишите название режима:</b>\n"
        "<i>Например: ДМ рифл онли ХС</i>\n\n"
        "Это название будет отображаться в финальном сообщении.",
        parse_mode="HTML"
    )
    await LobbyCreation.question_custom_name.set()


@dp.callback_query_handler(lambda c: c.data in ['mode_free_for_all', 'mode_escalation'],
                           state=LobbyCreation.question_gamemode)
async def process_gamemode(callback_query: types.CallbackQuery, state: FSMContext):
    user_id = callback_query.from_user.id
    if not await check_access(user_id):
        await callback_query.answer()
        return

    await state.update_data(mode=mode_text[callback_query.data])
    await callback_query.answer()
    await send_map_menu(callback_query.message, state)


@dp.callback_query_handler(state=LobbyCreation.question_map)
async def process_map(callback_query: types.CallbackQuery, state: FSMContext):
    user_id = callback_query.from_user.id
    data = callback_query.data
    if not await check_access(user_id):
        await callback_query.answer()
        return

    if data == 'return_back':
        await LobbyCreation.question_gamemode.set()
        await send_gamemode_menu(callback_query.message, state)
        await callback_query.answer()
        return
    elif data == 'return_to_menu':
        await state.finish()
        await send_gamemode_menu(callback_query.message, state)
        await callback_query.answer()
        return

    if data.startswith('map_'):
        await state.update_data(map=map_text[data])
        await callback_query.answer()
        await send_time_menu(callback_query.message, state)
    else:
        await callback_query.answer()


@dp.callback_query_handler(state=LobbyCreation.question_time)
async def process_time(callback_query: types.CallbackQuery, state: FSMContext):
    user_id = callback_query.from_user.id
    data = callback_query.data
    if not await check_access(user_id):
        await callback_query.answer()
        return

    if data == 'return_back':
        await LobbyCreation.question_map.set()
        await send_map_menu(callback_query.message, state)
        await callback_query.answer()
        return
    elif data == 'return_to_menu':
        await state.finish()
        await send_gamemode_menu(callback_query.message, state)
        await callback_query.answer()
        return

    if data.startswith('time_'):
        await state.update_data(time=time_text[data])
        await callback_query.answer()
        await send_hitbox_menu(callback_query.message, state)
    else:
        await callback_query.answer()


@dp.callback_query_handler(state=LobbyCreation.question_hitbox)
async def process_hitbox(callback_query: types.CallbackQuery, state: FSMContext):
    user_id = callback_query.from_user.id
    data = callback_query.data
    if not await check_access(user_id):
        await callback_query.answer()
        return

    if data == 'return_back':
        await LobbyCreation.question_time.set()
        await send_time_menu(callback_query.message, state)
        await callback_query.answer()
        return
    elif data == 'return_to_menu':
        await state.finish()
        await send_gamemode_menu(callback_query.message, state)
        await callback_query.answer()
        return

    if data.startswith('hitbox_'):
        await state.update_data(hitbox=hitbox_text[data])
        await callback_query.answer()
        await send_lobbyurl_request(callback_query.message, state)
    else:
        await callback_query.answer()


@dp.message_handler(state=LobbyCreation.question_lobbyurl)
async def process_lobby_url(message: types.Message, state: FSMContext):
    user_id = message.from_user.id
    if not await check_access(user_id):
        return

    if not message.text.startswith('https://link.standoff2.com'):
        await message.answer(
            "<b>❌ Неверная ссылка на лобби.</b> Используйте ссылку формата https://link.standoff2.com/...",
            parse_mode="HTML"
        )
        return

    await state.update_data(lobby_url=message.text)
    await send_custom_name_request(message, state)


@dp.message_handler(state=LobbyCreation.question_custom_name)
async def process_custom_name(message: types.Message, state: FSMContext):
    user_id = message.from_user.id
    if not await check_access(user_id):
        return

    await state.update_data(custom_name=message.text)
    user_data = await state.get_data()
    keyboard = InlineKeyboardMarkup(row_width=2)
    buttons = [
        InlineKeyboardButton(text="Опубликовать", callback_data="confirm_publish"),
        InlineKeyboardButton(text="Вернуться в меню", callback_data="return_to_menu")
    ]
    keyboard.add(*buttons)
    await message.answer(
        f"<b>🔎 Финальная проверка перед запуском матча:</b>\n\n"
        f"<b>Название:</b> {user_data['custom_name']}\n"
        f"<b>Режим:</b> {user_data['mode']}\n"
        f"<b>Карта:</b> {user_data['map']}\n"
        f"<b>Время:</b> {user_data['time']}\n"
        f"<b>Урон:</b> {user_data['hitbox']}\n\n"
        f"Если всё <b>верно</b> — жми опубликовать. Если что-то <b>не так</b> — у тебя ещё есть шанс не облажаться 😎",
        reply_markup=keyboard,
        parse_mode="HTML"
    )
    await LobbyCreation.confirmation.set()


@dp.callback_query_handler(lambda c: c.data in ['confirm_publish'], state=LobbyCreation.confirmation)
async def process_publish(callback_query: types.CallbackQuery, state: FSMContext):
    user_id = callback_query.from_user.id
    if not await check_access(user_id):
        await callback_query.answer()
        return

    user_data = await state.get_data()
    nickname = ALLOWED_USERS.get(user_id, "Unknown")

    channel_text = (
        f"<b>📢 Лобби создано!</b>\n\n"
        f"🎮 <b>Режим</b> — {user_data['custom_name']} by {nickname}\n"
        f"🗺️ <b>Карта</b> — {user_data['map']}\n"
        f"🕖 <b>Время</b> — {user_data['time']}\n"
        f"☠️ <b>Урон</b> — {user_data['hitbox']}\n\n"
        f"<b>Готов?</b> Нажимай и врывайся."
    )

    keyboard = InlineKeyboardMarkup()
    keyboard.add(InlineKeyboardButton(
        text="Присоединиться",
        url=user_data['lobby_url']
    ))

    sent_channel_msg = await bot.send_message(
        chat_id=CHANNEL_ID,
        text=channel_text,
        reply_markup=keyboard,
        parse_mode="HTML"
    )

    delete_keyboard = InlineKeyboardMarkup()
    delete_keyboard.add(
        InlineKeyboardButton(
            text="Удалить лобби",
            callback_data=f"delete_lobby:{sent_channel_msg.message_id}"
        )
    )

    sent_user_msg = await callback_query.message.answer(
        f"<b>📡 Сигнал пошёл.</b> Сейчас налетят как чайки на {user_data['map']}",
        reply_markup=delete_keyboard,
        parse_mode="HTML"
    )

    # Сохраняем запись в БД с ретраями
    await execute_with_retry('''
        INSERT INTO lobbies (user_id, channel_id, channel_message_id, user_message_id)
        VALUES (?, ?, ?, ?)
    ''', (user_id, CHANNEL_ID, sent_channel_msg.message_id, sent_user_msg.message_id))

    await state.finish()
    await callback_query.answer()


previous_state_map = {
    LobbyCreation.question_map.state: LobbyCreation.question_gamemode,
    LobbyCreation.question_time.state: LobbyCreation.question_map,
    LobbyCreation.question_hitbox.state: LobbyCreation.question_time,
    LobbyCreation.question_lobbyurl.state: LobbyCreation.question_hitbox,
    LobbyCreation.question_custom_name.state: LobbyCreation.question_lobbyurl,
    LobbyCreation.confirmation.state: LobbyCreation.question_custom_name,
}


@dp.callback_query_handler(lambda c: c.data == 'return_back', state='*')
async def universal_go_back(callback_query: types.CallbackQuery, state: FSMContext):
    user_id = callback_query.from_user.id
    if not await check_access(user_id):
        await callback_query.answer()
        return

    current_state = await state.get_state()
    if current_state is None:
        await send_gamemode_menu(callback_query.message, state)
        await callback_query.answer()
        return

    prev_state = previous_state_map.get(current_state)
    if not prev_state:
        await send_gamemode_menu(callback_query.message, state)
        await callback_query.answer()
        return

    await prev_state.set()

    if prev_state == LobbyCreation.question_gamemode:
        await send_gamemode_menu(callback_query.message, state)
    elif prev_state == LobbyCreation.question_map:
        await send_map_menu(callback_query.message, state)
    elif prev_state == LobbyCreation.question_time:
        await send_time_menu(callback_query.message, state)
    elif prev_state == LobbyCreation.question_hitbox:
        await send_hitbox_menu(callback_query.message, state)
    elif prev_state == LobbyCreation.question_lobbyurl:
        await send_lobbyurl_request(callback_query.message, state)
    elif prev_state == LobbyCreation.question_custom_name:
        await send_custom_name_request(callback_query.message, state)

    await callback_query.answer()


@dp.callback_query_handler(lambda c: c.data == 'return_to_menu', state='*')
async def return_to_main_menu_handler(callback_query: types.CallbackQuery, state: FSMContext):
    user_id = callback_query.from_user.id
    if not await check_access(user_id):
        await callback_query.answer()
        return

    await state.finish()
    await send_gamemode_menu(callback_query.message, state)
    await callback_query.answer()


@dp.callback_query_handler(lambda c: c.data and c.data.startswith('delete_lobby:'))
async def delete_lobby_handler(callback_query: types.CallbackQuery):
    user_id = callback_query.from_user.id
    _, lobby_msg_id_str = callback_query.data.split(':', 1)
    try:
        lobby_msg_id = int(lobby_msg_id_str)
    except ValueError:
        await callback_query.answer("Некорректный идентификатор лобби.", show_alert=True)
        return

    row = await execute_with_retry('''
        SELECT id, user_message_id FROM lobbies
        WHERE user_id = ? AND channel_message_id = ? AND channel_id = ?
    ''', (user_id, lobby_msg_id, CHANNEL_ID), fetchone=True)

    if not row:
        await callback_query.answer("Лобби не найдено или вы не являетесь владельцем.", show_alert=True)
        return

    lobby_id, user_msg_id = row

    try:
        await bot.delete_message(chat_id=CHANNEL_ID, message_id=lobby_msg_id)
    except Exception as e:
        logger.error(f"Не удалось удалить сообщение лобби в канале: {e}")

    try:
        await bot.delete_message(chat_id=user_id, message_id=user_msg_id)
    except Exception as e:
        logger.error(f"Не удалось удалить сообщение с кнопкой у пользователя: {e}")

    await execute_with_retry('DELETE FROM lobbies WHERE id = ?', (lobby_id,))

    await callback_query.answer("Лобби успешно удалено.", show_alert=True)


async def reload_allowed_users():
    global ALLOWED_USERS
    ALLOWED_USERS = {}
    async with db.execute('SELECT user_id, nickname FROM allowed_users') as cursor:
        async for row in cursor:
            ALLOWED_USERS[row[0]] = row[1]
    logger.info(f"ALLOWED_USERS reloaded: {ALLOWED_USERS}")

# Добавляем проверку админа
def is_admin(user_id: int) -> bool:
    return user_id in ADMINS

@dp.message_handler(commands=['adduser'], state='*')
async def cmd_add_user(message: types.Message):
    user_id = message.from_user.id
    if not is_admin(user_id):
        await message.reply("❌ У вас нет прав для выполнения этой команды.")
        return

    parts = message.text.strip().split(maxsplit=2)
    if len(parts) < 3:
        await message.reply("Использование:\n/adduser <user_id> <nickname>")
        return

    try:
        new_user_id = int(parts[1])
    except ValueError:
        await message.reply("❌ user_id должен быть числом.")
        return

    nickname = parts[2]

    # Добавляем или обновляем пользователя в БД
    await execute_with_retry(
        "INSERT OR REPLACE INTO allowed_users (user_id, nickname) VALUES (?, ?)",
        (new_user_id, nickname)
    )
    await reload_allowed_users()

    await message.reply(f"✅ Пользователь {nickname} (ID: {new_user_id}) добавлен/обновлен в списке разрешённых.")


@dp.message_handler(commands=['deluser'], state='*')
async def cmd_del_user(message: types.Message):
    user_id = message.from_user.id
    if not is_admin(user_id):
        await message.reply("❌ У вас нет прав для выполнения этой команды.")
        return

    parts = message.text.strip().split(maxsplit=1)
    if len(parts) != 2:
        await message.reply("Использование:\n/deluser <user_id>")
        return

    try:
        del_user_id = int(parts[1])
    except ValueError:
        await message.reply("❌ user_id должен быть числом.")
        return

    await execute_with_retry("DELETE FROM allowed_users WHERE user_id = ?", (del_user_id,))
    await reload_allowed_users()

    await message.reply(f"✅ Пользователь с ID {del_user_id} удалён из списка разрешённых.")


@dp.message_handler(commands=['listusers'], state='*')
async def cmd_list_users(message: types.Message):
    user_id = message.from_user.id
    if not is_admin(user_id):
        await message.reply("❌ У вас нет прав для выполнения этой команды.")
        return

    if not ALLOWED_USERS:
        await message.reply("Список разрешённых пользователей пуст.")
        return

    text = "<b>Список разрешённых пользователей:</b>\n"
    for uid, nick in ALLOWED_USERS.items():
        text += f"• {nick} (ID: {uid})\n"
    await message.reply(text, parse_mode="HTML")

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(init_db())
    executor.start_polling(dp, skip_updates=True)


Добавь в этот код невероятно много и очень подробные логи. Для всех случаев.