import base64
import datetime
import json
import math
import os
import random
import time

import requests
from Crypto.Cipher import AES

from dailycheckin import CheckIn


class Encrypt:
    def __init__(self, key, iv):
        self.key = key.encode("utf-8")
        self.iv = iv.encode("utf-8")

    def pkcs7padding(self, text):
        """明文使用PKCS7填充"""
        bs = 16
        length = len(text)
        bytes_length = len(text.encode("utf-8"))
        padding_size = length if (bytes_length == length) else bytes_length
        padding = bs - padding_size % bs
        padding_text = chr(padding) * padding
        self.coding = chr(padding)
        return text + padding_text

    def aes_encrypt(self, content):
        """AES加密"""
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        content_padding = self.pkcs7padding(content)
        encrypt_bytes = cipher.encrypt(content_padding.encode("utf-8"))
        result = str(base64.b64encode(encrypt_bytes), encoding="utf-8")
        return result

    def aes_decrypt(self, content):
        """AES解密"""
        cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
        content = base64.b64decode(content)
        text = cipher.decrypt(content).decode("utf-8")
        return text.rstrip(self.coding)


class IMAOTAI(CheckIn):
    name = "i茅台"

    def __init__(self, check_item):
        self.check_item = check_item
        self.RESERVE_RULE = 0
        self.mt_r = "clips_OlU6TmFRag5rCXwbNAQ/Tz1SKlN8THcecBp/"
        self.ITEM_MAP = {
            "10941": "53%vol 500ml贵州茅台酒（甲辰龙年）",
            "10942": "53%vol 375ml×2贵州茅台酒（甲辰龙年）",
            "10056": "53%vol 500ml茅台1935",
            "2478": "53%vol 500ml贵州茅台酒（珍品）",
        }
        self.ITEM_CODES = ["10941", "10942"]
        AES_KEY = "qbhajinldepmucsonaaaccgypwuvcjaa"
        AES_IV = "2018534749963515"
        self.encrypt = Encrypt(key=AES_KEY, iv=AES_IV)

        self.mt_version = json.loads(
            requests.get("https://itunes.apple.com/cn/lookup?id=1600482450").text
        )["results"][0]["version"]
        self.headers = {}
        self.header_context = """
MT-Lat: 28.499562
MT-K: 1675213490331
MT-Lng: 102.182324
Host: app.moutai519.com.cn
MT-User-Tag: 0
Accept: */*
MT-Network-Type: WIFI
MT-Token: 1
MT-Info: 028e7f96f6369cafe1d105579c5b9377
MT-Device-ID: 2F2075D0-B66C-4287-A903-DBFF6358342A
MT-Bundle-ID: com.moutai.mall
Accept-Language: en-CN;q=1, zh-Hans-CN;q=0.9
MT-Request-ID: 167560018873318465
MT-APP-Version: 1.3.7
User-Agent: iOS;16.3;Apple;?unrecognized?
MT-R: clips_OlU6TmFRag5rCXwbNAQ/Tz1SKlN8THcecBp/HGhHdw==
Content-Length: 93
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: application/json
userId: 2
"""

    def init_headers(
        self,
        user_id: str = "1",
        token: str = "2",
        lat: str = "29.83826",
        lng: str = "119.74375",
    ):
        for k in self.header_context.strip().split("\n"):
            temp_l = k.split(": ")
            dict.update(self.headers, {temp_l[0]: temp_l[1]})
        dict.update(self.headers, {"userId": user_id})
        dict.update(self.headers, {"MT-Token": token})
        dict.update(self.headers, {"MT-Lat": lat})
        dict.update(self.headers, {"MT-Lng": lng})
        dict.update(self.headers, {"MT-APP-Version": self.mt_version})

    def get_current_session_id(self):
        day_time = int(time.mktime(datetime.date.today().timetuple())) * 1000
        my_url = f"https://static.moutai519.com.cn/mt-backend/xhr/front/mall/index/session/get/{day_time}"
        responses = requests.get(my_url)
        if responses.status_code != 200:
            print(
                f"get_current_session_id : params : {day_time}, response code : {responses.status_code}, response body : {responses.text}"
            )
        current_session_id = responses.json()["data"]["sessionId"]
        dict.update(self.headers, {"current_session_id": str(current_session_id)})

    def get_map(self, lat: str = "28.499562", lng: str = "102.182324"):
        p_c_map = {}
        url = "https://static.moutai519.com.cn/mt-backend/xhr/front/mall/resource/get"
        headers = {
            "X-Requested-With": "XMLHttpRequest",
            "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0_1 like Mac OS X)",
            "Referer": "https://h5.moutai519.com.cn/gux/game/main?appConfig=2_1_2",
            "Client-User-Agent": "iOS;16.0.1;Apple;iPhone 14 ProMax",
            "MT-R": "clips_OlU6TmFRag5rCXwbNAQ/Tz1SKlN8THcecBp/HGhHdw==",
            "Origin": "https://h5.moutai519.com.cn",
            "MT-APP-Version": self.mt_version,
            "MT-Request-ID": f"{int(time.time() * 1000)}{random.randint(1111111, 999999999)}{int(time.time() * 1000)}",
            "Accept-Language": "zh-CN,zh-Hans;q=1",
            "MT-Device-ID": f"{int(time.time() * 1000)}{random.randint(1111111, 999999999)}{int(time.time() * 1000)}",
            "Accept": "application/json, text/javascript, */*; q=0.01",
            "mt-lng": f"{lng}",
            "mt-lat": f"{lat}",
        }
        res = requests.get(url, headers=headers)
        mtshops = res.json().get("data", {}).get("mtshops_pc", {})
        urls = mtshops.get("url")
        r = requests.get(urls)
        for k, v in dict(r.json()).items():
            provinceName = v.get("provinceName")
            cityName = v.get("cityName")
            if not p_c_map.get(provinceName):
                p_c_map[provinceName] = {}
            if not p_c_map[provinceName].get(cityName, None):
                p_c_map[provinceName][cityName] = [k]
            else:
                p_c_map[provinceName][cityName].append(k)

        return p_c_map, dict(r.json())

    def max_shop(self, city, item_code, p_c_map, province, shops):
        max_count = 0
        max_shop_id = "0"
        shop_ids = p_c_map[province][city]
        for shop in shops:
            shopId = shop["shopId"]
            items = shop["items"]

            if shopId not in shop_ids:
                continue
            for item in items:
                if item["itemId"] != str(item_code):
                    continue
                if item["inventory"] > max_count:
                    max_count = item["inventory"]
                    max_shop_id = shopId
        print(
            f"item code {item_code}, max shop id : {max_shop_id}, max count : {max_count}"
        )
        return max_shop_id

    def distance_shop(
        self,
        item_code,
        shops,
        source_data,
        lat: str = "28.499562",
        lng: str = "102.182324",
    ):
        temp_list = []
        for shop in shops:
            shopId = shop["shopId"]
            items = shop["items"]
            item_ids = [i["itemId"] for i in items]
            if str(item_code) not in item_ids:
                continue
            shop_info = source_data.get(shopId)
            d = math.sqrt(
                (float(lat) - shop_info["lat"]) ** 2
                + (float(lng) - shop_info["lng"]) ** 2
            )
            temp_list.append((d, shopId))

        temp_list = sorted(temp_list, key=lambda x: x[0])
        if len(temp_list) > 0:
            return temp_list[0][1]
        else:
            return "0"

    def get_location_count(
        self,
        province: str,
        city: str,
        item_code: str,
        p_c_map: dict,
        source_data: dict,
        lat: str = "29.83826",
        lng: str = "102.182324",
        reserve_rule: int = 0,
    ):
        day_time = int(time.mktime(datetime.date.today().timetuple())) * 1000
        session_id = self.headers["current_session_id"]
        responses = requests.get(
            f"https://static.moutai519.com.cn/mt-backend/xhr/front/mall/shop/list/slim/v3/{session_id}/{province}/{item_code}/{day_time}"
        )
        if responses.status_code != 200:
            print(
                f"get_location_count : params : {day_time}, response code : {responses.status_code}, response body : {responses.text}"
            )
        shops = responses.json()["data"]["shops"]

        if reserve_rule == 0:
            return self.distance_shop(item_code, shops, source_data, lat, lng)
        if reserve_rule == 1:
            return self.max_shop(city, item_code, p_c_map, province, shops)

    def act_params(self, shop_id: str, item_id: str):
        session_id = self.headers["current_session_id"]
        userId = self.headers["userId"]
        params = {
            "itemInfoList": [{"count": 1, "itemId": item_id}],
            "sessionId": int(session_id),
            "userId": userId,
            "shopId": shop_id,
        }
        s = json.dumps(params)
        act = self.encrypt.aes_encrypt(s)
        params.update({"actParam": act})
        return params

    def reservation(self, params: dict):
        params.pop("userId")
        responses = requests.post(
            "https://app.moutai519.com.cn/xhr/front/mall/reservation/add",
            json=params,
            headers=self.headers,
        ).json()
        if responses.get("code") == 401:
            msg = {
                "name": "申购结果",
                "value": "token失效, 请重新抓包获取",
            }
        elif responses.get("code") != 2000:
            msg = {
                "name": "申购结果",
                "value": responses.get("message"),
            }
        else:
            msg = {
                "name": "申购结果",
                "value": responses.get("data", {}).get("successDesc"),
            }
        return msg

    def getUserEnergyAward(self):
        """
        耐力值
        """
        cookies = {
            "MT-Device-ID-Wap": self.headers["MT-Device-ID"],
            "MT-Token-Wap": self.headers["MT-Token"],
            "YX_SUPPORT_WEBP": "1",
        }
        response = requests.post(
            url="https://h5.moutai519.com.cn/game/isolationPage/getUserEnergyAward",
            cookies=cookies,
            headers=self.headers,
            json={},
        ).json()
        if response.get("code") == 200:
            msg = {
                "name": "耐力",
                "value": "✅领取耐力成功",
            }
        else:
            msg = {
                "name": "耐力",
                "value": response.get("message"),
            }
        return msg

    def main(self):
        msg = []
        mobile = self.check_item.get("mobile")
        province = self.check_item.get("province")
        city = self.check_item.get("city")
        token = self.check_item.get("token")
        userId = self.check_item.get("userid")
        lat = self.check_item.get("lat")
        lng = self.check_item.get("lng")
        item_codes = self.check_item.get("item_codes", self.ITEM_CODES)
        reserve_rule = self.check_item.get("reserve_rule", 0)
        msg = [
            {
                "name": "手机号",
                "value": f"{mobile}",
            },
            {
                "name": "省份城市",
                "value": f"{province}{city}",
            },
        ]
        p_c_map, source_data = self.get_map(lat=lat, lng=lng)
        self.get_current_session_id()
        self.init_headers(user_id=userId, token=token, lng=lng, lat=lat)
        try:
            for item in item_codes:
                max_shop_id = self.get_location_count(
                    province=province,
                    city=city,
                    item_code=item,
                    p_c_map=p_c_map,
                    source_data=source_data,
                    lat=lat,
                    lng=lng,
                    reserve_rule=reserve_rule,
                )
                if max_shop_id == "0":
                    continue
                reservation_params = self.act_params(max_shop_id, item)
                reservation_msg = self.reservation(reservation_params)
                time.sleep(20)
                award_msg = self.getUserEnergyAward()
                msg.append(reservation_msg)
                msg.append(award_msg)
        except BaseException as e:
            msg.append(
                {
                    "name": "申购结果",
                    "value": e,
                }
            )
        msg = "\n".join([f"{one.get('name')}: {one.get('value')}" for one in msg])
        return msg


if __name__ == "__main__":
    with open(
        os.path.join(os.path.dirname(os.path.dirname(__file__)), "config.json"),
        encoding="utf-8",
    ) as f:
        datas = json.loads(f.read())
    _check_item = datas.get("IMAOTAI", [])[0]
    print(IMAOTAI(check_item=_check_item).main())