"""Отдельный бот-портал: капча при /start и постоянные ссылки на чаты."""

from __future__ import annotations

import asyncio
import random
import sys
from pathlib import Path
from typing import Optional

_ROOT = Path(__file__).resolve().parent.parent
if str(_ROOT) not in sys.path:
    sys.path.insert(0, str(_ROOT))

from aiogram import Bot, Dispatcher, F
from aiogram.client.default import DefaultBotProperties
from aiogram.enums import ParseMode, PollType
from aiogram.filters import CommandStart
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message, Poll

from api.db_api import list_portal_chats
from server.config import PORTAL_BOT_TOKEN

_pending: dict[str, dict] = {}
_bot: Optional[Bot] = None
_dp: Optional[Dispatcher] = None
_task: Optional[asyncio.Task] = None


def _make_captcha() -> tuple[str, list[str], int]:
    kind = random.choice(("+", "-", "×"))
    if kind == "+":
        left, right = random.randint(2, 12), random.randint(2, 12)
        answer = left + right
        question = f"Сколько будет {left} + {right}?"
    elif kind == "-":
        left = random.randint(8, 20)
        right = random.randint(1, left - 1)
        answer = left - right
        question = f"Сколько будет {left} − {right}?"
    else:
        left, right = random.randint(2, 9), random.randint(2, 9)
        answer = left * right
        question = f"Сколько будет {left} × {right}?"

    options = {answer}
    while len(options) < 4:
        delta = random.randint(-8, 8)
        if delta == 0:
            continue
        candidate = answer + delta
        if candidate > 0:
            options.add(candidate)
    shuffled = list(options)
    random.shuffle(shuffled)
    return question, [str(item) for item in shuffled], shuffled.index(answer)


def _drop_user_pending(user_id: int) -> None:
    for key, value in list(_pending.items()):
        if value.get("user_id") == user_id:
            _pending.pop(key, None)


def _chat_buttons() -> InlineKeyboardMarkup | None:
    rows = []
    for item in list_portal_chats():
        title = str(item.get("title") or "").strip() or "Чат"
        link = str(item.get("invite_link") or "").strip()
        if not link:
            continue
        rows.append([InlineKeyboardButton(text=title[:64], url=link)])
    if not rows:
        return None
    return InlineKeyboardMarkup(inline_keyboard=rows)


async def _send_chats(chat_id: int) -> None:
    if _bot is None:
        return
    markup = _chat_buttons()
    if markup is None:
        await _bot.send_message(chat_id, "Проверка пройдена, но чаты пока не добавлены.")
        return
    await _bot.send_message(chat_id, "Готово. Ссылки на чаты:", reply_markup=markup)


def register_handlers(dp: Dispatcher) -> None:
    @dp.message(CommandStart(), F.chat.type == "private")
    async def cmd_start(message: Message) -> None:
        user_id = message.from_user.id if message.from_user else 0
        _drop_user_pending(user_id)
        question, options, correct = _make_captcha()
        await message.answer("Чтобы получить ссылки на чаты, ответьте на вопрос:")
        poll_msg = await message.answer_poll(
            question=question,
            options=options,
            type=PollType.QUIZ,
            correct_option_id=correct,
            is_anonymous=True,
            explanation="Нажмите /start, чтобы попробовать снова.",
        )
        poll = poll_msg.poll
        if poll:
            _pending[poll.id] = {
                "user_id": user_id,
                "chat_id": message.chat.id,
                "correct": correct,
            }

    @dp.poll()
    async def on_poll(poll: Poll) -> None:
        pending = _pending.get(poll.id)
        if not pending:
            return
        votes = [index for index, option in enumerate(poll.options) if option.voter_count > 0]
        if not votes:
            return
        _pending.pop(poll.id, None)
        if votes[0] == pending["correct"]:
            await _send_chats(pending["chat_id"])
            return
        if _bot is not None:
            await _bot.send_message(
                pending["chat_id"],
                "Неверно. Нажмите /start, чтобы попробовать снова.",
            )

    @dp.message(F.chat.type == "private", F.text)
    async def other_text(message: Message) -> None:
        await message.answer("Нажмите /start, чтобы пройти проверку и получить ссылки.")


def _token() -> str:
    return str(PORTAL_BOT_TOKEN or "").strip()


async def start_portal_bot() -> None:
    global _bot, _dp, _task
    token = _token()
    if not token:
        print("portal bot: PORTAL_BOT_TOKEN пуст — бот не запущен")
        return
    if _task and not _task.done():
        return
    _bot = Bot(token=token, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
    _dp = Dispatcher()
    register_handlers(_dp)

    async def _run() -> None:
        assert _dp is not None and _bot is not None
        try:
            await _dp.start_polling(_bot, allowed_updates=["message", "poll"])
        except asyncio.CancelledError:
            raise
        except Exception as exc:  # noqa: BLE001
            print(f"portal bot: {exc}")

    _task = asyncio.create_task(_run())
    print("portal bot: polling started")


async def stop_portal_bot() -> None:
    global _bot, _dp, _task
    task = _task
    _task = None
    if _dp is not None:
        try:
            await _dp.stop_polling()
        except Exception:
            pass
    if task:
        task.cancel()
        try:
            await task
        except (asyncio.CancelledError, Exception):
            pass
    if _bot is not None:
        await _bot.session.close()
    _bot = None
    _dp = None


async def _main() -> None:
    await start_portal_bot()
    if _task is None:
        return
    try:
        await _task
    except asyncio.CancelledError:
        pass
    finally:
        await stop_portal_bot()


if __name__ == "__main__":
    try:
        asyncio.run(_main())
    except KeyboardInterrupt:
        pass
