from typing import Any, List, Optional, Tuple
from java.util import ArrayList

from org.telegram.tgnet import TLRPC

from android_utils import log
from client_utils import get_user_config
from base_plugin import BasePlugin, HookResult, HookStrategy, MenuItemData, MenuItemType
from ui.settings import Header, Switch, Input, Selector, Divider
from ui.bulletin import BulletinHelper

__id__ = "quick_tap_reaction"
__name__ = "Quick Tap Reaction"
__description__ = "Подменяет реакцию по двойному тапу на настроенную (в т.ч. Premium) в выбранном чате"
__author__ = "@razeplugin"
__version__ = "2.1.6"
__min_version__ = "11.9.1"


class QuickTapReactionPlugin(BasePlugin):

    def on_plugin_load(self):
        self.add_hook("TL_messages_sendReaction")

        self._capture_item_id = self.add_menu_item(
            MenuItemData(
                menu_type=MenuItemType.MESSAGE_CONTEXT_MENU,
                text="Сохранить реакцию для плагина",
                on_click=self._on_capture_click,
                icon="msg_reactions2",
            )
        )
        self._log("Плагин успешно загружен")

    def on_plugin_unload(self):
        if hasattr(self, "_capture_item_id") and self._capture_item_id:
            self.remove_menu_item(self._capture_item_id)

    # --- логирование и утилиты ---

    def _log(self, msg: str):
        log(f"[QuickTapReaction] {msg}")

    def _debug_bulletin(self, msg: str, error: bool = False):
        if self.get_setting("debug", True):
            if error:
                BulletinHelper.show_error(msg)
            else:
                BulletinHelper.show_info(msg)

    def _save_all_settings(self):
        if hasattr(self, "save_settings"):
            try:
                self.save_settings()
            except Exception as e:
                self._log(f"Ошибка сохранения настроек: {e}")

    def _safe_int(self, val: Any) -> Optional[int]:
        if val is None:
            return None
        try:
            return int(str(val).strip())
        except (ValueError, TypeError):
            return None

    def _is_premium_selected(self) -> bool:
        val = self.get_setting("reaction_type", 0)
        if isinstance(val, int) and val == 1:
            return True
        val_str = str(val).strip()
        return val_str in ("1", "Premium (custom emoji)", "Premium")

    # --- извлечение ID / эмодзи из Java-объекта TLRPC ---

    def _extract_reaction_info(self, react_obj: Any) -> Optional[Tuple[str, str]]:
        """
        Более надёжное извлечение: пробуем разные имена полей и вложенные объекты.
        При включённом debug выводим type() и первые атрибуты объекта.
        Возвращаем ("custom", id_str) или ("emoji", emoji_str) или None.
        """
        if react_obj is None:
            return None

        # Отладочный вывод: тип и список атрибутов (не перегружаем длинным выводом)
        if self.get_setting("debug", True):
            try:
                attrs = dir(react_obj)
                # Показываем первые 100 атрибутов в лог
                self._log(f"react_obj type={type(react_obj)} attrs_sample={attrs[:100]}")
                try:
                    self._log(f"react_obj repr: {repr(react_obj)[:1000]}")
                except Exception:
                    pass
            except Exception:
                pass

        # Если объект — простая строка (часто для обычной эмодзи)
        try:
            if isinstance(react_obj, str):
                if react_obj:
                    return ("emoji", react_obj)
        except Exception:
            pass

        # Иногда реакция может быть вложена в поле reaction (когда элемент wrapper)
        try:
            inner = getattr(react_obj, "reaction", None)
            if inner is not None and inner is not react_obj:
                info = self._extract_reaction_info(inner)
                if info:
                    return info
        except Exception:
            pass

        # 1) Попытки найти document id / custom emoji
        try:
            doc_attr_names = ("document_id", "documentId", "document", "doc", "custom_emoji", "customEmoji", "emoji_document")
            for name in doc_attr_names:
                val = getattr(react_obj, name, None)
                if val:
                    # Если это вложенный объект Document — извлекаем возможные id-поля
                    if not isinstance(val, (str, int)):
                        for id_name in ("id", "document_id", "documentId", "file_id"):
                            doc_id = getattr(val, id_name, None)
                            if doc_id is not None:
                                try:
                                    if int(doc_id) != 0:
                                        return ("custom", str(doc_id))
                                except Exception:
                                    return ("custom", str(doc_id))
                        # Если вложенный объект приводится к строке с цифрами — попробуем
                        try:
                            s = str(val)
                            if s.isdigit():
                                return ("custom", s)
                        except Exception:
                            pass
                    else:
                        # val — простое значение (число или строка)
                        try:
                            if int(val) != 0:
                                return ("custom", str(val))
                        except Exception:
                            # если не число, возможно это строковое представление ID — вернём как есть
                            if isinstance(val, str) and val.strip():
                                return ("custom", val.strip())
        except Exception:
            pass

        # 2) Попытки извлечь обычное эмодзи
        try:
            emoji_candidates = (
                getattr(react_obj, "emoticon", None),
                getattr(react_obj, "emoji", None),
                getattr(react_obj, "text", None),
                getattr(react_obj, "symbol", None),
            )
            for cand in emoji_candidates:
                if cand:
                    if isinstance(cand, str):
                        return ("emoji", cand)
                    # если это объект с полем emoticon
                    if hasattr(cand, "emoticon"):
                        e = getattr(cand, "emoticon")
                        if e:
                            return ("emoji", str(e))
        except Exception:
            pass

        return None

    # --- настройки ---

    def create_settings(self) -> List[Any]:
        return [
            Header(text="Быстрая реакция по двойному тапу"),
            Switch(key="enabled", text="Включить", default=True),
            Input(
                key="chat_id",
                text="ID чата",
                default="",
                subtext=(
                    "Числовой ID чата, где реакция будет подменяться. "
                    "Для групп/каналов — со знаком минус (например -1001234567890). "
                    "Для личного чата — просто user_id."
                ),
            ),
            Switch(key="big", text="Крупная реакция", default=False),
            Divider(),
            Header(text="Тип реакции"),
            Selector(
                key="reaction_type",
                text="Тип",
                default=0,
                items=["Обычное эмодзи", "Premium (custom emoji)"],
            ),
            Input(
                key="emoji",
                text="Эмодзи",
                default="👍",
                subtext="Используется при типе «Обычное эмодзи»",
            ),
            Input(
                key="custom_emoji_id",
                text="ID premium-эмодзи (document_id)",
                default="",
                subtext=(
                    "Заполняется автоматически через «Сохранить реакцию для плагина», "
                    "либо впишите число (document_id) вручную."
                ),
            ),
            Divider(),
            Switch(
                key="debug",
                text="Показывать уведомления-диагностику",
                default=True,
            ),
        ]

    # --- захват реакции с сообщения через контекстное меню ---

    def _on_capture_click(self, context: dict):
        message = context.get("message")
        if message is None:
            self._debug_bulletin("Сообщение не найдено", error=True)
            return

        msg_owner = getattr(message, "messageOwner", message)
        reactions = getattr(msg_owner, "reactions", None)
        if reactions is None:
            self._debug_bulletin("На сообщении нет реакций", error=True)
            return

        found_type = None
        found_val = None

        # 1) Сканируем recentReactions
        recent = getattr(reactions, "recentReactions", None)
        if recent:
            for pr in recent:
                react_obj = getattr(pr, "reaction", None)
                # Если recent элемент — сам по себе реакция (иногда)
                if react_obj is None:
                    react_obj = getattr(pr, "reaction", None) or pr
                info = self._extract_reaction_info(react_obj)
                if info:
                    found_type, found_val = info
                    # Если нашли Premium — сразу останавливаем поиск
                    if found_type == "custom":
                        break

        # 2) Сканируем results (если в recentReactions не нашли Premium)
        if found_type != "custom":
            results = getattr(reactions, "results", None)
            if results:
                for r in results:
                    react_obj = getattr(r, "reaction", None)
                    if react_obj is None:
                        react_obj = getattr(r, "reaction", None) or r
                    info = self._extract_reaction_info(react_obj)
                    if info:
                        # Запоминаем или перебиваем обычную реакцию на Premium
                        if info[0] == "custom" or found_val is None:
                            found_type, found_val = info
                            if found_type == "custom":
                                break

        if not found_val:
            self._debug_bulletin("Не удалось прочитать ID реакции", error=True)
            return

        # Сохранение значений
        if found_type == "custom":
            self.set_setting("reaction_type", 1)
            self.set_setting("custom_emoji_id", str(found_val))
            self._save_all_settings()
            self._debug_bulletin(f"Сохранен Premium ID: {found_val}")
        else:
            self.set_setting("reaction_type", 0)
            self.set_setting("emoji", str(found_val))
            self._save_all_settings()
            self._debug_bulletin(f"Сохранено эмодзи: {found_val}")

    # --- перехват sendReaction ---

    def pre_request_hook(self, request_name: str, account: int, request: Any) -> HookResult:
        if request_name != "TL_messages_sendReaction":
            return HookResult()

        if not self.get_setting("enabled", True):
            return HookResult()

        try:
            target_raw = (self.get_setting("chat_id", "") or "").strip()
            if not target_raw:
                return HookResult()

            target_id = self._safe_int(target_raw)
            if target_id is None:
                self._debug_bulletin("Некорректный ID чата в настройках", error=True)
                return HookResult()

            dialog_id = self._input_peer_to_dialog_id(request.peer)
            if dialog_id is None or dialog_id != target_id:
                return HookResult()

            reaction, err_reason = self._build_reaction()
            if reaction is None:
                self._debug_bulletin(f"Ошибка: {err_reason}", error=True)
                return HookResult()

            # Запись реакции в Java ArrayList
            if hasattr(request, "reaction") and request.reaction is not None and hasattr(request.reaction, "clear"):
                try:
                    request.reaction.clear()
                except Exception:
                    pass
                try:
                    request.reaction.add(reaction)
                except Exception:
                    # На всякий случай создаём новый ArrayList
                    java_list = ArrayList()
                    java_list.add(reaction)
                    request.reaction = java_list
            else:
                java_list = ArrayList()
                java_list.add(reaction)
                request.reaction = java_list

            try:
                request.big = bool(self.get_setting("big", False))
            except Exception:
                pass

            if self._is_premium_selected():
                self._debug_bulletin(f"Отправка Premium (ID: {self.get_setting('custom_emoji_id')})")
            else:
                self._debug_bulletin(f"Отправка эмодзи: {self.get_setting('emoji')}")

            return HookResult(strategy=HookStrategy.MODIFY, request=request)

        except Exception as e:
            self._log(f"Ошибка в pre_request_hook: {e}")
            self._debug_bulletin(f"Ошибка плагина: {e}", error=True)
            return HookResult()

    def post_request_hook(self, request_name: str, account: int, response: Any, error: Any) -> HookResult:
        if request_name == "TL_messages_sendReaction" and error:
            try:
                self._log(f"sendReaction ошибка сервера: {error.text}")
                self._debug_bulletin(f"Сервер отклонил реакцию: {error.text}", error=True)
            except Exception:
                self._log("sendReaction ошибка сервера (ошибка при выводе текста)")
        return HookResult()

    # --- вспомогательное ---

    def _input_peer_to_dialog_id(self, peer: Any) -> Optional[int]:
        if peer is None:
            return None
        if isinstance(peer, TLRPC.TL_inputPeerUser):
            return int(peer.user_id)
        if isinstance(peer, TLRPC.TL_inputPeerChat):
            return -int(peer.chat_id)
        if isinstance(peer, TLRPC.TL_inputPeerChannel):
            return -(1000000000000 + int(peer.channel_id))
        return None

    def _build_reaction(self) -> Tuple[Optional[Any], Optional[str]]:
        """
        Создаёт объект реакции в зависимости от выбора (Premium или обычная).
        При создании premium пытаемся установить несколько вариантов имён полей.
        """
        if self._is_premium_selected():
            raw_val = self.get_setting("custom_emoji_id", "")
            raw_id = str(raw_val or "").strip()

            if not raw_id:
                return None, "Поле ID premium-эмодзи пустое!"

            doc_id = self._safe_int(raw_id)
            if doc_id is None:
                return None, f"ID '{raw_id}' не является числом!"

            try:
                reaction = TLRPC.TL_reactionCustomEmoji()
                # Попытка установить разные имена полей, чтобы покрыть разные версии TL-классов
                for field_name in ("document_id", "documentId", "document", "doc", "file_id"):
                    try:
                        setattr(reaction, field_name, doc_id)
                    except Exception:
                        pass
                # Иногда нужен вложенный Document-объект, но в большинстве случаев достаточно id
                return reaction, None
            except Exception as e:
                return None, f"Ошибка создания объекта Premium: {e}"
        else:
            emoji = str(self.get_setting("emoji", "👍") or "👍").strip()
            if not emoji:
                return None, "Поле эмодзи пустое!"

            try:
                reaction = TLRPC.TL_reactionEmoji()
                # Попробуем установить оба варианта имени поля
                for field_name in ("emoticon", "emoji", "text"):
                    try:
                        setattr(reaction, field_name, emoji)
                    except Exception:
                        pass
                return reaction, None
            except Exception as e:
                return None, f"Ошибка создания эмодзи: {e}"