import logging
from typing import Optional

from rest_framework.request import Request
from telegram import Bot, Update

from apps.base.utils import live_settings
from apps.telegram.models import TelegramToOrganizationConnector, TelegramToUserConnector
from apps.telegram.updates.update_handlers.update_handler import UpdateHandler

logger = logging.getLogger(__name__)

TELEGRAM_ID = 777000


class UpdateManager:
    """
    Manager for Telegram updates
    It selects appropriate UpdateHandler and makes selected handler process the update
    Also UpdateManager updates user, channel and group names on every update to make sure names in database are in sync
    """

    @classmethod
    def process_update(cls, update: Update) -> None:
        cls._update_entity_names(update)

        handler = cls.select_update_handler(update)
        if handler is None:
            logger.info("No update handlers applied for update")
            return

        logger.info(f"Processing update with handler: {handler.__class__.__name__}")
        handler.process_update()

    @staticmethod
    def select_update_handler(update: Update) -> Optional[UpdateHandler]:
        handler_classes = UpdateHandler.__subclasses__()
        for handler_class in handler_classes:
            handler = handler_class(update)
            if handler.matches():
                return handler
        return None

    @classmethod
    def process_request(cls, request: Request) -> None:
        update = Update.de_json(request.data, bot=Bot(live_settings.TELEGRAM_TOKEN))
        logger.info(f"Update from Telegram: {update}")
        cls.process_update(update)

    @classmethod
    def _update_entity_names(cls, update: Update) -> None:
        if update.effective_user is None:
            return

        if update.effective_user.id == TELEGRAM_ID:
            cls._update_channel_and_group_names(update)
        else:
            cls._update_user_names(update)

    @staticmethod
    def _update_channel_and_group_names(update: Update) -> None:
        """
        some updates may not necessarily come from the channel
        (in which case they would contain the `forward_from_chat` object). Some updates may come directly from the
        discussion group, in which case `forward_from_chat` is not present
        """
        if update.message.forward_from_chat is None:
            return

        channel_chat_id = update.message.forward_from_chat.id
        channel_name = update.message.forward_from_chat.title

        discussion_group_chat_id = update.message.chat.id
        discussion_group_name = update.message.chat.title

        TelegramToOrganizationConnector.objects.filter(
            channel_chat_id=channel_chat_id, discussion_group_chat_id=discussion_group_chat_id
        ).update(channel_name=channel_name, discussion_group_name=discussion_group_name)

    @staticmethod
    def _update_user_names(update: Update) -> None:
        user = update.effective_user
        telegram_nick_name = user.username or user.first_name or user.last_name or "Unknown"

        TelegramToUserConnector.objects.filter(telegram_chat_id=user.id).update(telegram_nick_name=telegram_nick_name)
