from typing import Tuple, List, Optional

from app.core.config import settings
from app.db.systemconfig_oper import SystemConfigOper
from app.log import logger
from app.schemas import MediaType
from app.utils.http import RequestUtils, AsyncRequestUtils
from app.utils.string import StringUtils


class YemaSpider:
    """
    YemaPT API
    """
    _indexerid = None
    _domain = None
    _name = ""
    _proxy = None
    _cookie = None
    _ua = None
    _size = 40
    _searchurl = "%sapi/torrent/fetchOpenTorrentList"
    _downloadurl = "%sapi/torrent/download?id=%s"
    _pageurl = "%s#/torrent/detail/%s/"
    _timeout = 15

    # 分类
    _movie_category = [4]
    _tv_category = [5, 13, 14, 17, 15, 6, 16]

    # 标签 https://wiki.yemapt.org/developer/constants
    _labels = {
        "1": "禁转",
        "2": "首发",
        "3": "官方",
        "4": "自制",
        "5": "国语",
        "6": "中字",
        "7": "粤语",
        "8": "英字",
        "9": "HDR10",
        "10": "杜比视界",
        "11": "分集",
        "12": "完结",
    }

    @classmethod
    def get_search_page_size(cls, keyword: Optional[str] = None) -> Optional[int]:
        """
        获取搜索接口单页容量。
        """
        return cls._size

    def __init__(self, indexer: dict):
        self.systemconfig = SystemConfigOper()
        if indexer:
            self._indexerid = indexer.get('id')
            self._domain = indexer.get('domain')
            self._searchurl = self._searchurl % self._domain
            self._name = indexer.get('name')
            if indexer.get('proxy'):
                self._proxy = settings.PROXY
            self._cookie = indexer.get('cookie')
            self._ua = indexer.get('ua')
            self._timeout = indexer.get('timeout') or 15

    def __get_params(self, keyword: str = None, page: Optional[int] = 0) -> dict:
        """
        获取搜索参数
        """
        params = {
            "pageParam": {
                "current": page + 1,
                "pageSize": self._size,
                "total": self._size
            },
            "sorter": {}
        }
        if keyword:
            params.update({
                "keyword": keyword,
            })
        return params

    def __parse_result(self, results: List[dict]) -> List[dict]:
        """
        解析搜索结果
        """
        torrents = []
        if not results:
            return torrents

        for result in results:
            category_value = result.get('categoryId')
            if category_value in self._tv_category:
                category = MediaType.TV.value
            elif category_value in self._movie_category:
                category = MediaType.MOVIE.value
            else:
                category = MediaType.UNKNOWN.value
                pass

            torrentLabelIds = result.get('tagList', []) or []
            torrentLabels = []
            for labelId in torrentLabelIds:
                if self._labels.get(labelId) is not None:
                    torrentLabels.append(self._labels.get(labelId))
                    pass
                pass
            torrent = {
                'title': result.get('showName'),
                'description': result.get('shortDesc'),
                'enclosure': self.__get_download_url(result.get('id')),
                'pubdate': StringUtils.unify_datetime_str(result.get('listingTime')),
                'size': result.get('fileSize'),
                'seeders': result.get('seedNum'),
                'peers': result.get('leechNum'),
                'grabs': result.get('completedNum'),
                'downloadvolumefactor': self.__get_downloadvolumefactor(result.get('downloadPromotion')),
                'uploadvolumefactor': self.__get_uploadvolumefactor(result.get('uploadPromotion')),
                'freedate': StringUtils.unify_datetime_str(result.get('downloadPromotionEndTime')),
                'page_url': self._pageurl % (self._domain, result.get('id')),
                'labels': torrentLabels,
                'category': category
            }
            torrents.append(torrent)

        return torrents

    def search(self, keyword: str,
               mtype: MediaType = None, page: Optional[int] = 0) -> Tuple[bool, List[dict]]:
        """
        搜索
        """

        res = RequestUtils(
            headers={
                "Content-Type": "application/json",
                "User-Agent": f"{self._ua}",
                "Accept": "application/json, text/plain, */*"
            },
            cookies=self._cookie,
            proxies=self._proxy,
            referer=f"{self._domain}",
            timeout=self._timeout
        ).post_res(url=self._searchurl, json=self.__get_params(keyword, page))
        if res and res.status_code == 200:
            results = res.json().get('data', []) or []
            return False, self.__parse_result(results)
        elif res is not None:
            logger.warn(f"{self._name} 搜索失败，错误码：{res.status_code}")
            return True, []
        else:
            logger.warn(f"{self._name} 搜索失败，无法连接 {self._domain}")
            return True, []

    async def async_search(self, keyword: str,
                           mtype: MediaType = None, page: Optional[int] = 0) -> Tuple[bool, List[dict]]:
        """
        异步搜索
        """
        res = await AsyncRequestUtils(
            headers={
                "Content-Type": "application/json",
                "User-Agent": f"{self._ua}",
                "Accept": "application/json, text/plain, */*"
            },
            cookies=self._cookie,
            proxies=self._proxy,
            referer=f"{self._domain}",
            timeout=self._timeout
        ).post_res(url=self._searchurl, json=self.__get_params(keyword, page))

        if res and res.status_code == 200:
            results = res.json().get('data', []) or []
            return False, self.__parse_result(results)
        elif res is not None:
            logger.warn(f"{self._name} 搜索失败，错误码：{res.status_code}")
            return True, []
        else:
            logger.warn(f"{self._name} 搜索失败，无法连接 {self._domain}")
            return True, []

    @staticmethod
    def __get_downloadvolumefactor(discount: str) -> float:
        """
        获取下载系数
        """
        discount_dict = {
            "free": 0,
            "half": 0.5,
            "none": 1
        }
        if discount:
            return discount_dict.get(discount, 1)
        return 1

    @staticmethod
    def __get_uploadvolumefactor(discount: str) -> float:
        """
        获取上传系数
        """
        discount_dict = {
            "none": 1,
            "one_half": 1.5,
            "double_upload": 2
        }
        if discount:
            return discount_dict.get(discount, 1)
        return 1

    def __get_download_url(self, torrent_id: str) -> str:
        """
        获取下载链接
        """
        return self._downloadurl % (self._domain, torrent_id)
