from aiogram import Bot, Dispatcher, types, F
from aiogram.filters import Command, StateFilter
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from aiogram.fsm.storage.memory import MemoryStorage
import asyncio
import json
import datetime
import re
import os
import fcntl
import aiosqlite
import logging

# Настройки
API_TOKEN = '8111944723:AAEyCRmUEfo7sAmS5ytc804ycC2kXi0Dibk'
CHANNEL_USERNAME = '@donballon_news'
cnl_id = -1001383416605
DATABASE_PATH = 'votes.db'

# Настройка логирования
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        # logging.FileHandler('/root/telegram_bot/server_bot.log', encoding='utf-8'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

# Инициализация бота
storage = MemoryStorage()
bot = Bot(token=API_TOKEN)
dp = Dispatcher(storage=storage)

# Глобальная переменная для хранения задач обновления
update_tasks = {}


class VoteStates(StatesGroup):
    waiting_for_photo = State()
    waiting_for_variable = State()
    waiting_for_winners_count = State()
    waiting_for_data = State()


async def acquire_db_lock():
    """Блокирует базу данных при работающем боте"""
    try:
        lock_file = f"{DATABASE_PATH}.lock"
        lock_fd = os.open(lock_file, os.O_CREAT | os.O_RDWR)

        try:
            fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
            logger.info("База данных заблокирована серверным ботом")
            return lock_fd
        except IOError:
            os.close(lock_fd)
            logger.warning("Не удалось заблокировать БД - уже заблокирована другим процессом")
            return None
    except Exception as e:
        logger.error(f"Ошибка блокировки БД: {e}")
        return None


async def release_db_lock(lock_fd):
    """Разблокирует базу данных"""
    try:
        if lock_fd:
            fcntl.flock(lock_fd, fcntl.LOCK_UN)
            os.close(lock_fd)

            lock_file = f"{DATABASE_PATH}.lock"
            if os.path.exists(lock_file):
                os.unlink(lock_file)

        logger.info("База данных разблокирована")
    except Exception as e:
        logger.error(f"Ошибка разблокировки БД: {e}")


async def init_database():
    """Инициализация базы данных с проверкой существующих колонок"""
    try:
        async with aiosqlite.connect(DATABASE_PATH) as db:
            await db.execute('''
                             CREATE TABLE IF NOT EXISTS active_votes
                             (
                                 id
                                 INTEGER
                                 PRIMARY
                                 KEY
                                 AUTOINCREMENT,
                                 created_at
                                 TEXT
                                 NOT
                                 NULL,
                                 variables
                                 TEXT
                                 NOT
                                 NULL,
                                 file_ids_with_types
                                 TEXT,
                                 end_time
                                 TEXT,
                                 channel_message_id
                                 INTEGER,
                                 finish_button_message_id
                                 INTEGER,
                                 is_active
                                 INTEGER
                                 DEFAULT
                                 1,
                                 winners_count
                                 INTEGER
                                 DEFAULT
                                 1
                             )
                             ''')

            await db.execute('''
                             CREATE TABLE IF NOT EXISTS user_votes
                             (
                                 id
                                 INTEGER
                                 PRIMARY
                                 KEY
                                 AUTOINCREMENT,
                                 user_id
                                 INTEGER
                                 NOT
                                 NULL,
                                 vote_id
                                 INTEGER
                                 NOT
                                 NULL,
                                 option_name
                                 TEXT
                                 NOT
                                 NULL,
                                 voted_at
                                 TEXT
                                 NOT
                                 NULL,
                                 UNIQUE
                             (
                                 user_id,
                                 vote_id
                             )
                                 )
                             ''')

            await db.execute('''
                             CREATE TABLE IF NOT EXISTS subscribers
                             (
                                 id
                                 INTEGER
                                 PRIMARY
                                 KEY
                                 AUTOINCREMENT,
                                 user_id
                                 INTEGER
                                 UNIQUE
                                 NOT
                                 NULL,
                                 subscribed_at
                                 TEXT
                                 NOT
                                 NULL
                             )
                             ''')

            await db.execute('''
                             CREATE TABLE IF NOT EXISTS pending_subscribers
                             (
                                 id
                                 INTEGER
                                 PRIMARY
                                 KEY
                                 AUTOINCREMENT,
                                 user_id
                                 INTEGER
                                 UNIQUE
                                 NOT
                                 NULL,
                                 created_at
                                 TEXT
                                 NOT
                                 NULL,
                                 message_ids
                                 TEXT,
                                 vote_ids
                                 TEXT
                             )
                             ''')

            await add_missing_columns(db)
            await db.commit()

        logger.info("База данных серверного бота инициализирована")
    except Exception as e:
        logger.error(f"Ошибка инициализации БД серверного бота: {e}")
        raise


