import difflib
import os.path
import time
import uuid

import ddddocr
import requests
import json
import datetime
from datetime import datetime
import random

from PIL import Image
from requests.exceptions import SSLError, Timeout, ConnectionError, HTTPError, RequestException, ProxyError
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

import encrypted


class WeibanHelper:
    tenantCode = 0
    userId = ""
    x_token = ""
    userProjectId = ""
    project_list = {}
    ocr = None
    finish_exam_time = 0
    exam_threshold = 1
    headers = {
        "X-Token": "",
        "ContentType": "application/x-www-form-urlencoded; charset=UTF-8",
        "User-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.82",
    }

    tempUserCourseId = ""

    def __init__(self, account, password, school_name, auto_verify=False, project_index=0):
        self.ocr = ddddocr.DdddOcr(show_ad=False)
        tenant_code = self.get_tenant_code(school_name=school_name)
        img_file_uuid = ""
        verify_time = time.time()
        self.session = self.create_session()

        login_data = {}
        # éªŒè¯�ç �å¤„ç�†
        verify_code = ''
        if not auto_verify:
            img_file_uuid = self.get_verify_code(get_time=verify_time, download=True)
            Image.open(f"code/{img_file_uuid}.jpg").show()
            verify_code = input("è¯·è¾“å…¥éªŒè¯�ç �: ")
        else:
            verify_code = self.ocr.classification(self.get_verify_code(get_time=verify_time, download=False))
        login_data = self.login(account, password, tenant_code, verify_code, verify_time)

        if auto_verify:
            while login_data['code'] == '-1' and str(login_data).find("éªŒè¯�ç �") != -1:
                verify_time = time.time()
                verify_code = self.ocr.classification(self.get_verify_code(get_time=verify_time, download=False))
                login_data = self.login(account, password, tenant_code, verify_code, verify_time)
                time.sleep(5)
        # å�‡è®¾login_dataæ˜¯ä»�æŸ�ä¸ªè¯·æ±‚è¿”å›�çš„JSONæ•°æ�®ä¸­è�·å�–çš„
        if 'data' in login_data:
            login_data = login_data['data']
            self.project_list = WeibanHelper.get_project_id(
                login_data["userId"], tenant_code, login_data["token"]
            )
            self.lab_info = WeibanHelper.get_lab_id(
                login_data["userId"], tenant_code, login_data["token"]
            )
            if self.lab_info:  # æ£€æŸ¥æ˜¯å�¦æˆ�åŠŸè�·å�–åˆ°å®�éªŒè¯¾ä¿¡æ�¯
                print(f"å®�éªŒè¯¾ç¨‹å��ç§°: {self.lab_info['projectName']}")
                print(f"å®�éªŒè¯¾ç¨‹ID: {self.lab_info['userProjectId']}")
            else:
                print("å½“å‰�è´¦æˆ·æ²¡æœ‰å®�éªŒè¯¾ç¨‹ã€‚")
        else:
            # å¦‚æ�œ 'data' é”®ä¸�å­˜åœ¨ï¼Œè¾“å‡ºæ��ç¤ºä¿¡æ�¯
            print("ç™»å½•å¤±è´¥ï¼Œå�¯èƒ½æ˜¯å­¦æ ¡å��ç§°è¾“å…¥é”™è¯¯ã€‚\n")
            print(f"è¿”å›�çš„é”™è¯¯ä¿¡æ�¯: {login_data}\n")

        if self.project_list is None and self.lab_info is not None:
            self.init(tenant_code, login_data["userId"], login_data["token"], self.lab_info["userProjectId"])
            self.project_list = []
        elif self.project_list is not None:
            project_id = self.project_list[project_index]["userProjectId"]
            self.init(tenant_code, login_data["userId"], login_data["token"], project_id)

    def init(self, code, id, token, projectId):
        self.tenantCode = code
        self.userId = id
        self.x_token = token
        self.userProjectId = projectId
        self.headers["X-Token"] = self.x_token

    def create_session(self):
        """
        åˆ›å»ºä¸€ä¸ªå¸¦æœ‰é‡�è¯•ç­–ç•¥çš„ä¼šè¯�å¯¹è±¡ã€‚
        """
        session = requests.Session()
        retry_strategy = Retry(
            total=5,
            status_forcelist=[429, 500, 502, 503, 504],
            allowed_methods=["HEAD", "GET", "OPTIONS", "POST"],  # æ›¿æ�¢ `method_whitelist`
            backoff_factor=1
        )
        adapter = HTTPAdapter(max_retries=retry_strategy)
        session.mount("https://", adapter)
        session.mount("http://", adapter)
        return session

    def retry_request(self, func, *args, retry_count=5, wait_time=3):
        """
        å°�è£…çš„é‡�è¯•è¯·æ±‚æ–¹æ³•ã€‚
        """
        for attempt in range(retry_count):
            try:
                return func(*args)  # è°ƒç”¨ä¼ å…¥çš„å‡½æ•°å¹¶è¿”å›�å…¶ç»“æ�œ
            except (SSLError, Timeout, ConnectionError, HTTPError, RequestException, ProxyError) as e:
                print(f"ç½‘ç»œé”™è¯¯ [{type(e).__name__}]: {e}ï¼ŒURL: {args[0]}ï¼Œæ­£åœ¨é‡�è¯• {attempt + 1} / {retry_count} æ¬¡...")
                time.sleep(wait_time)  # ç­‰å¾…æŒ‡å®šæ—¶é—´å��é‡�è¯•
                if attempt == retry_count - 1:
                    print("è¾¾åˆ°æœ€å¤§é‡�è¯•æ¬¡æ•°ï¼Œè·³è¿‡æ­¤æ“�ä½œã€‚")
                    return None  # å¦‚æ�œæœ€ç»ˆå¤±è´¥ï¼Œè¿”å›� None

    def start(self, courseId):
        """
        å�¯åŠ¨è¯¾ç¨‹å­¦ä¹ çš„è¯·æ±‚æ–¹æ³•ï¼ŒåŒ…å�«é”™è¯¯å¤„ç�†å’Œé‡�è¯•æœºåˆ¶ã€‚
        :param courseId: è¯¾ç¨‹IDï¼Œç”¨äº�å�¯åŠ¨æŒ‡å®šçš„è¯¾ç¨‹å­¦ä¹ ã€‚
        """
        url = "https://weiban.mycourse.cn/pharos/usercourse/study.do"
        data = {
            "userProjectId": self.userProjectId,
            "tenantCode": self.tenantCode,
            "userId": self.userId,
            "courseId": courseId,
        }
        headers = {"x-token": self.x_token}
        retry_count = 0
        max_retries = 5  # æœ€å¤§é‡�è¯•æ¬¡æ•°
        timeout = 10

        while retry_count < max_retries:
            try:
                print(f"å°�è¯•å�¯åŠ¨è¯¾ç¨‹ (ç¬¬ {retry_count + 1} æ¬¡) ...")

                # å�‘èµ·è¯·æ±‚
                response = self.session.post(
                    url,
                    data=data,
                    headers=headers,
                    proxies={"http": None, "https": None},  # ç¦�ç”¨ä»£ç�†
                    timeout=timeout,  # è®¾ç½®è¶…æ—¶æ—¶é—´
                    verify=False  # å¦‚æ�œéœ€è¦�è·³è¿‡ SSL è¯�ä¹¦éªŒè¯�
                )

                # æ£€æŸ¥çŠ¶æ€�ç �
                if response.status_code != 200:
                    print(f"è¯·æ±‚å¤±è´¥ï¼ŒçŠ¶æ€�ç �: {response.status_code}ï¼Œå“�åº”å†…å®¹: {response.text}")
                    retry_count += 1
                    time.sleep(5)  # ç­‰å¾…5ç§’å��é‡�è¯•
                    continue

                # æ£€æŸ¥è¿”å›�å†…å®¹æ˜¯å�¦ä¸ºç©º
                if not response.text:
                    print(f"è¯·æ±‚è¿”å›�äº†ç©ºå†…å®¹ï¼ŒURL: {url}")
                    retry_count += 1
                    time.sleep(5)  # ç­‰å¾…5ç§’å��é‡�è¯•
                    continue

                # è§£æ��è¿”å›�çš„ JSON æ•°æ�®
                try:
                    response_json = response.json()
                except json.JSONDecodeError as e:
                    print(f"[JSON è§£æ��é”™è¯¯] é”™è¯¯ä¿¡æ�¯: {e}")  # ï¼Œå“�åº”å†…å®¹: {response.text}
                    retry_count += 1
                    time.sleep(5)  # ç­‰å¾…5ç§’å��é‡�è¯•
                    continue

                # æ‰“å�°æœ�åŠ¡å™¨å®Œæ•´å“�åº”
                print(f"æœ�åŠ¡å™¨è¿”å›�å®Œæ•´çš„å“�åº”: {response_json}")

                # æ£€æŸ¥è¯·æ±‚æ˜¯å�¦æˆ�åŠŸ
                code = response_json.get("code")
                detail_code = response_json.get("detailCode")

                if code == '0' and detail_code == '0':
                    # è¯¾ç¨‹å�¯åŠ¨æˆ�åŠŸ
                    print("è¯¾ç¨‹å�¯åŠ¨æˆ�åŠŸ")
                    print(f" - å½“å‰�æ—¶é—´: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                    return  # æˆ�åŠŸå��é€€å‡ºé‡�è¯•å¾ªç�¯
                else:
                    # è¯¾ç¨‹å�¯åŠ¨å¤±è´¥
                    print(
                        f"å�¯åŠ¨è¯¾ç¨‹å¤±è´¥ï¼Œé”™è¯¯ä»£ç �: {code}ï¼Œè¯¦ç»†ä»£ç �: {detail_code}ï¼Œæ¶ˆæ�¯: {response_json.get('message', 'æ— æ¶ˆæ�¯å†…å®¹')}")
                    retry_count += 1
                    time.sleep(5)  # ç­‰å¾…5ç§’å��é‡�è¯•

            except (ProxyError, SSLError, Timeout, ConnectionError, HTTPError, RequestException) as e:
                # ç½‘ç»œé”™è¯¯å¤„ç�†
                print(f"[ç½‘ç»œé”™è¯¯] [{type(e).__name__}]: {e}ï¼ŒURL: {url}")
                retry_count += 1
                time.sleep(5)  # ç­‰å¾…5ç§’å��é‡�è¯•

        print(f"å·²è¾¾åˆ°æœ€å¤§é‡�è¯•æ¬¡æ•° ({max_retries})ï¼Œå�¯åŠ¨è¯¾ç¨‹å¤±è´¥ã€‚")

    def run(self):
        # é��å�† chooseType 2 å’Œ 3 è¿›è¡Œåˆ·è¯¾
        for chooseType in [2, 3]:
            finishIdList = self.retry_request(self.getFinishIdList, chooseType)

            if finishIdList is None:
                print(f"æ— æ³•è�·å�– finishIdListï¼Œè·³è¿‡ chooseType={chooseType} çš„è¯¾ç¨‹å¤„ç�†ã€‚")
                continue

            course_list = self.retry_request(self.getCourse, chooseType)

            if course_list is None:
                print(f"æ— æ³•è�·å�–è¯¾ç¨‹åˆ—è¡¨ï¼Œè·³è¿‡ chooseType={chooseType} çš„è¯¾ç¨‹å¤„ç�†ã€‚")
                continue

            num = len(course_list)
            index = 1
            for i in course_list:
                print(f"{index} / {num}")
                self.start(i)
                time.sleep(random.randint(15, 20))  # åˆ·è¯¾æ—¶é—´åŒºé—´
                self.retry_request(self.finish, i, finishIdList[i])
                index += 1
            print(f"chooseType={chooseType} çš„è¯¾ç¨‹åˆ·è¯¾å®Œæˆ�")

    # jsé‡Œçš„æ—¶é—´æˆ³ä¼¼ä¹�éƒ½æ˜¯ä¿�ç•™äº†ä¸‰ä½�å°�æ•°çš„.
    def __get_timestamp(self):
        return str(round(time.time(), 3))

    # Magic: ç”¨äº�æ�„é€ ã€�æ‹¼æ�¥"å®Œæˆ�å­¦ä¹ ä»»åŠ¡"çš„url
    def __gen_rand(self):
        return ("3.4.1" + str(random.random())).replace(".", "")

    def getProgress(self):
        url = "https://weiban.mycourse.cn/pharos/project/showProgress.do"
        data = {
            "userProjectId": self.userProjectId,
            "tenantCode": self.tenantCode,
            "userId": self.userId,
        }
        response = requests.post(url, data=data, headers=self.headers)
        data = json.loads(response.text)
        return data["data"]["progressPet"]

    def getAnswerList(self):
        """
        è�·å�–ç­”é¢˜è®°å½•çš„åˆ—è¡¨ï¼Œé€šè¿‡é€�æ�¡è�·å�–çš„æ–¹å¼�å¤„ç�†å¤šä¸ªè®°å½•
        """
        answer_list = []
        url = "https://weiban.mycourse.cn/pharos/exam/reviewPaper.do?timestamp=" + self.__get_timestamp()
        exam_id_list = self.listHistory()  # è°ƒç”¨ listHistory æ�¥è�·å�–å¤šä¸ªè€ƒè¯•ID
        for exam_id in exam_id_list:
            data = {
                "tenantCode": self.tenantCode,
                "userId": self.userId,
                "userExamId": exam_id,
                "isRetake": "2"
            }
            response = self.session.post(url, data=data, headers=self.headers)
            if response.status_code == 200:
                answer_list.append(response.text)  # å­˜å‚¨æ¯�æ�¡è€ƒè¯•çš„ç­”é¢˜è®°å½•
        return answer_list

    def listHistory(self):
        """
        è�·å�–ç”¨æˆ·çš„å�†å�²è€ƒè¯•è®°å½•ï¼Œå¹¶è¿”å›�å¤šä¸ªè€ƒè¯•ID
        """
        result = []
        url = "https://weiban.mycourse.cn/pharos/exam/listHistory.do?timestamp=" + self.__get_timestamp()
        exam_plan_id_list = self.listExamPlan()  # è�·å�–è€ƒè¯•è®¡åˆ’IDåˆ—è¡¨
        for exam_plan_id in exam_plan_id_list:
            dataList = {
                "tenantCode": self.tenantCode,
                "userId": self.userId,
                "examPlanId": exam_plan_id
            }
            response = self.session.post(url, headers=self.headers, data=dataList)
            data = json.loads(response.text)
            if data['code'] == '-1':
                return result
            else:
                for history in data['data']:  # é��å�†å�†å�²è€ƒè¯•è®°å½•
                    result.append(history['id'])
        return result

    def listExamPlan(self):
        """
        è�·å�–ç”¨æˆ·çš„è€ƒè¯•è®¡åˆ’IDåˆ—è¡¨
        """
        url = "https://weiban.mycourse.cn/pharos/record/project/listExamPlanStat.do?timestamp=" + self.__get_timestamp()
        data = {
            "tenantCode": self.tenantCode,
            "userId": self.userId,
            "userProjectId": self.userProjectId
        }
        response = requests.post(url, headers=self.headers, data=data)
        exam_plan_id_list = []
        for exam_plan in json.loads(response.text)['data']:
            exam_plan_id_list.append(exam_plan['examPlanId'])
        return exam_plan_id_list

    def getCategory(self, chooseType):
        result = []
        url = "https://weiban.mycourse.cn/pharos/usercourse/listCategory.do"
        data = {
            "userProjectId": self.userProjectId,
            "tenantCode": self.tenantCode,
            "userId": self.userId,
            "chooseType": chooseType,
        }
        response = requests.post(url, data=data, headers=self.headers)
        list = json.loads(response.text)["data"]
        for i in list:
            if i["totalNum"] > i["finishedNum"]:
                result.append(i["categoryCode"])
        return result

    def getCourse(self, chooseType):
        url = "https://weiban.mycourse.cn/pharos/usercourse/listCourse.do"
        result = []
        for i in self.getCategory(chooseType):
            data = {
                "userProjectId": self.userProjectId,
                "tenantCode": self.tenantCode,
                "userId": self.userId,
                "chooseType": chooseType,
                "name": "",
                "categoryCode": i,
            }
            response = requests.post(url, data=data, headers=self.headers)
            text = response.text
            data = json.loads(text)["data"]
            for i in data:
                if i["finished"] == 2:
                    result.append(i["resourceId"])
        return result

    def autoExam(self):
        list_plan_url = f"https://weiban.mycourse.cn/pharos/exam/listPlan.do"
        before_paper_url = f"https://weiban.mycourse.cn/pharos/exam/beforePaper.do"
        get_verify_code_url = f"https://weiban.mycourse.cn/pharos/login/randLetterImage.do?time="
        check_verify_code_url = f"https://weiban.mycourse.cn/pharos/exam/checkVerifyCode.do?timestamp"
        start_paper_url = f"https://weiban.mycourse.cn/pharos/exam/startPaper.do?"
        submit_url = f"https://weiban.mycourse.cn/pharos/exam/submitPaper.do?timestamp="
        answer_data = None

        with open("QuestionBank/result.json", 'r', encoding='utf8') as f:
            answer_data = json.loads(f.read())

        def retry_request_2(method, url, headers=None, data=None, max_retries=5, retry_delay=5):
            for attempt in range(max_retries):
                try:
                    if method == "GET":
                        response = requests.get(url, headers=headers, data=data)
                    elif method == "POST":
                        response = requests.post(url, headers=headers, data=data)
                    else:
                        raise ValueError("Invalid method type")
                    response.raise_for_status()  # æ£€æŸ¥æ˜¯å�¦è¿”å›�äº†é”™è¯¯çš„çŠ¶æ€�ç �
                    return response
                except (requests.exceptions.RequestException, ValueError) as e:
                    print(
                        f"ç½‘ç»œé”™è¯¯:Request failed: {e}. æ­£åœ¨é‡�è¯•:Attempt {attempt + 1} / {max_retries}æ¬¡. Retrying...")
                    if attempt < max_retries - 1:
                        time.sleep(retry_delay)
                    else:
                        print("Max retries reached. Request failed.")
                        raise

        def get_answer_list(question_title):
            closest_match = difflib.get_close_matches(question_title, answer_data.keys(), n=1, cutoff=1)
            answer_list = []
            if closest_match:
                data = answer_data[closest_match[0]]
                for i in data['optionList']:
                    if i['isCorrect'] == 1:
                        answer_list.append(i['content'])
                return answer_list, True
            else:
                return answer_list, False

        def get_verify_code():
            now = time.time()
            content = retry_request_2("GET", get_verify_code_url + str(now), headers=self.headers).content
            return self.ocr.classification(content), now

        # è�·å�–å½“å‰�ç³»ç»Ÿæ—¶é—´
        current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        # è�·å�–æ‰€æœ‰è®¡åˆ’
        plan_data = retry_request_2("POST", list_plan_url, headers=self.headers, data={
            "tenantCode": self.tenantCode,
            "userId": self.userId,
            "userProjectId": self.userProjectId
        }).json()

        if plan_data['code'] != '0':
            print("è�·å�–è€ƒè¯•è®¡åˆ’å¤±è´¥")
            return

        # é��å�†æ‰€æœ‰è€ƒè¯•è®¡åˆ’
        for plan in plan_data['data']:
            plan_id = plan['id']
            exam_plan_id = plan['examPlanId']
            exam_plan_name = plan['examPlanName']
            exam_time_state = plan['examTimeState']
            can_not_exam_info = plan.get("canNotExamInfo", "")
            start_Time = plan['startTime']
            end_Time = plan['endTime']

            # Before
            print(retry_request_2("POST", before_paper_url, headers=self.headers, data={
                "tenantCode": self.tenantCode,
                "userId": self.userId,
                "userExamPlanId": plan_id
            }).text)

            # æ£€æŸ¥æ˜¯å�¦èƒ½å¤Ÿå�‚åŠ è€ƒè¯•
            if exam_time_state != 2:
                print(f"è€ƒè¯•è®¡åˆ’ '{exam_plan_name}' æ— æ³•å�‚åŠ è€ƒè¯•: '{can_not_exam_info}' \n")
                continue  # è·³è¿‡è¿™ä¸ªè€ƒè¯•ï¼Œç»§ç»­ä¸‹ä¸€ä¸ª

            print(f"å¼€å§‹æ‰§è¡Œ '{exam_plan_name}' è€ƒè¯•å¼€æ”¾ä¸ºæ—¶é—´: {start_Time} åˆ° {end_Time}\n")
            # Prepare
            print(retry_request_2("POST", f"https://weiban.mycourse.cn/pharos/exam/preparePaper.do?timestamp",
                                  headers=self.headers, data={
                    "tenantCode": self.tenantCode,
                    "userId": self.userId,
                    "userExamPlanId": plan_id,
                }).text)

            # éªŒè¯�ç �æ ¡éªŒ
            verify_count = 0
            while True:
                verify_code, verify_time = get_verify_code()
                verify_data = retry_request_2("POST", check_verify_code_url, headers=self.headers, data={
                    "tenantCode": self.tenantCode,
                    "time": verify_time,
                    "userId": self.userId,
                    "verifyCode": verify_code,
                    "userExamPlanId": plan_id
                }).json()

                if verify_data['code'] == '0':
                    break

                verify_count += 1
                if verify_count > 3:
                    print("éªŒè¯�ç �è¯†åˆ«å¤±è´¥")
                    return

            # å¼€å§‹è€ƒè¯•
            paper_data = retry_request_2("POST", start_paper_url, headers=self.headers, data={
                "tenantCode": self.tenantCode,
                "userId": self.userId,
                "userExamPlanId": plan_id,
            }).json()['data']

            # æ��å�–é¢˜ç›®åˆ—è¡¨
            question_list = paper_data['questionList']
            match_count = 0

            for question in question_list:
                question_title = question['title']
                option_list = question['optionList']
                submit_answer_id_list = []

                # è�·å�–ç­”æ¡ˆåˆ—è¡¨å’Œåˆ�å§‹çš„åŒ¹é…�æ ‡å¿—
                answer_list, _ = get_answer_list(question_title)

                print(f"é¢˜ç›®: {question_title}")

                # æ£€æŸ¥é¢˜ç›®æ ‡é¢˜æ˜¯å�¦åŒ¹é…�
                if answer_list:
                    found_match = False
                    for answer in answer_list:
                        matched_option = next((option for option in option_list if option['content'] == answer), None)
                        if matched_option:
                            submit_answer_id_list.append(matched_option['id'])
                            print(f"ç­”æ¡ˆ: {answer}")
                            found_match = True

                    if found_match:
                        match_count += 1
                        print("<===ç­”æ¡ˆåŒ¹é…�æˆ�åŠŸ===>\n")
                    else:
                        print("<â€”â€”â€”â€”â€”â€”â€”â€”â€”â€”!!!é¢˜ç›®åŒ¹é…�ä½†é€‰é¡¹æœªæ‰¾åˆ°åŒ¹é…�é¡¹!!!â€”â€”â€”â€”â€”â€”â€”â€”â€”â€”>\n")
                else:
                    print("<â€”â€”â€”â€”â€”â€”â€”â€”â€”â€”!!!æœªåŒ¹é…�åˆ°ç­”æ¡ˆï¼Œé¢˜åº“æš‚æœªæ”¶å½•æ­¤é¢˜!!!â€”â€”â€”â€”â€”â€”â€”â€”â€”â€”>\n")

                # è®°å½•ç­”æ¡ˆ
                record_data = {
                    "answerIds": ",".join(submit_answer_id_list),
                    "questionId": question['id'],
                    "tenantCode": self.tenantCode,
                    "userId": self.userId,
                    "userExamPlanId": plan_id,
                    "examPlanId": exam_plan_id,
                    "useTime": random.randint(60, 90)
                }
                retry_request_2("POST",
                                f"https://weiban.mycourse.cn/pharos/exam/recordQuestion.do?timestamp={time.time()}",
                                headers=self.headers, data=record_data)

            # è¾“å‡ºåŒ¹é…�åº¦
            print("ç­”æ¡ˆåŒ¹é…�åº¦: ", match_count, " / ", len(question_list))
            print(f" - å½“å‰�æ—¶é—´: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")

            if len(question_list) - match_count > self.exam_threshold:
                print(f"é¢˜åº“åŒ¹é…�åº¦è¿‡ä½�, '{exam_plan_name}' æš‚æœªæ��äº¤,è¯·é‡�æ–°è€ƒè¯•")
                return

            print("è¯·è€�å¿ƒç­‰å¾…è€ƒè¯•å®Œæˆ�ï¼ˆç­‰å¾…æ—¶é•¿ä¸ºä½ å¡«å†™çš„è€ƒè¯•æ—¶é—´ï¼‰\n")

            # æ��äº¤è€ƒè¯•
            submit_data = {
                "tenantCode": self.tenantCode,
                "userId": self.userId,
                "userExamPlanId": plan_id,
            }
            time.sleep(self.finish_exam_time)
            print(retry_request_2("POST", submit_url + str(int(time.time()) + 600), headers=self.headers,
                                  data=submit_data).text)
            print(" - è€ƒè¯•å·²å®Œæˆ� \n")
            print(f" - å½“å‰�æ—¶é—´: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")

    def getFinishIdList(self, chooseType):
        url = "https://weiban.mycourse.cn/pharos/usercourse/listCourse.do"
        result = {}
        for i in self.getCategory(chooseType):
            data = {
                "userProjectId": self.userProjectId,
                "tenantCode": self.tenantCode,
                "userId": self.userId,
                "chooseType": chooseType,
                "categoryCode": i,
            }
            response = requests.post(url, data=data, headers=self.headers)
            text = response.text
            data = json.loads(text)["data"]
            for i in data:
                if i["finished"] == 2:
                    if "userCourseId" in i:
                        result[i["resourceId"]] = i["userCourseId"]
                        # print(i['resourceName'])
                        self.tempUserCourseId = i["userCourseId"]
                    else:
                        result[i["resourceId"]] = self.tempUserCourseId
            print(f"åŠ è½½ç« èŠ‚ : {i['categoryName']}")
        print("\nèµ„æº�åŠ è½½å®Œæˆ�")
        return result

    # æ„Ÿè°¢ä»¥ä¸‹é¡¹ç›®çš„æ€�è·¯
    def finish(self, courseId, finishId):
        from datetime import datetime
        # è�·å�–å½“å‰�ç³»ç»Ÿæ—¶é—´
        current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

        def get_mid_text(text, start, end):
            """
            ä»�æ–‡æœ¬ä¸­æ��å�–ä½�äº� start å’Œ end ä¹‹é—´çš„å­�å­—ç¬¦ä¸²ã€‚

            å�‚æ•°:
            text: å�Ÿå§‹æ–‡æœ¬
            start: èµ·å§‹æ ‡è®°
            end: ç»“æ�Ÿæ ‡è®°

            è¿”å›�:
            å­�å­—ç¬¦ä¸²
            """
            start_index = text.index(start) + len(start)
            end_index = text.index(end, start_index)
            return text[start_index:end_index]

        def finish_first_attempt():
            """
            å°�è¯•é¦–æ¬¡è¯·æ±‚ä»¥å®Œæˆ�ä»»åŠ¡ã€‚

            è¯·æ±‚è¿‡ç¨‹:
            1. å�‘é€� POST è¯·æ±‚è�·å�–è¯¾ç¨‹çš„ URLã€‚
            2. ä½¿ç”¨è¿”å›�çš„æ•°æ�®ç”Ÿæˆ�å®Œæˆ�ä»»åŠ¡çš„ URLã€‚
            3. å�‘é€� GET è¯·æ±‚å°�è¯•å®Œæˆ�ä»»åŠ¡ï¼Œå¹¶è¿”å›�å“�åº”æ–‡æœ¬ã€‚

            è¿”å›�:
            å“�åº”æ–‡æœ¬
            """
            # è�·å�–è¯¾ç¨‹ URL çš„æ�¥å�£
            get_url_url = "https://weiban.mycourse.cn/pharos/usercourse/getCourseUrl.do"
            # å®Œæˆ�ä»»åŠ¡çš„æ�¥å�£ï¼ˆæ¨¡æ�¿ï¼‰
            finish_url = "https://weiban.mycourse.cn/pharos/usercourse/v2/{}.do"
            # è¯·æ±‚æ•°æ�®
            data = {
                "userProjectId": self.userProjectId,
                "tenantCode": self.tenantCode,
                "userId": self.userId,
                "courseId": courseId,
            }
            # å�‘é€� POST è¯·æ±‚è�·å�–è¯¾ç¨‹ URL
            response = requests.post(get_url_url, data=data, headers=self.headers)
            # è�·å�–ä»»åŠ¡å®Œæˆ�çš„ä»¤ç‰Œ
            token = self.get_method_token(finishId)
            # æ ¼å¼�åŒ–å®Œæˆ�ä»»åŠ¡çš„ URL
            finish_url = finish_url.format(token)
            # è�·å�–å½“å‰�æ—¶é—´æˆ³å¹¶å¤„ç�†
            ts = self.__get_timestamp().replace(".", "")
            # ç”Ÿæˆ�è¯·æ±‚å�‚æ•°
            param = {
                "callback": "jQuery{}_{}".format(self.__gen_rand(), ts),
                "userCourseId": finishId,
                "tenantCode": self.tenantCode,
                "_": str(int(ts) + 1),
            }
            # å�‘é€� GET è¯·æ±‚å®Œæˆ�ä»»åŠ¡ï¼Œå¹¶è¿”å›�å“�åº”æ–‡æœ¬
            text = requests.get(finish_url, params=param, headers=self.headers).text
            return text

        def finish_second_attempt():
            """
            å½“ç¬¬ä¸€æ¬¡å°�è¯•å¤±è´¥æ—¶ï¼Œä½¿ç”¨å¤‡é€‰æ–¹æ³•è¿›è¡Œç¬¬äºŒæ¬¡å°�è¯•ã€‚

            è¯·æ±‚è¿‡ç¨‹:
            1. å�‘é€� POST è¯·æ±‚è�·å�–è¯¾ç¨‹çš„ URL å’Œ userCourseIdã€‚
            2. ä½¿ç”¨è¿”å›�çš„æ•°æ�®ç”Ÿæˆ�å®Œæˆ�ä»»åŠ¡çš„ URLã€‚
            3. å�‘é€� GET è¯·æ±‚å°�è¯•å®Œæˆ�ä»»åŠ¡ï¼Œå¹¶è¿”å›�å“�åº”æ–‡æœ¬ã€‚

            è¿”å›�:
            å“�åº”æ–‡æœ¬
            """
            # è�·å�–è¯¾ç¨‹ URL çš„æ�¥å�£
            get_url_url = "https://weiban.mycourse.cn/pharos/usercourse/getCourseUrl.do"
            # å®Œæˆ�ä»»åŠ¡çš„æ�¥å�£ï¼ˆæ¨¡æ�¿ï¼‰
            finish_url = "https://weiban.mycourse.cn/pharos/usercourse/v2/{}.do"
            # è¯·æ±‚æ•°æ�®
            data = {
                "userProjectId": self.userProjectId,
                "tenantCode": self.tenantCode,
                "userId": self.userId,
                "courseId": courseId,
            }
            # å�‘é€� POST è¯·æ±‚è�·å�–è¯¾ç¨‹ URL å’Œ userCourseId
            text = json.loads(requests.post(get_url_url, data=data, headers=self.headers).text)['data']
            # ä»�è¿”å›�çš„æ•°æ�®ä¸­æ��å�– userCourseId
            token = get_mid_text(text, "userCourseId=", "&tenantCode")
            # æ ¼å¼�åŒ–å®Œæˆ�ä»»åŠ¡çš„ URL
            finish_url = finish_url.format(token)
            # è�·å�–å½“å‰�æ—¶é—´æˆ³å¹¶å¤„ç�†
            ts = self.__get_timestamp().replace(".", "")
            # ç”Ÿæˆ�è¯·æ±‚å�‚æ•°
            param = {
                "callback": "jQuery{}_{}".format(self.__gen_rand(), ts),
                "userCourseId": finishId,
                "tenantCode": self.tenantCode,
                "_": str(int(ts) + 1),
            }
            # å�‘é€� GET è¯·æ±‚å®Œæˆ�ä»»åŠ¡ï¼Œå¹¶è¿”å›�å“�åº”æ–‡æœ¬
            text = requests.get(finish_url, params=param, headers=self.headers).text
            return text

        # å°�è¯•ä½¿ç”¨ç¬¬ä¸€ä¸ªå‡½æ•°é€»è¾‘è¯·æ±‚
        first_attempt_response = finish_first_attempt()

        # æ£€æŸ¥ç¬¬ä¸€ä¸ªå‡½æ•°çš„å“�åº”æ˜¯å�¦æˆ�åŠŸ
        if ('{"msg":"ok"' in first_attempt_response
                and '"code":"0"' in first_attempt_response
                and '"detailCode":"0"' in first_attempt_response):
            # è¾“å‡ºç¬¬ä¸€ä¸ªå‡½æ•°è¯·æ±‚æˆ�åŠŸçš„æ¶ˆæ�¯
            print("finish_first_attemptå‡½æ•°è¯·æ±‚æˆ�åŠŸğŸ—¹")
            # è¾“å‡ºå“�åº”æ–‡æœ¬
            print(first_attempt_response)
            # è¾“å‡ºæŒ‡å®šæ–‡æœ¬å’Œå½“å‰�ç³»ç»Ÿæ—¶é—´
            print(f" - å½“å‰�æ—¶é—´: {current_time} \n")
            # è¿”å›�å“�åº”æ–‡æœ¬
            return first_attempt_response
        else:
            # å¦‚æ�œç¬¬ä¸€ä¸ªå‡½æ•°è¯·æ±‚å¤±è´¥ï¼Œå…ˆè¾“å‡ºå¤±è´¥ä¿¡æ�¯
            print("finish_first_attemptå‡½æ•°è¯·æ±‚å¤±è´¥ğŸ—µï¼Œå°�è¯•ä½¿ç”¨finish_second_attemptå‡½æ•°è¯·æ±‚")
            # è¾“å‡ºç¬¬ä¸€ä¸ªå‡½æ•°çš„å“�åº”æ–‡æœ¬
            print(first_attempt_response)
            print(" Ò‰ Ò‰ Ò‰ Ò‰ Ò‰ Ò‰ Ò‰ Ò‰ Ò‰ Ò‰ ")

            # å°�è¯•ä½¿ç”¨ç¬¬äºŒä¸ªå‡½æ•°é€»è¾‘è¯·æ±‚
            second_attempt_response = finish_second_attempt()

            # æ£€æŸ¥ç¬¬äºŒä¸ªå‡½æ•°çš„å“�åº”æ˜¯å�¦æˆ�åŠŸ
            if ('{"msg":"ok"' in second_attempt_response
                    and '"code":"0"' in second_attempt_response
                    and '"detailCode":"0"' in second_attempt_response):
                # è¾“å‡ºç¬¬äºŒä¸ªå‡½æ•°è¯·æ±‚æˆ�åŠŸçš„æ¶ˆæ�¯
                print("finish_second_attemptå‡½æ•°è¯·æ±‚æˆ�åŠŸğŸ—¹")
                # è¾“å‡ºå“�åº”æ–‡æœ¬
                print(second_attempt_response)
                # è¾“å‡ºæŒ‡å®šæ–‡æœ¬å’Œå½“å‰�ç³»ç»Ÿæ—¶é—´
                print(f" - å½“å‰�æ—¶é—´: {current_time} \n")
                # è¿”å›�å“�åº”æ–‡æœ¬
                return second_attempt_response
            else:
                # è¾“å‡ºç¬¬äºŒä¸ªå‡½æ•°è¯·æ±‚å¤±è´¥çš„æ¶ˆæ�¯
                print("finish_second_attemptå‡½æ•°è¯·æ±‚å¤±è´¥ğŸ—µ")
                # è¾“å‡ºç¬¬äºŒä¸ªå‡½æ•°çš„å“�åº”æ–‡æœ¬
                print(second_attempt_response)
                # è¾“å‡ºæŒ‡å®šæ–‡æœ¬å’Œå½“å‰�ç³»ç»Ÿæ—¶é—´
                print(f" - å½“å‰�æ—¶é—´: {current_time} \n")
                # è¿”å›�å“�åº”æ–‡æœ¬
                return second_attempt_response

    def get_method_token(self, course_id):
        url = "https://weiban.mycourse.cn/pharos/usercourse/getCaptcha.do"
        params = {
            "userCourseId": course_id,
            "userProjectId": self.userProjectId,
            "userId": self.userId,
            "tenantCode": self.tenantCode
        }
        text = requests.get(url, headers=self.headers, params=params).text
        question_id = json.loads(text)['captcha']['questionId']
        url = "https://weiban.mycourse.cn/pharos/usercourse/checkCaptcha.do"
        params = {
            "userCourseId": course_id,
            "userProjectId": self.userProjectId,
            "userId": self.userId,
            "tenantCode": self.tenantCode,
            "questionId": question_id
        }
        data = {
            "coordinateXYs": "[{\"x\":199,\"y\":448},{\"x\":241,\"y\":466},{\"x\":144,\"y\":429}]"
        }
        text = requests.post(url, headers=self.headers, params=params, data=data).text
        return json.loads(text)['data']['methodToken']

    @staticmethod
    def get_project_id(user_id, tenant_code, token: str):
        url = "https://weiban.mycourse.cn/pharos/index/listMyProject.do"
        headers = {
            "X-Token": token,
            "ContentType": "application/x-www-form-urlencoded; charset=UTF-8",
            "User-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.82",
        }
        data = {"tenantCode": tenant_code, "userId": user_id, "ended": 2}
        text = requests.post(url=url, headers=headers, data=data).text
        data = json.loads(text)["data"]
        if len(data) <= 0:
            print("å·²å®Œæˆ�å…¨éƒ¨")
            # exit(1)
        else:
            return data

    def get_lab_id(user_id, tenant_code, token: str):
        """
        è�·å�–ç”¨æˆ·çš„å®�éªŒè¯¾ç¨‹ä¿¡æ�¯ã€‚
        """
        url = f"https://weiban.mycourse.cn/pharos/lab/index.do?timestamp={int(time.time())}"
        headers = {
            "X-Token": token,
            "ContentType": "application/x-www-form-urlencoded; charset=UTF-8",
            "User-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.82",
        }
        data = {"tenantCode": tenant_code, "userId": user_id}
        response = requests.get(url, headers=headers, params=data)
        response_data = response.json()  # è§£æ��JSONå“�åº”

        if response_data['code'] == '0' and response_data['detailCode'] == '0':
            # æ£€æŸ¥ 'current' é”®æ˜¯å�¦å­˜åœ¨äº�å“�åº”æ•°æ�®ä¸­
            if 'current' in response_data['data']:
                # æ��å�–å®�éªŒè¯¾ç¨‹çš„ä¿¡æ�¯
                lab_info = response_data['data']['current']
                return lab_info
            else:
                print("æ²¡æœ‰æ‰¾åˆ°å®�éªŒè¯¾ç¨‹ä¿¡æ�¯ã€‚")
                return None
        else:
            print("è�·å�–å®�éªŒè¯¾ç¨‹ä¿¡æ�¯å¤±è´¥")
            return None

    @staticmethod
    def get_tenant_code(school_name: str) -> str:
        tenant_list = requests.get(
            "https://weiban.mycourse.cn/pharos/login/getTenantListWithLetter.do"
        ).text
        data = json.loads(tenant_list)["data"]
        for i in data:
            for j in i["list"]:
                if j["name"] == school_name:
                    return j["code"]

    @staticmethod
    def get_verify_code(get_time, download=False):
        img_uuid = uuid.uuid4()
        img_data = requests.get(
            f"https://weiban.mycourse.cn/pharos/login/randLetterImage.do?time={get_time}"
        ).content
        if img_data is None:
            print("éªŒè¯�ç �è�·å�–å¤±è´¥")
            exit(1)
        # å¦‚æ�œcodeç›®å½•ä¸�å­˜åœ¨åˆ™åˆ›å»º
        if download:
            if not os.path.exists("code"):
                os.mkdir("code")
            with open(f"code/{img_uuid}.jpg", "wb") as file:
                file.write(img_data)
            return img_uuid
        else:
            return img_data

    @staticmethod
    def login(account, password, tenant_code, verify_code, verify_time):
        url = "https://weiban.mycourse.cn/pharos/login/login.do"
        payload = {
            "userName": account,
            "password": password,
            "tenantCode": tenant_code,
            "timestamp": verify_time,
            "verificationCode": verify_code,
        }
        ret = encrypted.login(payload)
        response = requests.post(url, data={"data": ret})
        text = response.text
        data = json.loads(text)
        print(data)
        if data['code'] == '-1':
            if str(data).find("ä¸�åŒ¹é…�") != -1:
                exit(1)
        return data
