import base64
import ctypes
import hashlib
import hmac
import json
import random
import time
import traceback
import uuid
from urllib.parse import quote

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('step2.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():
    # PX11431 o111ooo1
    ts: str = ''
    # PX15454 o111oooo
    num1: int = ''
    # PX11701 111o1o
    num2: str = ''
    # PX11701 111o1o
    str1: str = ''
    vid: str = ''
    sid: str = ''
    cts: str = ''

    session = tls_client.Session(client_identifier='chrome_117')
    # print(session.get('https://tls.peet.ws/api/all').json())
    # session = requests.Session()
    session.proxies = {
        'http': 'http://127.0.0.1:7890',
        'https': 'http://127.0.0.1:7890',
    }

    session.proxies = {
        'http': "http://unfflcc:76cc14-47b8dd-1f8ace-827836-0c740e@usa.rotating.proxyrack.net:9000",
        'https': "http://unfflcc:76cc14-47b8dd-1f8ace-827836-0c740e@usa.rotating.proxyrack.net:9000",
    }
    # session.proxies = {
    #     'http': 'http://user-uni001-region-us-sessid-1111-sesstime-5-keep-true:q39CEBTs5A5YQXor@pr.roxlabs.cn:4600',
    #     'https': 'http://user-uni001-region-us-sessid-1111-sesstime-5-keep-true:q39CEBTs5A5YQXor@pr.roxlabs.cn:4600',
    # }
    prox = 'http://b6b6b6b6-zone-custom-region-us-session-130oj36t4-sessTime-5:bpoMDFG5oD@na.ipidea.io:2333'

    prox = 'http://user-uni001-region-us-sessid-1111-sesstime-5-keep-true:q39CEBTs5A5YQXor@pr.roxlabs.cn:4600'
    prox = 'http://unfflcc:76cc14-47b8dd-1f8ace-827836-0c740e@usa.rotating.proxyrack.net:9000'
    prox = f'http://unfflcc:76cc14-47b8dd-1f8ace-827836-0c740e@usa.rotating.proxyrack.net:{random.randint(10000, 13500)}'
    prox = 'http://127.0.0.1:7890'
    session.proxies = {
        'http': prox,
        'https': prox,
    }
    tag = "v8.7.2"
    ft = "317"
    appId = "PXVb73hTEg"

    def __init__(self, target_url):

        self.pxHeaders = {
            "authority": "collector-pxvb73hteg.px-cloud.net",
            "accept": "*/*",
            "accept-language": "en-US,en;q=0.9",
            "cache-control": "no-cache",
            "content-type": "application/x-www-form-urlencoded",
            "origin": "https://www.flyfrontier.com",
            "pragma": "no-cache",
            "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": "cross-site",
            "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"
        }
        self.pxUrl = f"https://collector-{self.appId}.px-cloud.net/api/v2/collector"
        self.target_url = target_url
        self.log = logger.bind(module_name='px')

    def inc(self):
        self.seq += 1
        self.rsc += 1
        self.log.info(f'seq: {self.seq} rsc:{self.rsc}')

    def do_collector(self, data):
        if isinstance(data, list):
            data_str = json.dumps(data, separators=(',', ':'))
        else:
            data_str = data
        self.log.info(f'data_str {data_str}')

        payload = ctx.call('encodePayload', data_str, self.uuid, self.ts)
        self.log.info(f'payload {payload}')
        pc = self._gen_pc(data_str, f'{self.uuid}:{self.tag}:{self.ft}')
        body = {
            "payload": payload,
            "appId": self.appId,
            "tag": self.tag,
            "uuid": self.uuid,
            "ft": self.ft,
            "seq": self.seq,
            "en": "NTA",
            "pc": pc,
            "rsc": self.rsc
        }
        body = self.update_sid_vid(body)
        response = self.session.post(self.pxUrl, headers=self.pxHeaders, data=body)
        self.log.info(f'collector1 {response.text}')
        resp = response.json()
        self.parse_resp(resp)

        self.inc()


    def run(self):
        self._init()
        self.seq = 0
        self.rsc = 1
        self._step_1()
        self.seq += 1
        self._step_11()

        # time.sleep(1.5)

        self._step_2(True)
        # time.sleep(2)
        self._step_2(False)
        # if self.session.cookies.get_dict().get('_px2', None) == None:
        self._step_3()
        time.sleep(2)

        # time.sleep(2)
        self.__token()

    def _init(self):

        res = self.session.get(
            self.target_url,
            headers={
                "authority": "www.flyfrontier.com",
                "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
                "accept-language": "en-US,en;q=0.9",
                "cache-control": "no-cache",
                "pragma": "no-cache",
                "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": "document",
                "sec-fetch-mode": "navigate",
                "sec-fetch-site": "none",
                "sec-fetch-user": "?1",
                "upgrade-insecure-requests": "1",
                "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",
            }, allow_redirects=True)
        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}")
        # with open('index.html', 'w') as f:
        #     f.write(res.text)

    def __uuid(self):
        return str(uuid.uuid4())
        return "b8a39670-7866-11ee-9655-63a4f05cd5d1"
        # return ctxStep2.call('genUuid')

        # 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 _step_1(self):
        t1 = int(time.time() * 1000)

        t2 = t1 + random.randint(3, 12)
        self.startTs = t1
        self.startTs2 = t2
        _uuid = self.__uuid()
        self.uuid = _uuid
        # "[{"t":"PX12095","d":{"PX11645":self.target_url,"PX12207":0,"PX12458":"MacIntel","PX11902":0,"PX11560":3781,"PX12248":3600,
        # "PX11385":1698649702900,"PX12280":1698649702913,"PX11496":"1ce53d20-76f3-11ee-8971-a3e00c89b2ff","PX12564":null,"PX12565":-1,"PX11379":false}}]"
        data = [{"t": "PX12095",
                 "d": {"PX11645": self.target_url, "PX12207": 0, "PX12458": "MacIntel", "PX11902": 0,
                       "PX11560": random.randint(24846, 24846 * 2), "PX12248": 3600, "PX11385": t1, "PX12280": t2,
                       "PX11496": _uuid, "PX12564": None, "PX12565": -1,
                       "PX11379": True
                       }}]
        self.do_collector(data)

    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, t, e):
        self.log.info(f'genpc t: {t}')
        self.log.info(f'genpc e: {e}')
        # ctx = execjs.compile(jsCode)
        h = hmac.new(e.encode(), t.encode(), hashlib.md5)
        t = h.hexdigest()
        e = ""
        b = ""
        for i in t:
            a = ord(i)
            if a >= 48 and a <= 57:
                e += i
            else:
                b += str(a % 10)

        r = e + b
        a2 = ""
        for i in range(0, len(r), 2):
            a2 += r[i]
        return a2

    def __token(self):
        # cks = []
        # for k, v in self.session.cookies.get_dict().items():
        #     cks.append(f'{k}={v}')
        # # resp = requests.post('http://127.0.0.1:59001/akamai/gen', json={
        self.prox = f'http://user-uni001-region-us-sessid-{random.randint(1000, 5000)}-sesstime-5-keep-true:q39CEBTs5A5YQXor@pr.roxlabs.cn:4600'
        # self.prox = f'http://unfflcc:76cc14-47b8dd-1f8ace-827836-0c740e@usa.rotating.proxyrack.net:{random.randint(10000, 13500)}'
        cks = []
        for k, v in self.session.cookies.get_dict().items():
            cks.append(f'{k}={quote(v)}')
        print('; '.join(cks))
        url = 'https://booking.flyfrontier.com/Flight/InternalSelect?o1=DEN&d1=LAS&dd1=2023-11-30&ADT=1&umnr=false&mon=true'
        response = requests.post('http://52.52.23.116/tls/forward',
                                 json=dict(url=url, headers={
                                     "authority": "booking.flyfrontier.com",
                                     "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
                                     "accept-language": "en-US,en;q=0.9",
                                     "cache-control": "no-cache",
                                     "pragma": "no-cache",
                                     "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": "document",
                                     "sec-fetch-mode": "navigate",
                                     "sec-fetch-site": "none",
                                     "sec-fetch-user": "?1",
                                     "upgrade-insecure-requests": "1",
                                     "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",
                                     "cookie": '; '.join(cks)
                                 }, method="GET", proxy=self.prox,
                                           debug=True
                                           # ,allow_redirects=True
                                           ))
        response = response.json()
        print(response)
        if response['status_code'] == 302:
            print(response['text'])
        print(response['status_code'])

    def _step_2(self, nn):

        self.log.info(self.uuid)
        # sid, vid,ts, num1 ,num2,str1,  uuid
        data_str = ctxStep2.call('genPayload2', self.target_url, self.sid, self.vid, self.ts, self.num1, self.num2,
                                 self.str1, self.uuid,
                                 self.startTs, self.startTs2, nn)
        self.do_collector(data_str)

    def _decode_resp(self):
        a = int(self.tag.replace('v', '').replace('.', ''))
        return a % 128

    def _step_11(self):
        data = [{"t": "PX12167", "d": {"PX11648": True, "PX11379": True, "PX11645": self.target_url}}]
        self.do_collector(data)

    def update_sid_vid(self, data: dict):
        if self.session.cookies.get_dict().get('_pxhd', None) != None:
            data.update({
                'pxhd': self.session.cookies.get_dict().get('_pxhd', None)
            })
        if self.vid != "":
            data.update({
                'vid': self.vid
            })
        if self.cts != "":
            data.update({
                'cts': self.cts
            })
        if self.sid != "":
            data.update({
                'sid': self.sid
            })
        return data

    def _step_3(self):

        # sid, vid,ts, num1 ,num2,str1,  uuid
        data_str = ctxStep2.call('genPayload3', self.target_url, self.sid, self.vid, self.ts, self.num1, self.num2,
                                 self.str1, self.uuid)
        self.log.info(f'data_str3 {data_str}')

        self.do_collector(data_str)

    def parse_resp(self, resp):
        if 'ob' in resp:
            cookies = self.encrypt(base64.b64decode(resp['ob'].encode()), self._decode_resp()).decode()
            cks = cookies.split('~~~~')
            self.log.info(json.dumps(cks, indent=True))
            for i in cks:
                tmp = i.split('|')
                if tmp[0] == 'o111ooo1':
                    self.ts = tmp[1]
                if tmp[0] == 'o111oooo':
                    self.num1 = int(tmp[1])
                if tmp[0] == '111o1o':
                    self.num2 = tmp[1]

                if tmp[0] == '1oooo1':
                    self.str1 = tmp[1]
                if tmp[0] == 'o1oo1o':
                    self.vid = tmp[1]
                    self.session.cookies.update({
                        '_pxvid': self.vid
                    })
                if tmp[0] == 'o11o11':
                    self.sid = tmp[1]
                if tmp[0] == '11o111':
                    self.cs = tmp[1]
                if tmp[0] == 'o11o11oo':
                    self.cts = tmp[1]
                    self.session.cookies.update({
                        'pxcts': self.cts
                    })

                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]
                    })


if __name__ == '__main__':
    url = "https://booking.flyfrontier.com/Flight/InternalSelect?o1=DEN&d1=LAS&dd1=2023-11-30&ADT=1&umnr=false&mon=true"
    url = "https://www.flyfrontier.com"
    # while True:
    try:
        px = PxBypass(url)
        t1 = '[{"t":"PX12095","d":{"PX11645":"https://www.flyfrontier.com/","PX12207":0,"PX12458":"MacIntel","PX11902":0,"PX11560":2839,"PX12248":3600,"PX11385":1700206931539,"PX12280":1700206931543,"PX11496":"d17e7c10-851c-11ee-8b6a-39050a20a12c","PX12564":null,"PX12565":-1,"PX11379":false}}]'

        t2 = 'd17e7c10-851c-11ee-8b6a-39050a20a12c:v8.7.2:317'
        # print(px._gen_pc(t1, t2))
        px.run()
    # except KeyError:
    #     pass
    except:
        traceback.print_exc()
    # time.sleep(1)
