import hashlib
import json
import time
from enum import Enum
from cacheout import LFUCache
from tornado.httputil import url_concat
from lib.client import HttpClient
from lib.config_manager import ConfigMangaer,ConfigModuleEnum

clientCache = LFUCache() #运行时系统缓存
CM = ConfigMangaer()
configByPorxy = CM.load(ConfigModuleEnum.PARXY_MODULE)
configByAuth = CM.load(ConfigModuleEnum.AUTH_MODULE)


def md5(_str):
    # 创建md5对象
    hl = hashlib.md5()
    # Tips
    # 此处必须声明encode
    # 若写法为hl.update(str)  报错为： Unicode-objects must be encoded before hashing
    hl.update(_str.encode(encoding='utf-8'))
    return hl.hexdigest()

class ProxyOAuthV1(object):
    def __init__(self) -> None:
        self.httpClient = None
        self.signParam = {}
    
    def signatuer(self):
        # 签名认证
        keys = sorted(self.signParam.keys())
        key_str = "".join(keys)
        values_str = ""
        for key in keys:
            values_str += str(self.signParam[key])
        s = "{}{}".format(
            key_str,
            values_str.replace('\'', '').replace(' ', '').replace('\"', ''))
        # 加密
        signature_str1 = md5(s)
        # 加盐加密na
        signature_str = md5("NCSS-SASH-{}".format(signature_str1))
        return signature_str


    async def post(self, url, body, headers={}, timeOut=1):
        if not self.httpClient:
            raise RuntimeError("缺少http请求对象")
        signStr = self.signatuer()
        headers.update({"Authorization":f"Bearer {signStr}"})
        r = await self.httpClient.post(url, body, headers, timeOut)
        return r
    
    async def get(self,url, param, headers={}, timeOut=1):
        if not self.httpClient:
            raise RuntimeError("缺少http请求对象")
        signStr = self.signatuer()
        headers.update({"Authorization":f"Bearer {signStr}"})
        r = await self.httpClient.get(url, param, headers, timeOut)
        return r
    

class ProxyOAuthV2(ProxyOAuthV1):
    def __init__(self) -> None:
        super(ProxyOAuthV2,self).__init__()
        self.clientId = ""
        self.clientSecret = ""
        self.token = ""
        self.expiresAt = 0
        self.getAuthParamUrl = f"{configByAuth['AUTH_PLATFORM_PROTOCOL']}://{configByAuth['AUTH_PLATFORM_HOST']}:{configByAuth['AUTH_PLATFORM_PORT']}/api/v1/base/platform/dev/login/"

    async def RequestInit(self):
        self.clientId = clientCache.get("clientId")
        self.clientSecret = clientCache.get("clientSecret")
        self.expiresAt = clientCache.get("expiresAt")
        self.token = clientCache.get("token")
        if (not (self.clientId and self.clientSecret and self.expiresAt and self.token)) or (int(time.time()) >= self.expiresAt):
            login = {
                "account":configByAuth["AUTH_ACCOUNT"],
                "password":configByAuth["AUTH_PASSWORD"]
            }
            try:
                r = await self.httpClient.post(self.getAuthParamUrl,login,{"Content-Type":"application/json"},1)
            except Exception as e:
                raise RuntimeError(f"请求授权服务器失败:{e}")
            data = json.loads(str(r.body, encoding='utf-8'))
            assert data["err_code"] == 0, f"{data['err_code']};MSG:{data['err_msg']}"
            loginInfo = data.get("data")
            if not loginInfo:
                raise RuntimeError("登录权限信息格式返回不正确")
            loginRes = loginInfo.get("login_res")
            if loginRes != 2:
                raise RuntimeError(f"登录失败:{loginRes}")
            self.clientId = loginInfo.get("client_id")
            self.clientSecret = loginInfo.get("client_secret")
            self.token = loginInfo.get("token")
            self.expiresAt = loginInfo.get("expires_at")
            clientCache.set_many({"clientId":self.clientId,"clientSecret":self.clientSecret,"expiresAt":self.expiresAt,"token":self.token})


    async def post(self, url, body, headers={}, timeOut=1):
        if not self.httpClient:
            raise RuntimeError("缺少http请求对象")
        await self.RequestInit()
        signStr = self.signatuer()
        headers.update(
            {
                "Authorization":f"Bearer {signStr}",
                "client-id" :self.clientId,
                "client-secret":self.clientSecret,
                "token":self.token
            }
        )
        r = await self.httpClient.post(url, body, headers, timeOut)
        return r
    

    async def get(self, url, param, headers={}, timeOut=1):
        if not self.httpClient:
            raise RuntimeError("缺少http请求对象")
        await self.RequestInit()
        signStr = self.signatuer()
        headers.update(
            {
                "Authorization":f"Bearer {signStr}",
                "client-id" :self.clientId,
                "client-secret":self.clientSecret,
                "token":self.token
            }
        )
        r = await self.httpClient.get(url, param, headers, timeOut)
        return r
    