async def add_missing_columns(db):
    """Добавляет отсутствующие колонки в таблицы"""
    try:
        cursor = await db.execute("PRAGMA table_info(active_votes)")
        columns = await cursor.fetchall()
        existing_columns = [column[1] for column in columns]

        required_columns = [
            'finish_button_message_id',
            'is_active',
            'winners_count'
        ]

        for column in required_columns:
            if column not in existing_columns:
                if column == 'finish_button_message_id':
                    await db.execute(f'ALTER TABLE active_votes ADD COLUMN {column} INTEGER')
                    logger.info(f"Добавлена колонка {column} в таблицу active_votes")
                elif column == 'is_active':
                    await db.execute(f'ALTER TABLE active_votes ADD COLUMN {column} INTEGER DEFAULT 1')
                    logger.info(f"Добавлена колонка {column} в таблицу active_votes")
                elif column == 'winners_count':
                    await db.execute(f'ALTER TABLE active_votes ADD COLUMN {column} INTEGER DEFAULT 1')
                    logger.info(f"Добавлена колонка {column} в таблицу active_votes")

    except Exception as e:
        logger.error(f"Ошибка при добавлении колонок: {e}")


async def is_subscribed(user_id, channel):
    """Проверяет, является ли пользователь администратором канала"""
    try:
        member = await bot.get_chat_member(chat_id=channel, user_id=user_id)
        if member.status in ['administrator', 'creator', 'owner']:
            return True
        return False
    except Exception as e:
        logger.error(f"Ошибка проверки прав администратора для пользователя {user_id}: {e}")
        return False


async def check_admin_access(message: types.Message) -> bool:
    """Проверяет доступ администратора и отправляет сообщение если нет прав"""
    is_admin = await is_subscribed(message.from_user.id, CHANNEL_USERNAME)
    if not is_admin:
        await message.answer(
            "У вас нет прав для создания голосований. Только администраторы канала могут создавать голосования.")
        logger.warning(f"Пользователь {message.from_user.id} попытался создать голосование без прав администратора")
    return is_admin


async def cache_media_group_to_state(group_id: str, message: types.Message, state: FSMContext,
                                     wait_time: float = 1.5) -> bool:
    """Кэширует медиагруппу в состоянии"""
    data = await state.get_data()
    if 'media_group_cache' not in data:
        data['media_group_cache'] = {}
    if group_id not in data['media_group_cache']:
        data['media_group_cache'][group_id] = {
            'messages': [message],
            'processed': False
        }
    else:
        data['media_group_cache'][group_id]['messages'].append(message)
    await state.update_data(media_group_cache=data['media_group_cache'])
    if len(data['media_group_cache'][group_id]['messages']) == 1:
        await asyncio.sleep(wait_time)
        return await process_media_group(group_id, state)
    return False


async def process_media_group(group_id: str, state: FSMContext) -> bool:
    """Обрабатывает медиагруппу"""
    data = await state.get_data()
    if group_id not in data.get('media_group_cache', {}):
        return False
    group_data = data['media_group_cache'][group_id]
    if group_data['processed']:
        return False
    group_data['processed'] = True
    await state.update_data(media_group_cache=data['media_group_cache'])
    try:
        messages = group_data['messages']
        messages.sort(key=lambda x: x.message_id)

        media_items = []

        for msg in messages:
            if msg.photo:
                media_item = {
                    'file_id': msg.photo[-1].file_id,
                    'file_unique_id': msg.photo[-1].file_unique_id,
                    'width': msg.photo[-1].width,
                    'height': msg.photo[-1].height,
                    'file_size': msg.photo[-1].file_size,
                    'message_id': msg.message_id,
                    'type': 'photo'
                }
                media_items.append(media_item)

            elif msg.video:
                media_item = {
                    'file_id': msg.video.file_id,
                    'file_unique_id': msg.video.file_unique_id,
                    'width': msg.video.width,
                    'height': msg.video.height,
                    'file_size': msg.video.file_size,
                    'duration': msg.video.duration,
                    'message_id': msg.message_id,
                    'type': 'video'
                }
                media_items.append(media_item)

        photos_data = [item for item in media_items if item['type'] == 'photo']
        videos_data = [item for item in media_items if item['type'] == 'video']

        await state.update_data({
            'cached_photos': photos_data,
            'cached_videos': videos_data,
            'cached_media_items': media_items,
            'cached_photos_count': len(photos_data),
            'cached_videos_count': len(videos_data),
            'cached_total_messages': len(messages),
            'cached_media_group_id': group_id,
            'media_group_processed': True
        })
        data = await state.get_data()
        if 'media_group_cache' in data and group_id in data['media_group_cache']:
            del data['media_group_cache'][group_id]
            await state.update_data(media_group_cache=data['media_group_cache'])

        logger.info(f"Обработана медиагруппа {group_id} с {len(messages)} медиафайлами")
        return True
    except Exception as e:
        logger.error(f"Ошибка обработки медиагруппы {group_id}: {e}")
        data = await state.get_data()
        if 'media_group_cache' in data and group_id in data['media_group_cache']:
            del data['media_group_cache'][group_id]
            await state.update_data(media_group_cache=data['media_group_cache'])
        return False


