from threading import Lock
from typing import Optional, Tuple, Union

from app.core.config import settings
from app.log import logger
from app.modules import _ModuleBase
from app.modules.thetvdb import tvdb_v4_official
from app.schemas.types import ModuleType, MediaRecognizeType


class TheTvDbModule(_ModuleBase):
    """
    TVDB媒体信息匹配
    """
    __timeout: int = 15
    tvdb: Optional[tvdb_v4_official.TVDB] = None
    __auth_lock = Lock()

    def init_module(self) -> None:
        pass

    def _initialize_tvdb_session(self, is_retry: bool = False) -> None:
        """
        创建或刷新 TVDB 登录会话。
        :param is_retry: 是否是由于token失效后的重试登录
        """
        action = "刷新" if is_retry else "创建"
        logger.info(f"开始{action}TVDB登录会话...")
        try:
            if not settings.TVDB_V4_API_KEY:
                raise ConnectionError("TVDB API Key 未配置，无法初始化会话。")
            self.tvdb = tvdb_v4_official.TVDB(apikey=settings.TVDB_V4_API_KEY,
                                              pin=settings.TVDB_V4_API_PIN,
                                              proxy=settings.PROXY,
                                              timeout=self.__timeout)
            if self.tvdb:
                logger.info(f"TVDB登录会话{action}成功。")
            else:
                raise ValueError(f"TVDB登录会话{action}后实例仍为None。")
        except Exception as e:
            self.tvdb = None
            raise ConnectionError(f"TVDB登录会话{action}失败: {str(e)}") from e

    def _ensure_tvdb_session(self, is_retry: bool = False) -> None:
        """
        确保TVDB会话存在。如果不存在或需要强制重新初始化，则进行初始化。
        :param is_retry: 是否重新初始化（例如token失效时）
        """
        # 第一次检查 (无锁)，提高性能，避免不必要锁竞争
        if not self.tvdb or is_retry:
            with self.__auth_lock:
                # 第二次检查 (有锁)，防止多个线程都通过第一次检查后重复初始化
                if not self.tvdb or is_retry:
                    self._initialize_tvdb_session(is_retry=is_retry)

    def _handle_tvdb_call(self, method_name: str, *args, **kwargs):
        """
        包裹 TVDB 调用，处理 token 失效情况并尝试重新初始化
        :param method_name: 要在 self.tvdb 实例上调用的方法的名称 (字符串)
        """
        try:
            self._ensure_tvdb_session()
            actual_method = getattr(self.tvdb, method_name)
            return actual_method(*args, **kwargs)
        except ValueError as e:
            if "Unauthorized" in str(e):
                logger.warning("TVDB Token 可能已失效，正在尝试重新登录...")
                try:
                    self._ensure_tvdb_session(is_retry=True)
                    actual_method = getattr(self.tvdb, method_name)
                    return actual_method(*args, **kwargs)
                except ConnectionError as conn_err:
                    logger.error(f"TVDB Token失效后重新登录失败: {conn_err}")
                    raise
            elif "NotFoundException" in str(e) or "ID not found" in str(e):
                logger.warning(f"TVDB 资源未找到 (调用 {method_name}): {e}")
                return None
            else:
                logger.error(f"TVDB 调用 ({method_name}) 时发生未处理的 ValueError: {str(e)}")
                raise
        except ConnectionError as e:
            logger.error(f"TVDB 连接会话错误: {str(e)}")
            raise
        except AttributeError as e:
            logger.error(f"TVDB 实例上没有方法 '{method_name}': {e}")
            raise
        except Exception as e:
            logger.error(f"TVDB 调用时发生未知错误: {str(e)}", exc_info=True)
            raise

    @staticmethod
    def get_name() -> str:
        return "TheTvDb"

    @staticmethod
    def get_type() -> ModuleType:
        """
        获取模块类型
        """
        return ModuleType.MediaRecognize

    @staticmethod
    def get_subtype() -> MediaRecognizeType:
        """
        获取模块子类型
        """
        return MediaRecognizeType.TVDB

    @staticmethod
    def get_priority() -> int:
        """
        获取模块优先级，数字越小优先级越高，只有同一接口下优先级才生效
        """
        return 4

    def stop(self):
        with self.__auth_lock:
            self.tvdb = None

    def test(self) -> Tuple[bool, str]:
        """
        测试模块连接性
        """
        try:
            self._handle_tvdb_call("get_series", 81189)
            return True, ""
        except Exception as e:
            return False, str(e)

    def init_setting(self) -> Tuple[str, Union[str, bool]]:
        pass

    def tvdb_info(self, tvdbid: int) -> Optional[dict]:
        """
        获取TVDB信息
        :param tvdbid: int
        :return: TVDB信息
        """
        try:
            logger.info(f"开始获取TVDB信息: {tvdbid} ...")
            return self._handle_tvdb_call("get_series_extended", tvdbid)
        except Exception as err:
            logger.error(f"获取TVDB信息失败: {str(err)}")
            return None

    def search_tvdb(self, title: str) -> list:
        """
        用标题搜索TVDB剧集
        :param title: 标题
        :return: TVDB信息
        """
        try:
            logger.info(f"开始用标题搜索TVDB剧集: {title} ...")
            res = self._handle_tvdb_call("search", title)
            if res is None:
                return []
            if not isinstance(res, list):
                logger.warning(f"TVDB 搜索 '{title}' 未返回列表：{type(res)}")
                return []
            return [item for item in res if isinstance(item, dict) and item.get("type") == "series"]
        except Exception as err:
            logger.error(f"用标题搜索TVDB剧集失败 ({title}): {str(err)}")
            return []

    def clear_cache(self):
        """
        清除缓存
        """
        logger.info(f"开始清除{self.get_name()}缓存 ...")
        if tvdb := self.tvdb:
            tvdb.clear_cache()
        logger.info(f"{self.get_name()}缓存清除完成")
