import logging
import os
from collections.abc import Sequence
from typing import TypedDict, Union
from pathlib import Path
from telegram import Bot, InputMediaPhoto


logger = logging.getLogger(__name__)


class TelegramMediaItem(TypedDict, total=False):
    photo: str
    caption: str


class TelegramClient:
    def __init__(self, token: str):
        self.bot = Bot(token=token)

    async def get_me(self):
        try:
            return await self.bot.get_me()
        except Exception:
            logger.exception("Failed to fetch Telegram bot info")
            raise

    async def send_text(
        self,
        chat_id: Union[int, str],
        text: str,
        parse_mode: str = "HTML",
        disable_web_page_preview: bool = True,
    ):
        try:
            return await self.bot.send_message(
                chat_id=chat_id,
                text=text,
                parse_mode=parse_mode,
                disable_web_page_preview=disable_web_page_preview,
            )
        except Exception:
            logger.exception("Error sending Telegram message")
            raise

    async def send_photo(
        self,
        *,
        photo_url_or_file: Union[str, None] = None,
        caption: str,
        chat_id: Union[int, str],
        parse_mode: str = "HTML",
        photo: Union[str, None] = None,
    ):
        file_handle = None
        photo_input = photo if photo is not None else photo_url_or_file
        if photo_input is None:
            raise ValueError("Either 'photo_url_or_file' or 'photo' must be provided")

        photo_payload = photo_input
        try:
            if os.path.exists(photo_input):
                file_handle = open(photo_input, "rb")
                photo_payload = file_handle
            return await self.bot.send_photo(
                chat_id=chat_id,
                photo=photo_payload,
                caption=caption,
                parse_mode=parse_mode,
            )
        except Exception:
            logger.exception("Error sending Telegram photo")
            raise
        finally:
            if file_handle is not None:
                file_handle.close()

    async def send_media_group(
        self,
        data: Sequence[dict],
        chat_id: Union[int, str],
        parse_mode: str = "HTML",
    ):
        if not data:
            return []

        media_group = []
        open_files = []

        try:
            for index, content in enumerate(data):
                raw_photo = content["photo"]
                photo_path = Path(raw_photo)

                if photo_path.is_file():
                    file_obj = photo_path.open("rb")
                    open_files.append(file_obj)
                    media = file_obj
                else:
                    media = raw_photo

                media_group.append(
                    InputMediaPhoto(
                        media=media,
                        caption=content.get("caption") if index == 0 else None,
                        parse_mode=parse_mode if index == 0 else None,
                    )
                )

            return await self.bot.send_media_group(
                chat_id=chat_id,
                media=media_group,
            )

        except Exception:
            logger.exception("Error sending Telegram media group")
            raise

        finally:
            for f in open_files:
                f.close()

    async def close(self):
        try:
            await self.bot.shutdown()
        except Exception:
            logger.exception("Failed to shutdown Telegram bot client")
            raise

    async def __aenter__(self):
        try:
            await self.bot.initialize()
            return self
        except Exception:
            logger.exception("Failed to initialize Telegram bot client")
            raise

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        await self.close()
