# bot/bot_factory.py
import logging
from typing import Optional, Sequence, Tuple

from telegram.constants import ParseMode
from telegram.ext import (
    Application,
    ApplicationBuilder,
    CommandHandler,
    MessageHandler,
    ChatMemberHandler,
    Defaults,
    filters,
)

from .models import TelegramBot, BotConfig
from .handlers import start, ping, echo, my_chat_member_update, error_handler
from .services import build_services

logger = logging.getLogger(__name__)


def _resolve_parse_mode(pm: Optional[str]):
    if not pm or pm == "None":
        return None
    return getattr(ParseMode, pm, ParseMode.HTML)


def build_application_for_bot(tb: TelegramBot, cfg: BotConfig) -> Tuple[Application, Optional[list]]:
    """
    Фабрика PTB Application для КОНКРЕТНОГО бота.
    ВАЖНО: никакого ORM внутри — cfg передаётся извне.
    """
    parse_mode = _resolve_parse_mode(cfg.parse_mode)
    defaults = Defaults(parse_mode=parse_mode) if parse_mode else None
    allowed_updates = cfg.allowed_updates or None

    builder: ApplicationBuilder = Application.builder().token(tb.token)
    if defaults:
        builder = builder.defaults(defaults)

    app = builder.build()

    # Хендлеры
    app.add_handler(CommandHandler("start", start))
    app.add_handler(CommandHandler("ping", ping))
    app.add_handler(ChatMemberHandler(my_chat_member_update, ChatMemberHandler.MY_CHAT_MEMBER))
    app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))

    # Глобальная обработка ошибок
    app.add_error_handler(error_handler)

    # DI: сервисы для этого бота
    services = build_services(tb)
    app.bot_data["services"] = services
    app.bot_data["allowed_updates"] = allowed_updates

    logger.info(
        "Built app for bot=%s (@%s), parse_mode=%s, allowed_updates=%s",
        tb.name, tb.username or "—", parse_mode, allowed_updates
    )
    return app, allowed_updates


def get_first_active_bot_with_config() -> Tuple[TelegramBot, BotConfig]:
    """
    СИНХРОННО! Забирает первого активного бота и его конфиг.
    """
    tb = TelegramBot.objects.filter(is_active=True).first()
    if not tb:
        raise RuntimeError("Нет активного TelegramBot (is_active=True). Создайте запись и включите её.")
    cfg = BotConfig.objects.filter(bot=tb).first()
    if not cfg:
        raise RuntimeError(f"Для бота '{tb}' не найден BotConfig. Создайте связанную запись BotConfig.")
    return tb, cfg


def get_all_active_bots_with_configs() -> Sequence[Tuple[TelegramBot, BotConfig]]:
    """
    СИНХРОННО! Возвращает все (bot, cfg) для активных ботов.
    Бросает, если хотя бы у одного нет BotConfig.
    """
    bots = list(TelegramBot.objects.filter(is_active=True))
    if not bots:
        return []

    cfg_map = {c.bot_id: c for c in BotConfig.objects.filter(bot__in=bots)}
    result: list[Tuple[TelegramBot, BotConfig]] = []
    missing = []
    for b in bots:
        cfg = cfg_map.get(b.id)
        if not cfg:
            missing.append(b)
        else:
            result.append((b, cfg))

    if missing:
        names = ", ".join(f"{b.name}(@{b.username or '—'})" for b in missing)
        raise RuntimeError(f"Нет BotConfig для ботов: {names}")

    return result
