import json
from hashlib import md5
from typing import Any, Dict
from urllib.parse import urlencode

import httpx
from httpx import ConnectTimeout, ReadTimeout
from nonebot.log import logger
from httpx._types import URLTypes


class RequestError(Exception):
    def __init__(self, code, message=None, data=None):
        self.code = code
        self.message = message
        self.data = data

    def __repr__(self):
        return f"<RequestError code={self.code} message={self.message}>"

    def __str__(self):
        return self.__repr__()


class BiliReq:
    def __init__(self):
        self.appkey = "4409e2ce8ffd12b8"
        self.appsec = "59b43e04ad6965f34319062b478f83dd"
        self.default_headers = {
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)\
             AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88\
              Safari/537.36 Edg/87.0.664.60",
            "Referer": "https://www.bilibili.com/",
        }
        # self.login = Config.get_login()
        self.proxies: Dict[URLTypes, Any] = {"all://": None}

    # TODO 制作一个装饰器捕获请求时的异常并用更友好的方式打印出来
    async def request(self, method, url, **kw) -> Dict:
        async with httpx.AsyncClient(proxies=self.proxies) as client:
            try:
                r = await client.request(method, url, **kw)
                r.encoding = "utf-8"
                res: Dict = r.json()
            except ConnectTimeout:
                logger.error(f"连接超时（{url}）")
                raise
            except ReadTimeout:
                logger.error(f"接收超时（{url}）")
                raise
            except Exception as e:
                logger.error(f"未知错误（{url}）\n{e}")
                raise

            if res["code"] != 0:
                raise RequestError(
                    code=res["code"], message=res["message"], data=res.get("data")
                )
            return res["data"]

    async def get(self, url, **kw):
        return await self.request("GET", url, **kw)

    async def post(self, url, **kw):
        return await self.request("POST", url, **kw)

    async def get_info(self, uid):
        url = f"https://api.bilibili.com/x/space/acc/info?mid={uid}"
        return await self.get(url, headers=self.default_headers)

    async def get_user_dynamics(self, uid):
        # need_top: {1: 带置顶, 0: 不带置顶}
        url = (
            "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history"
            f"?host_uid={uid}&offset_dynamic_id=0&need_top=0"
        )
        return await self.get(url, headers=self.default_headers)

        # async def get_new_dynamics(self):
        #     url = 'https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/dynamic_new'
        #     params = {'type_list': 268435455, 'access_key': self.login['access_token']}
        # return await self.get(url, params=params)

    # async def get_history_dynamics(self, offset_id):
    #     url = 'https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/dynamic_history'
    #     params = {
    #         'type_list': 268435455,
    #         'access_key': self.login['access_token'],
    #         'offset_dynamic_id': offset_id
    #     }
    #     return await self.get(url, params=params)

    async def get_live_list(self, uids):
        """根据 UID 获取直播间信息列表"""

        url = "https://api.live.bilibili.com/room/v1/Room/get_status_info_by_uids"
        data = {"uids": uids}
        return await self.post(url, headers=self.default_headers, data=json.dumps(data))

    # async def get_is_live_list(self):
    #     url = 'https://api.live.bilibili.com/xlive/app-interface/v1/relation/liveAnchor'
    #     params = {'access_key': self.login['access_token']}
    #     return await self.get(url, params=params)

    async def get_live_info(self, uid):
        url = f"https://api.live.bilibili.com/room/v1/Room/getRoomInfoOld?mid={uid}"
        return await self.get(url, headers=self.default_headers)

    def _sign(self, params):
        """获取 params 的 sign 值"""

        items = sorted(params.items())
        return md5(f"{urlencode(items)}{self.appsec}".encode("utf-8")).hexdigest()