async def create_media_group_from_state(state: FSMContext) -> list:
    """Создает медиагруппу из состояния"""
    data = await state.get_data()
    if 'cached_media_items' in data:
        media_group = []
        for media_item in data['cached_media_items']:
            if media_item['type'] == 'photo':
                media_group.append(types.InputMediaPhoto(media=media_item['file_id']))
            elif media_item['type'] == 'video':
                media_group.append(types.InputMediaVideo(media=media_item['file_id']))
        return media_group
    else:
        photos_data = data.get('cached_photos', [])
        videos_data = data.get('cached_videos', [])
        media_group = []
        for photo_data in photos_data:
            media_group.append(types.InputMediaPhoto(media=photo_data['file_id']))
        for video_data in videos_data:
            media_group.append(types.InputMediaVideo(media=video_data['file_id']))
        return media_group


def extract_username_from_url(url: str) -> str:
    """Извлекает username из URL"""
    url = url.split('?')[0]
    url = url.rstrip('/')
    if '/' in url:
        username = url.split('/')[-1]
    else:
        username = url
    if username.startswith('@'):
        username = username[1:]
    return username


def parse_variables_with_links(text: str):
    """Парсит варианты ответов с ссылками"""
    variables = []
    pattern = r"(https?://[^\s;]+(?:;[^\s;]+)*)|([^;]+)"
    matches = re.findall(pattern, text)
    parts = []
    current = ""
    for url_part, text_part in matches:
        token = url_part or text_part
        if token.strip():
            parts.append(token.strip())

    for item in parts:
        if not item:
            continue
        subparts = item.strip().split()
        if len(subparts) >= 2:
            name = ' '.join(subparts[:-1])
            potential_link = subparts[-1]
            if (potential_link.startswith('http') or
                    potential_link.startswith('@') or
                    '.' in potential_link or
                    '/' in potential_link):
                username = extract_username_from_url(potential_link)
                variables.append({
                    'name': name,
                    'link': potential_link,
                    'username': username
                })
            else:
                variables.append({
                    'name': item,
                    'link': '',
                    'username': ''
                })
        else:
            variables.append({
                'name': item,
                'link': '',
                'username': ''
            })
    return variables


def format_variables_for_display(variables):
    """Форматирует варианты для отображения"""
    str_variables = '📋 Варианты ответа:\n\n'
    for i, var in enumerate(variables, 1):
        if var['link']:
            short_link = var['link']
            if len(short_link) > 30:
                short_link = short_link[:27] + '...'
            str_variables += f"{i}. {var['name']}\n   {short_link}\n"
        else:
            str_variables += f"{i}. {var['name']}\n"
    return str_variables


def format_variables_for_results(variables, votes_data=None, is_finished=False, winners=None, winners_count=1):
    """Форматирует варианты для отображения результатов"""
    if is_finished:
        if winners:
            if len(winners) == 1:
                str_var = f"<b>Голосование завершено! Победитель: {winners[0]}! </b>\n\n"
            else:
                winners_str = ", ".join(winners)
                str_var = f"<b>Голосование завершено! Победители: {winners_str}! </b>\n\n"
        else:
            str_var = "<b> Голосование завершено! Ничья! </b>\n\n"
    else:
        str_var = "<b>Результаты:</b>\n\n"

    for i, var in enumerate(variables, 1):
        vote_count = votes_data.get(var['name'], 0) if votes_data else 0
        if var['link']:
            # Сначала название и голоса, потом ссылка на той же строке или компактно
            str_var += f"🎈 {var['name']} - {vote_count} голосов\n"
            str_var += f'<a href="{var["link"]}">{var["username"]}</a>\n'
        else:
            str_var += f"🎈 {var['name']} - {vote_count} голосов\n"
    return str_var


