import logging
from typing import Optional
from urllib.parse import urlparse

import aiohttp
from ezmm import MultimodalSequence, Image, Item, Video
from telethon import TelegramClient
from telethon.tl.types import Channel, User

from scrapemm.api_keys import get_api_key
from scrapemm.integrations.base import RetrievalIntegration
from scrapemm.util import get_domain

logger = logging.getLogger("Retriever")


class Telegram(RetrievalIntegration):
    """The Telegram integration for retrieving post contents from Telegram channels and groups."""

    domains = ["t.me", "telegram.me"]
    session_path = "temp/telegram"

    def __init__(self):
        api_id = int(get_api_key("telegram_api_id"))
        api_hash = get_api_key("telegram_api_hash")
        bot_token = get_api_key("telegram_bot_token")

        if api_id and api_hash and bot_token:
            self.client = TelegramClient(self.session_path, api_id, api_hash)
            self.client.start(bot_token=bot_token)
            self.connected = True
            logger.info("✅ Successfully connected to Telegram.")
        else:
            logger.warning("❌ Telegram integration not configured: Missing API keys.")

    async def get(self, url: str, session: aiohttp.ClientSession) -> Optional[MultimodalSequence]:
        """Retrieves content from a Telegram post URL."""
        assert get_domain(url) in self.domains

        # Parse the URL to get channel/group name and post ID
        parsed = urlparse(url)
        path_parts = parsed.path.strip("/").split("/")

        if len(path_parts) < 2:
            return None

        channel_name = path_parts[0]
        post_id = int(path_parts[1])

        try:
            # Get the message
            channel = await self.client.get_entity(channel_name)
            message = await self.client.get_messages(channel, ids=post_id)

            if not message:
                return None

            # Handle media
            media = await self._get_media_from_message(channel, message)

            author = message.sender
            author_type = type(author).__name__
            if isinstance(author, Channel):
                name = f'"{author.title}"'
                if author.username:
                    name += f" (@{author.username})"
            elif isinstance(author, User):
                if author.bot:
                    author_type = "Bot"
                name = f"{author.first_name} {author.last_name}" if author.last_name else author.first_name
                if author.username:
                    name += f" (@{author.username})"
                if author.phone:
                    name += f", Phone: {author.phone}"
                if author.verified:
                    name += " (Verified)"
            else:
                name = "Unknown"

            edit_text = "\nEdit date: " + message.edit_date.strftime("%B %d, %Y at %H:%M") if message.edit_date else ""
            reactions_text = "\nReactions: " + message.reactions.stringify() if message.reactions else ""

            text = f"""**Telegram Post**
Author: {author_type} {name}
Date: {message.date.strftime("%B %d, %Y at %H:%M")}{edit_text}
Views: {message.views}
Forwards: {message.forwards}{reactions_text}

{' '.join(m.reference for m in media)}
{message.text}"""

            return MultimodalSequence(text)

        except Exception as e:
            print(f"Error retrieving Telegram content: {e}")
            # raise
            return None

    async def _get_media_from_message(self, chat, original_post, max_amp=10) -> list[Item]:
        """
        Searches for Telegram posts that are part of the same group of uploads.
        The search is conducted around the id of the original post with an amplitude
        of `max_amp` both ways.
        Returns a list of [post] where each post has media and is in the same grouped_id.
        """
        # Gather posts that may belong to the same group
        if original_post.grouped_id is None:
            posts = [original_post]
        else:
            search_ids = list(range(original_post.id - max_amp, original_post.id + max_amp + 1))
            posts = await self.client.get_messages(chat, ids=search_ids)

        # Download media of posts that belong to the same group
        media = []
        for post in posts:
            if post is not None and post.grouped_id == original_post.grouped_id:
                if medium := post.media:
                    post_url = f"https://t.me/{chat.username}/{post.id}"
                    medium_bytes = await self.client.download_media(post, file=bytes)
                    if hasattr(medium, "photo"):
                        item = Image(binary_data=medium_bytes, source_url=post_url)
                    elif hasattr(medium, "video"):
                        item = Video(binary_data=medium_bytes, source_url=post_url)
                    else:
                        raise ValueError(f"Unsupported medium: {medium.__dict__}")
                    media.append(item)

        return media
