import base64
import ctypes
import json
import random
import re
import time

import execjs
import requests
import tls_client
from loguru import logger

with open('js/encodepayload.js', 'r', encoding='utf-8') as f:
    jsCode = f.read()
ctx = execjs.compile(jsCode)
ctxPC = execjs.compile(open('pc.js').read())
ctxStep2 = execjs.compile(open('captcha_step2.js').read())
ctxStep3 = execjs.compile(open('captcha_down.js').read())


def get_timestamp():
    return int(time.time() * 1000)


def unsinged_right_shift(x, y):
    x, y = ctypes.c_uint32(x).value, y % 32
    return ctypes.c_uint32(x >> y).value


class PxBypass():
    session = tls_client.Session(client_identifier='chrome_112')
    # session = requests.Session()
    session.proxies = {
        'http': 'http://127.0.0.1:8890',
        'https': 'http://127.0.0.1:8890',
    }
    code1 = ''
    code2 = ''
    tag = ''
    vid = ''
    uuid = ''
    token = ''
    sts = ''
    ci = ''
    cs = ''
    sid = ''
    cts = ''

    # session.proxies = {
    #     'http': "http://user-unifflcc-region-us:q39CEBTs5A5YQXor@pr.roxlabs.cn:4600",
    #     'https': "http://user-unifflcc-region-us:q39CEBTs5A5YQXor@pr.roxlabs.cn:4600",
    # }

    def __init__(self, target_url):
        self.target_url = target_url
        self.log = logger.bind(module_name='px')

    def run(self):
        self._init()

        self._step_1()
        self._step_2()
        self._step_3()
        c = self.__token()
        self.log.info(f'c: {c}')
        # if c == 403:
        #     self._step_1()
        #     self._step_2()
        #     self._step_3()

        self.log.info(self.session.cookies.get_dict())

    def _init(self):

        url = "https://www.spirit.com/api/prod-token/api/v1/token"
        data = {
            "applicationName": "dotRezWeb"
        }
        data = json.dumps(data, separators=(',', ':'))
        res = self.session.post(url, headers={
            "authority": "www.spirit.com",
            "accept": "application/json, text/plain, */*",
            "accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,ru;q=0.6",
            "cache-control": "no-cache",
            "content-type": "application/json",
            "ocp-apim-subscription-key": "dc6844776fe84b1c8b68affe7deb7916",
            "origin": "https://www.spirit.com",
            "pragma": "no-cache",
            "referer": "https://www.spirit.com/",
            "sec-ch-ua": "\"Google Chrome\";v=\"117\", \"Not;A=Brand\";v=\"8\", \"Chromium\";v=\"117\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "\"macOS\"",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "same-origin",
            "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",
            "x-dtpc": "2$217478397_28h17vUSRHUHMPCLARSKPAAKSGJSNTPCPCCFBR-0e0"
        }, data=data)

        self.log.info(f"初始化请求 res {res.status_code}")
        self.params = {}
        for k, v in self.session.cookies.items():
            self.params[k] = v
        self.log.info(f"init cookie {self.params}")
        vids = re.findall("_pxVid = '(.+?)'", res.text)
        if len(vids) != 0:
            self.vid = vids[0]
            self.log.info(f'获取到vid {self.vid}')
        uuids = re.findall("_pxUuid = '(.+?)'", res.text)
        if len(uuids) != 0:
            self.uuid = uuids[0]
            self.log.info(f'获取到uuid {self.uuid}')
        with open('index.html', 'w') as f:
            f.write(res.text)

    def __uuid(self):
        r = ["00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", "10", "11",
             "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", "20", "21", "22", "23",
             "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", "30", "31", "32", "33", "34", "35",
             "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", "40", "41", "42", "43", "44", "45", "46", "47",
             "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
             "5a", "5b", "5c", "5d", "5e", "5f", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b",
             "6c", "6d", "6e", "6f", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d",
             "7e", "7f", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f",
             "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", "a0", "a1",
             "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", "b0", "b1", "b2", "b3",
             "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", "c0", "c1", "c2", "c3", "c4", "c5",
             "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
             "d8", "d9", "da", "db", "dc", "dd", "de", "df", "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9",
             "ea", "eb", "ec", "ed", "ee", "ef", "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb",
             "fc", "fd", "fe", "ff"]
        bs = 0
        ys = [random.randint(10, 255) for _ in range(6)]
        # random.shuffle(ys)
        Gs = random.randint(1100, 9999)
        Ws = 0
        s = Gs
        h = get_timestamp()
        d = bs + 1
        m = int(h - Ws + (d - bs) / 1e4)

        Ws = h
        bs = d
        Gs = s
        h += 122192928e5
        h = int(h)
        f = []
        u = 0
        p = (1000 * (268435455 & h) + d) % 4294967296
        # p = 4180087664
        f[:3] = int(p).to_bytes(4, byteorder='big')
        Q = int(h / 4294967296) * 1000 & 268435455
        # Q = 32386116
        f.append(unsinged_right_shift(Q, 8) & 255)
        f.append(255 & Q)
        f.append(unsinged_right_shift(Q, 24) & 15 | 16)
        f.append(unsinged_right_shift(Q, 16) & 255)
        # s = 6488
        f.append(unsinged_right_shift(s, 8) | 128)
        f.append(255 & s)
        for i in ys:
            f.append(i)
        print(f)
        res = [r[f[0]], r[f[1]], r[f[2]], r[f[3]], '-', r[f[4]], r[f[5]], '-', r[f[6]], r[f[7]], '-', r[f[8]], r[f[9]],
               '-',
               r[f[10]], r[f[11]], r[f[12]], r[f[13]], r[f[14]], r[f[15]]]
        return ''.join(res)

    def process_ob(self, resp):
        m = {}
        if 'ob' in resp:
            cookies = self.encrypt(base64.b64decode(resp['ob'].encode()), 872 % 128).decode()
            print(cookies.split('~~~~'))
            for i in cookies.split('~~~~'):
                tmp = i.split('|')
                m[tmp[0]] = tmp[1]
                key = tmp[0]
                data = tmp[1:]
                if key == 'I00II0':
                    t = data[3].split('_')
                    self.token = t[0]
                    self.ci = t[1]
                if key == '0III0III':
                    self.sts = data[0]
                if key == 'I0I0I0':
                    self.hash = data[1]
                    self.hash2 = data[2]
                    self.hashv = data[3]
                if key == 'I0I0II':
                    self.sid = data[0]
                if key == 'I000I0':
                    self.vid = data[0]
                if key == 'I00III':
                    self.code2 = data[0]
                if key == 'IIII00':
                    self.tag = data[0]
                if key == '0III00I0':
                    self.cts = data[0]
                if key == '0III0II0':
                    self.code1 = data[0]
                if 'pxde' in i:
                    self.session.cookies.update({
                        '_pxde': tmp[3]
                    })
                elif '_px2' in i:
                    self.session.cookies.update({
                        '_px2': tmp[3]
                    })
                elif '31536000' in i:
                    self.session.cookies.update({
                        '_pxvid': tmp[1]
                    })

        else:
            for i in resp['do']:
                tmp = i.split('|')
                print(len(tmp), tmp)
                if len(tmp) == 6 and tmp[1] in ['_px', '_px3', '_pxde']:
                    self.session.cookies.update({
                        tmp[1]: tmp[3]
                    })
                elif len(tmp) in [3, 4] and tmp[1] in ['cts', 'vid']:
                    self.session.cookies.update({
                        tmp[0]: tmp[1]
                    })
        self.log.info(f'cookies1 {self.session.cookies.keys()}')
        self.m = m
        # self.token = m['I00II0']
        self.log.info(self.m)

    def _step_1(self):
        t1 = int(time.time() * 1000)
        self.init_time = t1
        t2 = t1 + random.randint(3, 12)
        if self.uuid == '':
            self.uuid = self.__uuid()
        data = [
            {
                "t": "PX12095",
                "d": {
                    "PX11645": "https://www.spirit.com/",
                    "PX12207": 0,
                    "PX12458": "MacIntel",
                    "PX11902": 0,
                    "PX11560": random.randint(8549, 8549 * 3),
                    "PX12248": 3600,
                    "PX11385": t1,
                    "PX12280": t2,
                    "PX11496": self.uuid,
                    "PX12564": None,
                    "PX12565": -1,
                    "PX11379": False,
                    "PX12307": "PX11745",
                    "PX11668": "pxhc",
                    "PX12348": False
                }
            }]

        data_str = json.dumps(data, separators=(',', ':'))

        self.log.info(f'data_str {data_str}')

        payload = ctx.call('encodePayload', data_str, self.uuid, '')
        self.log.info(f'payload {payload}')
        tag = "v8.7.2"
        ft = "317"
        pc = self.__gen_pc(data_str, f'{self.uuid}:{tag}:{ft}')
        print(pc)
        body = {
            "payload": payload,
            "appId": "PXkp4CLSb5",
            "tag": tag,
            "uuid": self.uuid,
            "ft": ft,
            "seq": "0",
            "en": "NTA",
            "pc": pc,
        }
        if self.vid != "":
            body.update({
                'vid': self.vid
            })

        print(body)
        url = "https://collector-PXkp4CLSb5.px-cloud.net/assets/js/bundle"
        headers = {
            "authority": "collector-PXkp4CLSb5.px-cloud.net",
            "accept": "*/*",
            "accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
            "cache-control": "no-cache",
            "content-type": "application/x-www-form-urlencoded",
            "origin": "https://www.spirit.com",
            "pragma": "no-cache",
            "referer": "https://www.spirit.com/",
            "sec-ch-ua": "\"Not/A)Brand\";v=\"99\", \"Google Chrome\";v=\"115\", \"Chromium\";v=\"115\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "\"Windows\"",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "cross-site",
            "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"
        }
        response = self.session.post(url, headers=headers, data=body)
        self.log.info(f'collector1 {response.text}')
        resp = response.json()
        self.process_ob(resp)

    def encrypt(self, data, param):
        n = bytearray()
        if isinstance(data, str):
            bytess = data.encode()
        else:
            bytess = data
        for i in bytess:
            n.append(i ^ param)
        return n

    def __gen_pc(self, data_str, param):
        # self.log.info(f'genpc {data_str} {param}')
        # ctx = execjs.compile(jsCode)
        return ctxPC.call('genPc', param, data_str)

    def __token(self):

        url = "https://www.spirit.com/api/prod-token/api/v1/token"
        data = {
            "applicationName": "dotRezWeb"
        }
        data = json.dumps(data, separators=(',', ':'))
        res = self.session.post(url, headers={
            "authority": "www.spirit.com",
            "accept": "application/json, text/plain, */*",
            "accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,ru;q=0.6",
            "cache-control": "no-cache",
            "content-type": "application/json",
            "ocp-apim-subscription-key": "dc6844776fe84b1c8b68affe7deb7916",
            "origin": "https://www.spirit.com",
            "pragma": "no-cache",
            "referer": "https://www.spirit.com/",
            "sec-ch-ua": "\"Google Chrome\";v=\"117\", \"Not;A=Brand\";v=\"8\", \"Chromium\";v=\"117\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "\"macOS\"",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "same-origin",
            "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",
            "x-dtpc": "2$217478397_28h17vUSRHUHMPCLARSKPAAKSGJSNTPCPCCFBR-0e0"
        }, data=data)

        self.log.info(f"c res {res.status_code}")
        self.log.info(f"c text {res.text}")

        return res.status_code

    def _step_2(self):
        self.log.info(self.m)
        # genPayload2(vid, sid, uuid, sts, code1, code2, tag)
        data_str = ctxStep2.call('genPayload2', self.vid, self.sid, self.uuid, self.sts, self.code1, self.code2,
                                 self.tag)

        self.log.info(f'data_str2 {data_str}')

        payload = ctx.call('encodePayload', data_str, self.uuid, self.sts)
        self.log.info(f'payload {payload}')
        tag = "v8.6.6"
        ft = "316"
        pc = self.__gen_pc(data_str, f'{self.uuid}:{tag}:{ft}')
        body = {
            "payload": payload,
            "appId": "PXkp4CLSb5",
            "tag": tag,
            "uuid": self.uuid,
            "ft": ft,
            "seq": "1",
            "en": "NTA",
            "cs": self.cs,
            "pc": pc,
            "sid": self.sid,
            "vid": self.vid,
            "cts": self.cts,
            "rsc": "1"
        }
        url = "https://collector-PXkp4CLSb5.px-cloud.net/assets/js/bundle"
        headers = {
            "authority": "collector-PXkp4CLSb5.px-cloud.net",
            "accept": "*/*",
            "accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
            "cache-control": "no-cache",
            "content-type": "application/x-www-form-urlencoded",
            "origin": "https://www.spirit.com",
            "pragma": "no-cache",
            "referer": "https://www.spirit.com/",
            "sec-ch-ua": "\"Not/A)Brand\";v=\"99\", \"Google Chrome\";v=\"115\", \"Chromium\";v=\"115\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "\"Windows\"",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "cross-site",
            "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"
        }
        response = self.session.post(url, headers=headers, data=body)
        self.log.info(f'collector2 {response.text}')
        resp = response.json()
        m = {}
        if 'ob' in resp:
            cookies = self.encrypt(base64.b64decode(resp['ob'].encode()), 866 % 128).decode()
            print(cookies.split('~~~~'))
            for i in cookies.split('~~~~'):
                tmp = i.split('|')
                m[tmp[0]] = tmp[1]
                if 'pxde' in i:
                    self.session.cookies.update({
                        '_pxde': tmp[3]
                    })
                elif '_px2' in i:
                    self.session.cookies.update({
                        '_px2': tmp[3]
                    })
                elif '31536000' in i:
                    self.session.cookies.update({
                        '_pxvid': tmp[1]
                    })

        else:
            for i in resp['do']:
                tmp = i.split('|')
                # print(len(tmp), tmp)
                if len(tmp) == 6 and tmp[1] in ['_px', '_px3', '_pxde']:
                    self.session.cookies.update({
                        tmp[1]: tmp[3]
                    })
                elif len(tmp) in [3, 4] and tmp[1] in ['cts', 'vid']:
                    self.session.cookies.update({
                        tmp[0]: tmp[1]
                    })
        self.log.info(f'cookies2 {self.session.cookies.keys()}')
        self.m = m
        self.log.info(self.m)

    def _step_3(self):
        # self.log.info(self.m)
        # self.log.info(self.uuid)
        # time.sleep(5)

        data_str = ctxStep3.call('genPayload', self.uuid, self.vid, self.hash, self.hash2, self.hashv, self.token,
                                 self.ci, self.init_time)

        self.log.info(f'data_str2 {data_str}')
        data_str = json.dumps(data_str)
        payload = ctx.call('encodePayload', data_str, self.uuid, self.sts)
        self.log.info(f'payload {payload}')
        tag = "v8.6.6"
        ft = "316"
        pc = self.__gen_pc(data_str, f'{self.uuid}:{tag}:{ft}')
        body = {
            "payload": payload,
            "appId": "PXkp4CLSb5",
            "tag": tag,
            "uuid": self.uuid,
            "ft": ft,
            "seq": "4",
            "en": "NTA",
            "cs": self.cs,
            "pc": pc,
            "sid": self.sid,
            "vid": self.vid,
            "ci": self.ci,
            "cts": self.cts,
            "rsc": "3"
        }
        url = "https://collector-PXkp4CLSb5.px-cloud.net/assets/js/bundle"
        headers = {
            "authority": "collector-PXkp4CLSb5.px-cloud.net",
            "accept": "*/*",
            "accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
            "cache-control": "no-cache",
            "content-type": "application/x-www-form-urlencoded",
            "origin": "https://www.spirit.com",
            "pragma": "no-cache",
            "referer": "https://www.spirit.com/",
            "sec-ch-ua": "\"Not/A)Brand\";v=\"99\", \"Google Chrome\";v=\"115\", \"Chromium\";v=\"115\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "\"Windows\"",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "cross-site",
            "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"
        }
        response = self.session.post(url, headers=headers, data=body)
        self.log.info(f'collector3 {response.text}')
        resp = response.json()
        m = {}
        if 'ob' in resp:
            cookies = self.encrypt(base64.b64decode(resp['ob'].encode()), 866 % 128).decode()
            print(cookies.split('~~~~'))
            for i in cookies.split('~~~~'):
                tmp = i.split('|')
                m[tmp[0]] = tmp[1]
                if 'pxde' in i:
                    self.session.cookies.update({
                        '_pxde': tmp[3]
                    })
                elif '_px2' in i:
                    self.session.cookies.update({
                        '_px2': tmp[3]
                    })
                elif '31536000' in i:
                    self.session.cookies.update({
                        '_pxvid': tmp[1]
                    })

        else:
            for i in resp['do']:
                tmp = i.split('|')
                # print(len(tmp), tmp)
                if len(tmp) == 6 and tmp[1] in ['_px', '_px3', '_pxde']:
                    self.session.cookies.update({
                        tmp[1]: tmp[3]
                    })
                elif len(tmp) in [3, 4] and tmp[1] in ['cts', 'vid']:
                    self.session.cookies.update({
                        tmp[0]: tmp[1]
                    })
        self.log.info(f'cookies2 {self.session.cookies.keys()}')
        self.m = m
        self.log.info(self.m)


if __name__ == '__main__':
    url = "https://booking.spirit.com/Flight/InternalSelect?o1=DEN&d1=LAS&dd1=2023-08-30&ADT=1&umnr=false&mon=true"
    px = PxBypass(url)
    px.run()
    print(unsinged_right_shift(275404960, 24) & 255)
