import sys
import psutil
import time
import random
from logger import info, warning, error
import traceback
from config import (
    LOGIN_URL,
    SIGN_UP_URL,
    SETTINGS_URL,
    EMAIL_DOMAINS,
    REGISTRATION_MAX_RETRIES,
    EMAIL_TYPE,
    EMAIL_CODE_TYPE
)
import secrets
import hashlib
import base64
import uuid
import requests
from typing import Optional, Tuple


if sys.stdout.encoding != "utf-8":
    sys.stdout.reconfigure(encoding="utf-8")
if sys.stderr.encoding != "utf-8":
    sys.stderr.reconfigure(encoding="utf-8")

from browser_utils import BrowserManager
from get_email_code import EmailVerificationHandler

from datetime import datetime  # 添加这行导入

TOTAL_USAGE = 0


def handle_turnstile(tab, max_retries: int = 5, retry_interval: tuple = (1, 2)) -> bool:
    """
    处理Turnstile验证
    
    Args:
        tab: 浏览器标签对象
        max_retries: 最大重试次数
        retry_interval: 重试间隔范围(最小值, 最大值)
        
    Returns:
        bool: 验证是否成功
    """
    info("=============正在检测 Turnstile 验证=============")
    
    # 初始化重试计数器
    retry_count = 0
    
    try:
        # 在最大重试次数内循环尝试验证
        while retry_count < max_retries:
            # 增加重试计数
            retry_count += 1
            info(f"正在进行 Turnstile 第 {retry_count} 次验证中...")
            
            try:
                # 检查页面状态，但不直接返回，先检查是否有Turnstile验证需要处理
                page_ready = False
                if tab.ele("@name=password"):
                    info("检测到密码输入页面，检查是否有验证需要处理...")
                    page_ready = True
                elif tab.ele("@data-index=0"):
                    info("检测到验证码输入页面，检查是否有验证需要处理...")
                    page_ready = True
                elif tab.ele("Account Settings"):
                    info("检测到账户设置页面，检查是否有验证需要处理...")
                    page_ready = True
                
                # 初始化元素变量
                element = None
                try:
                    # 尝试通过层级结构定位到Turnstile验证框的容器元素
                    element = (
                        tab.ele(".main-content")  # 找到 .main-content 元素
                        .ele("tag:div")        # 找到第一个子 div
                        .ele("tag:div")        # 找到第二个子 div
                        .ele("tag:div")        # 找到第三个子 div
                    )
                except Exception as e:
                    # 如果无法通过第一种方式找到元素，忽略异常继续执行
                    pass
                
                challenge_check = None
                if element:
                    # 如果找到了容器元素，则在其中定位验证框的输入元素
                    try:
                        challenge_check = (
                            element
                            .shadow_root.ele("tag:iframe")  # 找到shadow DOM中的iframe
                            .ele("tag:body")                # 找到iframe中的body
                            .sr("tag:input")                # 找到body中的input元素
                        )
                    except Exception as e:
                        pass
                else:
                    # 如果没有找到容器元素，则尝试另一种方式定位验证框
                    try:
                        challenge_check = (
                            tab.ele("@id=cf-turnstile", timeout=2)  # 通过id直接找到turnstile元素
                            .child()                                # 获取其子元素
                            .shadow_root.ele("tag:iframe")          # 找到shadow DOM中的iframe
                            .ele("tag:body")                        # 找到iframe中的body
                            .sr("tag:input")                        # 找到body中的input元素
                        )
                    except Exception as e:
                        pass
                    
                if challenge_check:
                    # 如果找到了验证输入元素，记录日志
                    info("检测到 Turnstile 验证，正在处理...")
                    # 点击前随机延迟，模拟人工操作
                    time.sleep(random.uniform(1, 3))
                    # 点击验证元素触发验证
                    challenge_check.click()
                    # 等待验证处理
                    time.sleep(2)
                    info("Turnstile 验证通过")
                    return True
                else:
                    info("未检测到 Turnstile 验证")
                
                # 如果页面已准备好且没有验证需要处理，则可以返回
                if page_ready:
                    info("页面已准备好，没有检测到需要处理的验证")
                    return True
                    
            except Exception as e:
                # 记录当前尝试失败的详细信息
                info(f"当前验证尝试失败: {str(e)}")
            
            # 在下一次尝试前随机延迟
            time.sleep(random.uniform(*retry_interval))
        
        # 超过最大重试次数，验证失败
        error(f"Turnstile 验证次数超过最大限制 {max_retries}，退出")
        return False
        
    except Exception as e:
        # 捕获整个验证过程中的异常
        error_msg = f"Turnstile 验证失败: {str(e)}"
        error(error_msg)
        return False


