#!/usr/bin/env python3
from __future__ import annotations

import json
import time
from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple

import requests

from cheapclaw_channel_base import ChannelAdapter


class TelegramAdapter(ChannelAdapter):
    name = "telegram"

    def __init__(self, config: Dict[str, Any], service: Any, *, logger=None):
        super().__init__(config, service, logger=logger)
        self.bot_token = str(config.get("bot_token") or "").strip()
        self.allowed_chats = {str(item) for item in config.get("allowed_chats", [])}
        self._api_root = f"https://api.telegram.org/bot{self.bot_token}" if self.bot_token else ""
        self._state = self.service.load_runtime_state()
        self._me_cache = self._state.get("telegram_bot_me") or {}

    def _request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
        if not self._api_root:
            return {}
        response = requests.request(method, self._api_root + endpoint, timeout=30, **kwargs)
        response.raise_for_status()
        data = response.json()
        return data if isinstance(data, dict) else {}

    def _get_me(self) -> Dict[str, Any]:
        if self._me_cache:
            return self._me_cache
        data = self._request("GET", "/getMe")
        self._me_cache = data.get("result", {}) if data.get("ok") else {}
        state = self.service.load_runtime_state()
        state["telegram_bot_me"] = self._me_cache
        self.service.save_runtime_state(state)
        return self._me_cache

    def _message_mentions_bot(self, message: Dict[str, Any], text: str, is_group: bool) -> bool:
        if not is_group:
            return True
        me = self._get_me() or {}
        bot_username = str(me.get("username") or "").strip()
        bot_id = str(me.get("id") or "").strip()
        if bot_username and f"@{bot_username}".lower() in text.lower():
            return True

        for entity in (message.get("entities") or []) + (message.get("caption_entities") or []):
            if not isinstance(entity, dict):
                continue
            entity_type = str(entity.get("type") or "")
            if entity_type == "mention" and bot_username:
                offset = int(entity.get("offset") or 0)
                length = int(entity.get("length") or 0)
                if text[offset : offset + length].lower() == f"@{bot_username}".lower():
                    return True
            if entity_type == "text_mention":
                user = entity.get("user") or {}
                if bot_id and str(user.get("id") or "") == bot_id:
                    return True

        reply_to = message.get("reply_to_message") or {}
        reply_from = reply_to.get("from") or {}
        if bot_id and str(reply_from.get("id") or "") == bot_id:
            return True
        if reply_from.get("is_bot") and bot_username and str(reply_from.get("username") or "").lower() == bot_username.lower():
            return True
        return False

    def poll_events(self) -> List[Dict[str, Any]]:
        if not self.bot_token:
            return []
        state = self.service.load_runtime_state()
        offsets = state.setdefault("telegram_offsets", {})
        offset = int(offsets.get("default", 0) or 0)
        data = self._request("GET", "/getUpdates", params={"timeout": 1, "offset": offset + 1})
        items = []
        for result in data.get("result", []):
            update_id = int(result.get("update_id") or 0)
            message = (
                result.get("message")
                or result.get("edited_message")
                or result.get("channel_post")
                or result.get("edited_channel_post")
                or result.get("business_message")
                or result.get("edited_business_message")
                or {}
            )
            chat = message.get("chat") or {}
            chat_id = str(chat.get("id") or "").strip()
            if not chat_id:
                continue
            if self.allowed_chats and chat_id not in self.allowed_chats:
                continue
            text = str(message.get("text") or message.get("caption") or "").strip()
            is_group = chat.get("type") in {"group", "supergroup"}
            mention = self._message_mentions_bot(message, text, is_group)
            if is_group and not mention:
                offsets["default"] = update_id
                continue
            from_user = message.get("from") or message.get("sender_chat") or {}
            sender_name = " ".join(
                part
                for part in [
                    str(from_user.get("first_name") or "").strip(),
                    str(from_user.get("last_name") or "").strip(),
                ]
                if part
            ).strip()
            if not sender_name:
                sender_name = str(from_user.get("title") or from_user.get("username") or "")
            items.append(
                {
                    "event_id": f"telegram_{update_id}",
                    "channel": "telegram",
                    "conversation_id": chat_id,
                    "conversation_type": "group" if is_group else "person",
                    "display_name": str(chat.get("title") or chat.get("username") or chat_id),
                    "sender_id": str(from_user.get("id") or ""),
                    "sender_name": sender_name,
                    "message_id": str(message.get("message_id") or ""),
                    "message_text": text,
                    "attachments": [],
                    "timestamp": datetime.fromtimestamp(int(message.get("date") or time.time())).astimezone().isoformat(timespec="seconds"),
                    "is_mention_to_bot": mention,
                    "require_mention": is_group,
                }
            )
            offsets["default"] = update_id
        self.service.save_runtime_state(state)
        return items

    def send_message(
        self,
        conversation_id: str,
        message: str,
        attachments: Optional[List[Dict[str, Any]]] = None,
    ) -> Tuple[bool, str]:
        if not self.bot_token:
            return False, "telegram bot_token is missing"
        last_remote_id = ""
        normalized = self._normalize_attachments(attachments)
        if message:
            payload = {"chat_id": conversation_id, "text": message}
            try:
                data = self._request("POST", "/sendMessage", json=payload)
            except Exception as exc:
                return False, str(exc)
            if not data.get("ok"):
                return False, json.dumps(data, ensure_ascii=False)
            last_remote_id = str((data.get("result") or {}).get("message_id") or "")
        for item in normalized:
            mime_type = item["mime_type"]
            method = "/sendPhoto" if mime_type.startswith("image/") else "/sendDocument"
            field = "photo" if method == "/sendPhoto" else "document"
            data_payload = {"chat_id": conversation_id}
            caption = item["caption"] or (message if not last_remote_id else "")
            if caption:
                data_payload["caption"] = caption
            with open(item["path"], "rb") as fh:
                files = {field: (item["filename"], fh, mime_type)}
                try:
                    data = self._request("POST", method, data=data_payload, files=files)
                except Exception as exc:
                    return False, str(exc)
            if not data.get("ok"):
                return False, json.dumps(data, ensure_ascii=False)
            last_remote_id = str((data.get("result") or {}).get("message_id") or "")
        if not message and not normalized:
            return False, "telegram message or attachments are required"
        return True, last_remote_id