def get_winners(variables, votes_data, winners_count=1):
    """Определяет победителей голосования"""
    if not votes_data:
        return []

    # Сортируем варианты по количеству голосов
    sorted_votes = sorted(votes_data.items(), key=lambda x: x[1], reverse=True)

    if not sorted_votes:
        return []

    # Находим пороговое значение голосов для winners_count лучших
    if len(sorted_votes) <= winners_count:
        return [var[0] for var in sorted_votes]

    threshold_votes = sorted_votes[winners_count - 1][1]
    winners = []

    for var_name, vote_count in sorted_votes:
        if vote_count >= threshold_votes and len(winners) < winners_count:
            winners.append(var_name)
        elif vote_count < threshold_votes:
            break

    return winners


async def get_votes_data_by_id(vote_id):
    """Получает данные голосов из базы данных по ID голосования"""
    try:
        async with aiosqlite.connect(DATABASE_PATH) as db:
            cursor = await db.execute('''
                                      SELECT option_name, COUNT(*) as vote_count
                                      FROM user_votes
                                      WHERE vote_id = ?
                                      GROUP BY option_name
                                      ''', (vote_id,))
            votes_results = await cursor.fetchall()

            votes_data = {}
            for option_name, vote_count in votes_results:
                votes_data[option_name] = vote_count

            return votes_data
    except Exception as e:
        logger.error(f"Ошибка получения данных голосов для голосования {vote_id}: {e}")
        return {}


async def finish_vote_by_id(vote_id):
    """Завершает конкретное голосование по ID и отправляет результаты"""
    try:
        async with aiosqlite.connect(DATABASE_PATH) as db:
            cursor = await db.execute(
                'SELECT variables, channel_message_id, winners_count FROM active_votes WHERE id = ? AND is_active = 1',
                (vote_id,)
            )
            result = await cursor.fetchone()

            if not result:
                logger.warning(f"Попытка завершить несуществующее голосование {vote_id}")
                return False

            variables_json, channel_message_id, winners_count = result
            variables = json.loads(variables_json)

        votes_data = await get_votes_data_by_id(vote_id)
        winners = get_winners(variables, votes_data, winners_count)

        # Обновляем сообщение в канале вместо отправки нового
        if channel_message_id:
            try:
                str_var = format_variables_for_results(variables, votes_data, is_finished=True, winners=winners,
                                                       winners_count=winners_count)
                await bot.edit_message_text(
                    chat_id=cnl_id,
                    message_id=channel_message_id,
                    text=str_var,
                    parse_mode="HTML"
                )
                logger.info(
                    f"Сообщение с результатами обновлено в канале (message_id: {channel_message_id}) для голосования {vote_id}")
            except Exception as e:
                logger.error(f"Ошибка обновления сообщения в канале для голосования {vote_id}: {e}")

        # Деактивируем голосование в базе данных
        async with aiosqlite.connect(DATABASE_PATH) as db:
            await db.execute('UPDATE active_votes SET is_active = 0 WHERE id = ?', (vote_id,))
            await db.commit()

        # Останавливаем задачу обновления для этого голосования
        if vote_id in update_tasks:
            update_tasks[vote_id].cancel()
            del update_tasks[vote_id]
            logger.info(f"Задача обновления для голосования {vote_id} остановлена")

        logger.info(f"Голосование {vote_id} успешно завершено")
        return True
    except Exception as e:
        logger.error(f"Ошибка завершения голосования {vote_id}: {e}")
        return False