def get_cursor_session_token(tab, max_attempts: int = 3, retry_interval: int = 2) -> Optional[Tuple[str, str]]:
    """
    获取Cursor会话token
    
    Args:
        tab: 浏览器标签页对象
        max_attempts: 最大尝试次数
        retry_interval: 重试间隔(秒)
        
    Returns:
        Tuple[str, str] | None: 成功返回(userId, accessToken)元组，失败返回None
    """
    info("开始获取会话令牌")
    
    # 首先尝试使用UUID深度登录方式
    info("尝试使用深度登录方式获取token")
    
    def _generate_pkce_pair():
        """生成PKCE验证对"""
        code_verifier = secrets.token_urlsafe(43)
        code_challenge_digest = hashlib.sha256(code_verifier.encode('utf-8')).digest()
        code_challenge = base64.urlsafe_b64encode(code_challenge_digest).decode('utf-8').rstrip('=')    
        return code_verifier, code_challenge
    
    attempts = 0
    while attempts < max_attempts:
        try:
            verifier, challenge = _generate_pkce_pair()
            id = uuid.uuid4()
            client_login_url = f"https://www.cursor.com/cn/loginDeepControl?challenge={challenge}&uuid={id}&mode=login"
            
            info(f"访问深度登录URL: {client_login_url}")
            tab.get(client_login_url)
            # save_screenshot(tab, f"deeplogin_attempt_{attempts}")
            
            if tab.ele("xpath=//span[contains(text(), 'Yes, Log In')]", timeout=5):
                info("点击确认登录按钮")
                tab.ele("xpath=//span[contains(text(), 'Yes, Log In')]").click()
                time.sleep(1.5)
                
                auth_poll_url = f"https://api2.cursor.sh/auth/poll?uuid={id}&verifier={verifier}"
                headers = {
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Cursor/0.48.6 Chrome/132.0.6834.210 Electron/34.3.4 Safari/537.36",
                    "Accept": "*/*"
                }
                
                info(f"轮询认证状态: {auth_poll_url}")
                response = requests.get(auth_poll_url, headers=headers, timeout=5)
                
                if response.status_code == 200:
                    data = response.json()
                    accessToken = data.get("accessToken", None)
                    authId = data.get("authId", "")
                    
                    if accessToken:
                        userId = ""
                        if len(authId.split("|")) > 1:
                            userId = authId.split("|")[1]
                        
                        info("成功获取账号token和userId")
                        return accessToken, userId
                else:
                    error(f"API请求失败，状态码: {response.status_code}")
            else:
                warning("未找到登录确认按钮")
                
            attempts += 1
            if attempts < max_attempts:
                wait_time = retry_interval * attempts  # 逐步增加等待时间
                warning(f"第 {attempts} 次尝试未获取到token，{wait_time}秒后重试...")
                # save_screenshot(tab, f"token_attempt_{attempts}")
                time.sleep(wait_time)
                
        except Exception as e:
            error(f"深度登录获取token失败: {str(e)}")
            attempts += 1
            # save_screenshot(tab, f"token_error_{attempts}")
            if attempts < max_attempts:
                wait_time = retry_interval * attempts
                warning(f"将在 {wait_time} 秒后重试...")
                time.sleep(wait_time)