class OAuthModeEnum(Enum):
    V1 = "v1"
    V2 = "v2"

class Proxy(object):
    def __init__(self,proxyOAuth=OAuthModeEnum.V1.value):
        if proxyOAuth == OAuthModeEnum.V2.value:self.proxyOAuth = ProxyOAuthV2()
        if proxyOAuth == OAuthModeEnum.V1.value:self.proxyOAuth = ProxyOAuthV1()
        self._isErr = False
        self._errMsg = ""
        self._host = ""
        self._port = 000
        self._timeOut = 3

    @property
    def host(self):
        return self._host

    @host.setter
    def host(self, value):
        self._host = value

    @property
    def port(self):
        return self._port

    @port.setter
    def port(self, value):
        if not isinstance(value, int):
            return TypeError(f"端口必须是一个int:{value}")
        self._port = value

    @property
    def isErr(self):
        return self._isErr

    @property
    def errMsg(self):
        return self._errMsg

    def __err(self, errMsg):
        self._isErr = True
        self._errMsg = str(errMsg)

class ProxyDataPlatform(Proxy):
    def __init__(self,proxyOAuth=OAuthModeEnum.V1.value):
        super(ProxyDataPlatform, self).__init__(proxyOAuth)
        self._protocol = configByPorxy["PLATFORM_PROTOCOL"]
        self._host = configByPorxy["PLATFORM_HOST"]
        self._port = configByPorxy["PLATFORM_PORT"]
        self._headers = {
            "Content-Type":"application/json",
            "Dev-Type":"Terminal"
        }

    @property
    def protocol(self):
        return

    @protocol.setter
    def protocol(self, value):
        if value not in ["http", "https"]:
            raise ValueError(f"代理协议类型不合法:{value}")
        self._protocol = value

    def _json(self, r):
        if r.code != 200:
            raise f"网络状态异常:{r.code}"
        try:
            data = json.loads(str(r.body, encoding='utf-8'))
        except Exception as e:
            raise RuntimeError(f"请求数据转换异常;{str(e)};{r.body}")
        assert data["err_code"] == 0, f"{data['err_code']};MSG:{data['err_msg']}"
        return data["data"]

    def _err(self, errMsg):
        self._isErr = True
        self._errMsg = str(errMsg)

    async def post(self, url, body, headers={}, timeOut=1):
        client = HttpClient()
        self.proxyOAuth.httpClient = client
        self.proxyOAuth.signParam = body
        url = url_concat(f"{self._protocol}://{self._host}:{self._port}{url}", dict(_time=str(int(time.time()))))
        self._headers.update(headers)
        try:
            r = await self.proxyOAuth.post(url,body,self._headers,timeOut)
            return self._json(r)
        except Exception as e:
            self._err(e)

        
    async def get(self, url, param, headers={}, timeOut=1):
        param["_time"] = int(time.time())
        client = HttpClient()
        self.proxyOAuth.httpClient = client
        self.proxyOAuth.signParam = param
        url = f"{self._protocol}://{self._host}:{self._port}{url}"
        self._headers.update(headers)
        try:
            r = await self.proxyOAuth.get(url, param, self._headers, timeOut)
            return self._json(r)
        except Exception as e:
            self._err(e)