async def update_channel_message_periodically(vote_id):
    """Обновляет сообщение в канале каждые 3 минуты с проверкой времени окончания для конкретного голосования"""
    logger.info(f"Запущена задача периодического обновления сообщений для голосования {vote_id}")

    while True:
        try:
            await asyncio.sleep(180)  # 3 минуты

            async with aiosqlite.connect(DATABASE_PATH) as db:
                cursor = await db.execute('SELECT id FROM active_votes WHERE id = ? AND is_active = 1', (vote_id,))
                result = await cursor.fetchone()
                if not result:
                    logger.info(f"Голосование {vote_id} не найдено или не активно, завершаем задачу")
                    if vote_id in update_tasks:
                        del update_tasks[vote_id]
                    return

                cursor = await db.execute(
                    'SELECT variables, end_time, channel_message_id, winners_count FROM active_votes WHERE id = ? AND is_active = 1',
                    (vote_id,)
                )
                result = await cursor.fetchone()
                if not result:
                    if vote_id in update_tasks:
                        del update_tasks[vote_id]
                    return

                variables_json, end_time, channel_message_id, winners_count = result
                variables = json.loads(variables_json)

            votes_data = await get_votes_data_by_id(vote_id)

            if end_time:
                try:
                    end_dt = datetime.datetime.strptime(end_time, "%d.%m.%Y %H:%M")
                    current_time = datetime.datetime.now()

                    if current_time >= end_dt:
                        logger.info(f"Время голосования {vote_id} истекло, завершаем...")
                        await finish_vote_by_id(vote_id)
                        return

                except Exception as e:
                    logger.error(f"Ошибка проверки времени голосования {vote_id}: {e}")

            str_var = format_variables_for_results(variables, votes_data, winners_count=winners_count)
            keyboard = types.InlineKeyboardMarkup(
                inline_keyboard=[
                    [types.InlineKeyboardButton(text="Проголосовать",
                                                url=f"https://t.me/DonBallon_vote_bot?start=vote_{vote_id}")],
                ]
            )

            try:
                await bot.edit_message_text(
                    chat_id=cnl_id,
                    message_id=channel_message_id,
                    text=str_var,
                    reply_markup=keyboard,
                    parse_mode="HTML"
                )
                logger.debug(
                    f"Сообщение в канале обновлено для голосования {vote_id}. Голосов: {sum(votes_data.values())}")
            except Exception as e:
                logger.error(f"Ошибка обновления сообщения в канале для голосования {vote_id}: {e}")

        except Exception as e:
            logger.error(f"Ошибка в периодическом обновлении для голосования {vote_id}: {e}")
            await asyncio.sleep(60)


async def restore_update_tasks():
    """Восстанавливает задачи обновления при перезапуске бота для всех активных голосований"""
    try:
        async with aiosqlite.connect(DATABASE_PATH) as db:
            cursor = await db.execute('SELECT id FROM active_votes WHERE is_active = 1')
            results = await cursor.fetchall()

            for result in results:
                vote_id = result[0]
                logger.info(f"Восстановление задачи обновления для активного голосования {vote_id}")

                # Отменяем существующую задачу если есть
                if vote_id in update_tasks and not update_tasks[vote_id].done():
                    update_tasks[vote_id].cancel()

                # Создаем новую задачу
                update_tasks[vote_id] = asyncio.create_task(update_channel_message_periodically(vote_id))

            logger.info(f"Восстановлено {len(results)} задач обновления")
            return len(results) > 0
    except Exception as e:
        logger.error(f"Ошибка восстановления задач обновления: {e}")
        return False


async def has_active_votes() -> bool:
    """Проверяет, есть ли активные голосования"""
    try:
        async with aiosqlite.connect(DATABASE_PATH) as db:
            cursor = await db.execute("SELECT 1 FROM active_votes WHERE is_active = 1 LIMIT 1")
            result = await cursor.fetchone()
            return bool(result)
    except Exception as e:
        logger.error(f"Ошибка проверки активных голосований: {e}")
        return False


@dp.callback_query(F.data.startswith("force_finish_"))
async def handle_force_finish(callback: types.CallbackQuery):
    """Обработчик кнопки принудительного завершения"""
    if not await check_admin_access(callback.message):
        await callback.answer("У вас нет прав для завершения голосования", show_alert=True)
        return

    try:
        # Извлекаем vote_id из callback_data
        vote_id = int(callback.data.split('_')[2])

        async with aiosqlite.connect(DATABASE_PATH) as db:
            cursor = await db.execute(
                'SELECT finish_button_message_id FROM active_votes WHERE id = ? AND is_active = 1',
                (vote_id,)
            )
            result = await cursor.fetchone()

            if not result or result[0] != callback.message.message_id:
                await callback.answer("Эта кнопка больше не активна", show_alert=True)
                return

        success = await finish_vote_by_id(vote_id)

        if success:
            await callback.answer("Голосование завершено!", show_alert=True)
            logger.info(f"Голосование {vote_id} принудительно завершено пользователем {callback.from_user.id}")
        else:
            await callback.answer("Ошибка завершения голосования", show_alert=True)

    except (ValueError, IndexError):
        await callback.answer("Ошибка обработки запроса", show_alert=True)
    except Exception as e:
        logger.error(f"Ошибка принудительного завершения голосования: {e}")
        await callback.answer("Ошибка завершения голосования", show_alert=True)


@dp.message(Command('start'))
async def cmd_start(message: types.Message, state: FSMContext):
    """Обработчик команды /start"""
    await state.clear()
    await message.answer("Здравствуйте! Вас приветствует бот голосований DonBallon")

    if not await check_admin_access(message):
        return

    await message.answer(
        "Вы можете создать голосование! Создайте превью голосования в канале, после чего перешлите его в этот чат. Далее напишите варианты ответов и выберите время окончания")
    await state.set_state(VoteStates.waiting_for_photo)
    logger.info(f"Пользователь {message.from_user.id} начал создание голосования")