def sign_up_account(browser, tab, account_info):
    info("=============开始注册账号=============")
    info(
        f"账号信息: 邮箱: {account_info['email']}, 密码: {account_info['password']}, 姓名: {account_info['first_name']} {account_info['last_name']}"
    )
    if EMAIL_TYPE == "zmail":
        EmailVerificationHandler.create_zmail_email(account_info)
    tab.get(SIGN_UP_URL)

    tab.wait(2)

    if tab.ele("@name=cf-turnstile-response"):
        error("开屏就是检测啊，大佬你的IP或UA需要换一下了啊，有问题了...要等一下")

    try:
        if tab.ele("@name=first_name"):
            info("=============正在填写个人信息=============")
            tab.actions.click("@name=first_name").input(account_info["first_name"])
            info(f"已输入名字: {account_info['first_name']}")
            time.sleep(random.uniform(1, 3))

            tab.actions.click("@name=last_name").input(account_info["last_name"])
            info(f"已输入姓氏: {account_info['last_name']}")
            time.sleep(random.uniform(1, 3))

            tab.actions.click("@name=email").input(account_info["email"])
            info(f"已输入邮箱: {account_info['email']}")
            time.sleep(random.uniform(1, 3))

            info("=============提交个人信息=============")
            tab.actions.click("@type=submit")
            time.sleep(random.uniform(0.2, 1))
            if (
                    tab.ele("verify the user is human. Please try again.")
                    or tab.ele("Can't verify the user is human. Please try again.")
                    or tab.ele("Can't verify the user is human. Please try again.")
            ):
                info("检测到turnstile验证失败，（IP问题、UA问题、域名问题）...正在重试...")
                return "EMAIL_USED"
    except Exception as e:
        info(f"填写个人信息失败: {str(e)}")
        return "ERROR"

    handle_turnstile(tab)

    if tab.ele("verify the user is human. Please try again.") or tab.ele(
        "Can't verify the user is human. Please try again."
    ):
        info("检测到turnstile验证失败，正在重试...")
        return "EMAIL_USED"

    try:
        if tab.ele("@name=password"):
            info(f"设置密码：{account_info['password']}")
            tab.ele("@name=password").input(account_info["password"])
            time.sleep(random.uniform(1, 2))

            info("提交密码...")
            tab.ele("@type=submit").click()
            info("密码设置成功,等待系统响应....")

    except Exception as e:
        info(f"密码设置失败: {str(e)}")
        return "ERROR"

    info("处理最终验证...")
    handle_turnstile(tab)

    if tab.ele("This email is not available."):
        info("邮箱已被使用")
        return "EMAIL_USED"

    if tab.ele("Sign up is restricted."):
        info("注册限制")
        return "SIGNUP_RESTRICTED"

    # 创建邮件处理器
    email_handler = EmailVerificationHandler()
    i = 0
    while i < 5:
        try:
            time.sleep(random.uniform(0.2, 1))
            if tab.ele("Account Settings"):
                info("注册成功，已进入账号设置页面")
                break
            if tab.ele("@data-index=0"):
                info("等待输入验证码...")
                # 切换到邮箱标签页
                code = email_handler.get_verification_code(
                    source_email=account_info["email"]
                )
                if code is None:
                    info("未获取到验证码...系统异常，正在退出....")
                    return "EMAIL_GET_CODE_FAILED"
                info(f"输入验证码: {code}")
                i = 0
                for digit in code:
                    tab.ele(f"@data-index={i}").input(digit)
                    time.sleep(random.uniform(0.3, 0.6))
                    i += 1
                info("验证码输入完成")
                time.sleep(random.uniform(3, 5))

                # 在验证码输入完成后检测是否出现了Turnstile验证
                info("检查是否出现了Turnstile验证...")
                try:
                    turnstile_element = tab.ele("@id=cf-turnstile", timeout=3)
                    if turnstile_element:
                        info("检测到验证码输入后出现Turnstile验证，正在处理...")
                        handle_turnstile(tab)
                except:
                    info("未检测到Turnstile验证，继续下一步")

                break
        except Exception as e:
            info(f"验证码处理失败: {str(e)}")
            return "ERROR"

    info("完成最终验证...")
    handle_turnstile(tab)
    time.sleep(random.uniform(3, 5))
    info("账号注册流程完成")
    return "SUCCESS"


class EmailGenerator:
    def __init__(
        self,
    ):
        # 将密码生成移到这里，避免类定义时执行随机密码生成
        self.default_first_name = self.generate_random_name()
        self.default_last_name = self.generate_random_name()

        # 从配置文件获取域名配置
        self.domains = EMAIL_DOMAINS
        info(f"当前可用域名: {self.domains}")

        self.email = None
        self.password = None

    def generate_random_password(self, length=12):
        """生成随机密码 - 改进密码生成算法，确保包含各类字符"""
        chars = "abcdefghijklmnopqrstuvwxyz"
        upper_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        digits = "0123456789"
        special = "!@#$%^&*"

        # 确保密码包含至少一个大写字母、一个数字和一个特殊字符
        password = [
            random.choice(chars),
            random.choice(upper_chars),
            random.choice(digits),
            random.choice(special),
        ]

        # 添加剩余随机字符
        password.extend(
            random.choices(chars + upper_chars + digits + special, k=length - 4)
        )

        # 打乱密码顺序
        random.shuffle(password)
        return "".join(password)

    def generate_random_name(self, length=6):
        """生成随机用户名"""
        first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
        rest_letters = "".join(
            random.choices("abcdefghijklmnopqrstuvwxyz", k=length - 1)
        )
        return first_letter + rest_letters

    def generate_email(self, length=8):
        """生成随机邮箱地址，使用随机域名"""
        random_str = "".join(
            random.choices("abcdefghijklmnopqrstuvwxyz1234567890", k=length)
        )
        timestamp = str(int(time.time()))[-4:]  # 使用时间戳后4位
        # 随机选择一个域名
        domain = random.choice(self.domains)
        return f"{random_str}@{domain}"

    def get_account_info(self):
        """获取账号信息，确保每次调用都生成新的邮箱和密码"""
        self.email = self.generate_email()
        self.password = self.generate_random_password()
        return {
            "email": self.email,
            "password": self.password,
            "first_name": self.default_first_name.capitalize(),
            "last_name": self.default_last_name.capitalize(),
        }

    def _save_account_info(self, user, token, total_usage):
        try:
            from database import get_session, AccountModel
            import asyncio
            import time

            async def save_to_db():
                info(f"开始保存账号信息: {self.email}")
                async with get_session() as session:
                    # 检查账号是否已存在
                    from sqlalchemy import select

                    result = await session.execute(
                        select(AccountModel).where(AccountModel.email == self.email)
                    )
                    existing_account = result.scalar_one_or_none()

                    if existing_account:
                        info(f"更新现有账号信息 (ID: {existing_account.id})")
                        existing_account.token = token
                        existing_account.user = user
                        existing_account.password = self.password
                        existing_account.usage_limit = str(total_usage)
                        # 如果账号状态是删除，更新为活跃
                        if existing_account.status == "deleted":
                            existing_account.status = "active"
                        # 不更新id，保留原始id值
                    else:
                        info("创建新账号记录")
                        # 生成毫秒级时间戳作为id
                        timestamp_ms = int(time.time() * 1000)
                        account = AccountModel(
                            email=self.email,
                            password=self.password,
                            token=token,
                            user=user,
                            usage_limit=str(total_usage),
                            created_at=datetime.now().strftime("%Y-%m-%d %H:%M"),
                            status="active",  # 设置默认状态为活跃
                            id=timestamp_ms,  # 设置毫秒时间戳id
                        )
                        session.add(account)

                    await session.commit()
                    info(f"账号 {self.email} 信息保存成功")
                    return True

            return asyncio.run(save_to_db())
        except Exception as e:
            info(f"保存账号信息失败: {str(e)}")
            return False


