import json
import time
from base64 import b64encode
from functools import reduce
from hashlib import md5
from random import choice, randint
from typing import Any, Dict, List, Optional
from urllib.parse import urlencode


class DouYinHelper(object):
    ttwid_list = [
        '1|G3wy_-RdLJnfG5P9zAcP54OM8_nTLZVrJxNi1lPzdmg|1693558867|5e43c47a424e939aaf7193b096e3c6f2274982ee64e9608c99c54d2a43982aca'
    ]

    @classmethod
    def _0x30492c(cls, x: bytes, y: bytes, f: Optional[List[int]] = None) -> bytes:
        """RC4加密, 可以用Crypto.Cipher.ARC4替代"""
        c = 0
        d = [i for i in range(256)]
        for b in range(256):
            c = (c + d[b] + x[b % len(x)]) % 256
            e = d[b]
            d[b] = d[c]
            d[c] = e
        t, c = 0, 0

        if not f:
            f = []
        for i in range(len(y)):
            t = (t + 1) % 256
            c = (c + d[t]) % 256
            e = d[t]
            d[t] = d[c]
            d[c] = e
            f.append(y[i] ^ d[(d[t] + d[c]) % 256])
        return bytes(f)

    @classmethod
    def _0x485470(cls, a: str) -> List[int]:
        _0x583e81 = [0] * 103
        for i in range(10):
            _0x583e81[i + 48] = i
        for j in range(10, 16):
            _0x583e81[j + 87] = j

        b = len(a) >> 1
        e = b << 1
        d = [0] * b
        c = 0
        for f in range(0, e, 2):
            d[c] = _0x583e81[ord(a[f])] << 4 | _0x583e81[ord(a[f + 1])]
            c += 1
        return d

    @classmethod
    def calc_x_bogus(cls, ua: str, query: str, data: Optional[Dict[str, Any]] = None) -> str:
        """计算X_Bogus参数"""
        query = query.encode()
        for _ in range(2):
            query = md5(query).hexdigest()
            query = bytes([int(query[i:i + 2], 16) for i in range(0, len(query), 2)])

        data = json.dumps(data, separators=(',', ':'), ensure_ascii=False).encode() if data else b''
        for _ in range(2):
            data = md5(data).hexdigest()
            data = bytes([int(data[i:i + 2], 16) for i in range(0, len(data), 2)])

        a = b'\x00\x01\x0e'
        ua = b64encode(cls._0x30492c(a, ua.encode())).decode()
        ua = md5(ua.encode()).hexdigest()
        ua = cls._0x485470(ua)

        t = int(time.time())
        fp = 2421646185  # 真实的canvas指纹
        arr1 = [
            64,
            1 / 256,
            1 % 256,
            14,
            query[14],
            query[15],
            data[14],
            data[15],
            ua[14],
            ua[15],
            t >> 24 & 255,
            t >> 16 & 255,
            t >> 8 & 255,
            t >> 0 & 255,
            fp >> 24 & 255,
            fp >> 16 & 255,
            fp >> 8 & 255,
            fp >> 0 & 255,
        ]
        reduce_num = reduce(lambda x, y: int(x) ^ int(y), arr1)
        arr1.append(reduce_num)
        arr2 = [int(arr1[i]) for i in range(len(arr1))]

        garble = cls._0x30492c(b'\xff', bytes(arr2), [2, 255])
        m = 'Dkdpgh4ZKsQB80/Mfvw36XI1R25-WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe='
        xb = ''
        for i in range(0, len(garble), 3):
            a, b, c = garble[i], garble[i + 1], garble[i + 2]
            base_num = c | b << 8 | a << 16
            c1 = m[(base_num & 16515072) >> 18]
            c2 = m[(base_num & 258048) >> 12]
            c3 = m[(base_num & 4032) >> 6]
            c4 = m[(base_num & 63)]
            xb += ''.join([c1, c2, c3, c4])
        return xb

    @classmethod
    def get_full_query(cls, ua: str, extra_data: Dict[str, Any]) -> Dict[str, Any]:
        ms_token = b64encode(bytes([randint(0, 255) for _ in range(94)])).decode()
        ms_token = ms_token.replace('+', '-').replace('/', '_').rstrip('=')

        data = {
            'device_platform': 'webapp',
            'aid': '6383',
            'channel': 'channel_pc_web',
            'pc_client_type': '1',
            'version_code': '190500',
            'version_name': '19.5.0',
            'cookie_enabled': 'true',
            'platform': 'PC',
            'msToken': ms_token,
        }
        data.update(extra_data)
        query = urlencode(data, safe='=')
        x_bogus = cls.calc_x_bogus(ua=ua, query=query, data=None)
        data.update({'X-Bogus': x_bogus})
        return data

    @classmethod
    def get_cookie(cls):
        ttwid = choice(cls.ttwid_list)
        return f'ttwid={ttwid}'