@dp.message(F.media_group_id, StateFilter("*"))
async def handle_album_reset_state(message: types.Message, state: FSMContext):
    """Если в любом состоянии отправлена медиагруппа — сбрасываем состояние и начинаем новое голосование."""
    # Сбрасываем состояние полностью
    await state.clear()

    if not await check_admin_access(message):
        return

    # Кэшируем медиагруппу
    success = await cache_media_group_to_state(
        group_id=message.media_group_id,
        message=message,
        state=state
    )

    if success:
        await message.answer(
            "Напишите варианты ответа с ссылками (например 'работа Ольги https://t.me/example;работа Вики https://www.instagram.com/example;работа Оксаны')"
        )
        await state.set_state(VoteStates.waiting_for_variable)


@dp.message(VoteStates.waiting_for_photo)
async def handle_album(message: types.Message, state: FSMContext):
    """Обработка медиаальбома и одиночных медиа"""
    if not await check_admin_access(message):
        await state.clear()
        return

    if message.media_group_id:
        data = await state.get_data()
        if data.get('media_group_processed'):
            return

        success = await cache_media_group_to_state(
            group_id=message.media_group_id,
            message=message,
            state=state
        )
        if success:
            await message.answer(
                "Напишите варианты ответа с ссылками (например 'работа Ольги https://t.me/example;работа Вики https://www.instagram.com/example;работа Оксаны')")
            await state.set_state(VoteStates.waiting_for_variable)
    elif message.photo or message.video:
        # Обработка одиночного медиа
        media_items = []

        if message.photo:
            media_item = {
                'file_id': message.photo[-1].file_id,
                'file_unique_id': message.photo[-1].file_unique_id,
                'width': message.photo[-1].width,
                'height': message.photo[-1].height,
                'file_size': message.photo[-1].file_size,
                'message_id': message.message_id,
                'type': 'photo'
            }
            media_items.append(media_item)
        elif message.video:
            media_item = {
                'file_id': message.video.file_id,
                'file_unique_id': message.video.file_unique_id,
                'width': message.video.width,
                'height': message.video.height,
                'file_size': message.video.file_size,
                'duration': message.video.duration,
                'message_id': message.message_id,
                'type': 'video'
            }
            media_items.append(media_item)

        await state.update_data({
            'cached_media_items': media_items,
            'cached_photos': [item for item in media_items if item['type'] == 'photo'],
            'cached_videos': [item for item in media_items if item['type'] == 'video'],
            'media_group_processed': True
        })

        await message.answer(
            "Напишите варианты ответа с ссылками (например 'работа Ольги https://t.me/example;работа Вики https://www.instagram.com/example;работа Оксаны')")
        await state.set_state(VoteStates.waiting_for_variable)
    else:
        await message.answer("Бот не смог получить медиафайл. Попробуйте еще раз")


@dp.message(VoteStates.waiting_for_variable)
async def handle_variable(message: types.Message, state: FSMContext):
    """Обработка вариантов ответа"""
    if not await check_admin_access(message):
        await state.clear()
        return

    try:
        variables = parse_variables_with_links(message.text)
        if not variables:
            await message.answer("Варианты ответа не могут быть пустыми. Попробуйте еще раз")
            return

        media_group = await create_media_group_from_state(state)
        if media_group:
            await bot.send_media_group(chat_id=message.chat.id, media=media_group)

        str_variables = format_variables_for_display(variables)
        await message.answer(str_variables)
        data = await state.get_data()

        file_ids_with_types = []

        if 'cached_media_items' in data:
            for media_item in data['cached_media_items']:
                file_ids_with_types.append({
                    'file_id': media_item['file_id'],
                    'type': media_item['type']
                })
        else:
            photos_data = data.get('cached_photos', [])
            videos_data = data.get('cached_videos', [])
            for photo in photos_data:
                file_ids_with_types.append({
                    'file_id': photo['file_id'],
                    'type': 'photo'
                })
            for video in videos_data:
                file_ids_with_types.append({
                    'file_id': video['file_id'],
                    'type': 'video'
                })

        await state.update_data(
            variables=variables,
            file_ids_with_types=file_ids_with_types,
            cached_photos=data.get('cached_photos', []),
            cached_videos=data.get('cached_videos', [])
        )

        await message.answer("Сколько победителей должно быть в голосовании? (введите число)")
        await state.set_state(VoteStates.waiting_for_winners_count)
        logger.info(f"Пользователь {message.from_user.id} добавил {len(variables)} вариантов ответа")

    except Exception as e:
        logger.error(f"Ошибка обработки вариантов ответа: {e}")
        await message.answer(f"Ошибка: {e}. Попробуйте еще раз")