def cleanup_and_exit(browser_manager=None, exit_code=0):
    """清理资源并退出程序"""
    try:
        if browser_manager:
            info("正在关闭浏览器")
            if hasattr(browser_manager, "browser"):
                browser_manager.browser.quit()

        current_process = psutil.Process()
        children = current_process.children(recursive=True)
        for child in children:
            try:
                child.terminate()
            except:
                pass

        info("程序正常退出")
        sys.exit(exit_code)

    except Exception as e:
        info(f"清理退出时发生错误: {str(e)}")
        sys.exit(1)


def main():
    browser_manager = None
    max_retries = REGISTRATION_MAX_RETRIES  # 从配置文件获取
    current_retry = 0

    try:
        email_handler = EmailVerificationHandler()
        if email_handler.check():
            info('邮箱服务连接正常，开始注册!')
        else:
            if EMAIL_CODE_TYPE == "API":
                error('邮箱服务连接失败，并且验证码为API获取，结束注册!')
                return
            else:
                info('邮箱服务连接失败，并且验证码为手动输入，等待输入验证码...')

        email_generator = EmailGenerator()
        browser_manager = BrowserManager()
        browser = browser_manager.init_browser()
        while current_retry < max_retries:
            try:
                account_info = email_generator.get_account_info()
                info(
                    f"初始化账号信息成功 => 邮箱: {account_info['email']}, 用户名: {account_info['first_name']}, 密码: {account_info['password']}"
                )

                signup_tab = browser.new_tab(LOGIN_URL)
                browser.activate_tab(signup_tab)

                signup_tab.run_js("try { turnstile.reset() } catch(e) { }")
                result = sign_up_account(browser, signup_tab, account_info)

                if result == "SUCCESS":
                    token, user = get_cursor_session_token(signup_tab)
                    info(f"获取到账号Token: {token}, 用户: {user}")
                    if token:
                        email_generator._save_account_info(user, token, TOTAL_USAGE)
                        info("注册流程完成")
                        cleanup_and_exit(browser_manager, 0)
                    else:
                        info("获取Cursor会话Token失败")
                        current_retry += 1
                elif result in [
                    "EMAIL_USED",
                    "SIGNUP_RESTRICTED",
                    "VERIFY_FAILED",
                    "EMAIL_GET_CODE_FAILED",
                ]:
                    info(f"遇到问题: {result}，尝试切换邮箱...")
                    continue  # 使用新邮箱重试注册
                else:  # ERROR
                    info("遇到错误，准备重试...")
                    current_retry += 1

                # 关闭标签页，准备下一次尝试
                signup_tab.close()
                time.sleep(2)

            except Exception as e:
                info(f"当前尝试发生错误: {str(e)}")
                current_retry += 1
                time.sleep(2)
                try:
                    # 尝试关闭可能存在的标签页
                    if "signup_tab" in locals():
                        signup_tab.close()
                except:
                    pass

        info(f"达到最大重试次数 {max_retries}，注册失败")
    except Exception as e:
        info(f"主程序错误: {str(e)}")
        info(f"错误详情: {traceback.format_exc()}")
        cleanup_and_exit(browser_manager, 1)
    finally:
        cleanup_and_exit(browser_manager, 1)