@dp.message(VoteStates.waiting_for_winners_count)
async def handle_winners_count(message: types.Message, state: FSMContext):
    """Обработка количества победителей"""
    if not await check_admin_access(message):
        await state.clear()
        return

    try:
        winners_count = int(message.text)
        if winners_count < 1:
            await message.answer("Количество победителей должно быть не менее 1. Попробуйте еще раз")
            return

        data = await state.get_data()
        variables = data.get('variables', [])

        if winners_count > len(variables):
            await message.answer(
                f"Количество победителей не может быть больше количества вариантов ({len(variables)}). Попробуйте еще раз")
            return

        await state.update_data(winners_count=winners_count)
        await message.answer("Напишите дату окончания голосования (например, 11.11.2025 11:11)")
        await state.set_state(VoteStates.waiting_for_data)
        logger.info(f"Пользователь {message.from_user.id} установил количество победителей: {winners_count}")

    except ValueError:
        await message.answer("Пожалуйста, введите корректное число")
        return
    except Exception as e:
        logger.error(f"Ошибка обработки количества победителей: {e}")
        await message.answer("Не удалось обработать количество победителей. Попробуйте еще раз")
        return


@dp.message(VoteStates.waiting_for_data)
async def handle_data(message: types.Message, state: FSMContext):
    """Обработка даты окончания"""
    if not await check_admin_access(message):
        await state.clear()
        return

    time_text = message.text
    try:
        dt = datetime.datetime.strptime(time_text, "%d.%m.%Y %H:%M")
        current_time = datetime.datetime.now()

        if dt <= current_time:
            await message.answer("Указанное время уже прошло! Пожалуйста, укажите будущую дату и время.")
            return

        await state.update_data(time=dt.strftime("%d.%m.%Y %H:%M"))
    except ValueError:
        await message.answer("Неверный формат времени. Используйте формат: дд.мм.гггг чч:мм")
        return
    except Exception as e:
        logger.error(f"Ошибка обработки времени: {e}")
        await message.answer("Не удалось записать время. Попробуйте еще раз")
        return

    keyboard = types.InlineKeyboardMarkup(
        inline_keyboard=[
            [types.InlineKeyboardButton(text="Да", callback_data="confim")],
            [types.InlineKeyboardButton(text="Нет", callback_data="unconfim")],
        ]
    )
    await message.answer("Подтвердить?", reply_markup=keyboard)


@dp.callback_query(lambda callback: callback.data == "confim")
async def handle_button_confim(callback: types.CallbackQuery, state: FSMContext):
    """Подтверждение создания голосования"""
    if not await check_admin_access(callback.message):
        await state.clear()
        return

    states = await state.get_state()
    if states == VoteStates.waiting_for_data:
        await bot.send_message(callback.message.chat.id, "Статистика отправлена в канал!")

        data = await state.get_data()

        async with aiosqlite.connect(DATABASE_PATH) as db:
            try:
                cursor = await db.execute(
                    'INSERT INTO active_votes (created_at, variables, file_ids_with_types, end_time, finish_button_message_id, is_active, winners_count) VALUES (?, ?, ?, ?, ?, 1, ?)',
                    (
                        datetime.datetime.now().isoformat(),
                        json.dumps(data['variables']),
                        json.dumps(data.get('file_ids_with_types', [])),
                        data['time'],
                        callback.message.message_id,
                        data.get('winners_count', 1)
                    )
                )
                vote_id = cursor.lastrowid
                await db.commit()

                # Создаем кнопку принудительного завершения для этого конкретного голосования
                finish_keyboard = types.InlineKeyboardMarkup(
                    inline_keyboard=[
                        [types.InlineKeyboardButton(text="Принудительно завершить",
                                                    callback_data=f"force_finish_{vote_id}")],
                    ]
                )

                finish_message = await callback.message.answer(
                    "Вы можете принудительно завершить голосование:",
                    reply_markup=finish_keyboard
                )

                # Обновляем finish_button_message_id в базе данных
                await db.execute(
                    'UPDATE active_votes SET finish_button_message_id = ? WHERE id = ?',
                    (finish_message.message_id, vote_id)
                )
                await db.commit()

            except Exception as e:
                logger.error(f"Ошибка при сохранении голосования: {e}")
                # Попробуем без finish_button_message_id
                cursor = await db.execute(
                    'INSERT INTO active_votes (created_at, variables, file_ids_with_types, end_time, is_active, winners_count) VALUES (?, ?, ?, ?, 1, ?)',
                    (
                        datetime.datetime.now().isoformat(),
                        json.dumps(data['variables']),
                        json.dumps(data.get('file_ids_with_types', [])),
                        data['time'],
                        data.get('winners_count', 1)
                    )
                )
                vote_id = cursor.lastrowid
                await db.commit()

        # Запускаем задачу обновления для этого голосования
        update_tasks[vote_id] = asyncio.create_task(update_channel_message_periodically(vote_id))

        keyboard = types.InlineKeyboardMarkup(
            inline_keyboard=[
                [types.InlineKeyboardButton(text="Проголосовать",
                                            url=f"https://t.me/DonBallon_vote_bot?start=vote_{vote_id}")],
            ]
        )

        str_var = format_variables_for_results(data['variables'], {}, winners_count=data.get('winners_count', 1))
        channel_message = await bot.send_message(cnl_id, str_var, reply_markup=keyboard, parse_mode="HTML")

        async with aiosqlite.connect(DATABASE_PATH) as db:
            try:
                await db.execute(
                    'UPDATE active_votes SET channel_message_id = ? WHERE id = ?',
                    (channel_message.message_id, vote_id)
                )
                await db.commit()
            except Exception as e:
                logger.error(f"Ошибка при обновлении channel_message_id: {e}")

        await state.clear()
        await state.set_state(VoteStates.waiting_for_photo)
        logger.info(f"Пользователь {callback.from_user.id} создал новое голосование {vote_id}")
    else:
        await bot.send_message(callback.message.chat.id,
                               "Вы еще не создали голосование. Отправте голосование из канала")
        await state.set_state(VoteStates.waiting_for_photo)


@dp.callback_query(lambda callback: callback.data == "unconfim")
async def handle_button_unconfim(callback: types.CallbackQuery, state: FSMContext):
    """Отмена создания голосования"""
    if not await check_admin_access(callback.message):
        await state.clear()
        return

    data = await state.get_state()
    if data == VoteStates.waiting_for_data:
        await bot.send_message(callback.message.chat.id,
                               "Хорошо, удаляю черновик голосования. Для создания следующего голосования отправьте голосование из канала")
        await state.clear()
        await state.set_state(VoteStates.waiting_for_photo)
        logger.info(f"Пользователь {callback.from_user.id} отменил создание голосования")
    else:
        await bot.send_message(callback.message.chat.id,
                               "Вы еще не создали голосование. Отправте голосование из канала")
        await state.set_state(VoteStates.waiting_for_photo)


@dp.message()
async def handle_message(message: types.Message, state: FSMContext):
    """Обработчик всех сообщений"""
    current_state = await state.get_state()
    if current_state in [VoteStates.waiting_for_photo, VoteStates.waiting_for_variable,
                         VoteStates.waiting_for_winners_count, VoteStates.waiting_for_data]:
        if not await check_admin_access(message):
            await state.clear()
            return

    if message.media_group_id:
        data = await state.get_data()
        if data.get('media_group_processed'):
            return
        success = await cache_media_group_to_state(
            group_id=message.media_group_id,
            message=message,
            state=state
        )
        if success:
            await message.answer(
                "Напишите варианты ответа с ссылками (например 'работа Ольги https://t.me/example;работа Вики https://www.instagram.com/example;работа Оксаны')")
            await state.set_state(VoteStates.waiting_for_variable)
    elif message.photo or message.video:
        # Обработка одиночного медиа в любом состоянии - сбрасываем состояние
        await state.clear()
        await handle_album(message, state)
    else:
        await message.answer("Я Вас не понимаю, перешлите голосование из канала для начала создания")


async def main():
    """Основная функция запуска бота"""
    db_lock = await acquire_db_lock()
    if not db_lock:
        logger.error("База данных уже заблокирована другим процессом. Возможно, бот уже запущен.")
        return

    try:
        await init_database()
        await restore_update_tasks()

        logger.info("Серверный бот запущен")
        await dp.start_polling(bot)
    except Exception as e:
        logger.error(f"Критическая ошибка при запуске серверного бота: {e}")
    finally:
        # Отменяем все задачи обновления при завершении
        for vote_id, task in update_tasks.items():
            if not task.done():
                task.cancel()
        await release_db_lock(db_lock)
        await bot.session.close()


if __name__ == '__main__':
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        logger.info("Бот остановлен пользователем")
    except Exception as e:
        logger.error(f"Ошибка запуска бота: {e}")