"""
B站登录认证模块
"""
import requests
import qrcode
import time
import json
import os
from pathlib import Path
from cryptography.fernet import Fernet


class BilibiliAuth:
    """B站登录认证管理"""
    
    # API端点
    QRCODE_GENERATE_URL = "https://passport.bilibili.com/x/passport-login/web/qrcode/generate"
    QRCODE_POLL_URL = "https://passport.bilibili.com/x/passport-login/web/qrcode/poll"
    USER_INFO_URL = "https://api.bilibili.com/x/web-interface/nav"
    
    # 通用请求头
    HEADERS = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        'Referer': 'https://www.bilibili.com',
        'Origin': 'https://www.bilibili.com',
    }
    
    def __init__(self, config_dir=None):
        """初始化认证管理器"""
        self.config_dir = config_dir or Path.home() / ".bilibili-dl"
        self.config_dir.mkdir(exist_ok=True)
        self.config_file = self.config_dir / "credentials.json"
        self.key_file = self.config_dir / "secret.key"
        
        # 设置文件权限(仅用户可读写)
        try:
            os.chmod(self.config_dir, 0o700)
        except Exception:
            pass  # Windows可能不支持
        
        self.cipher = self._get_cipher()
        
        # 创建session以复用连接
        self.session = requests.Session()
        self.session.headers.update(self.HEADERS)
    
    def _get_cipher(self):
        """获取加密密钥"""
        if self.key_file.exists():
            key = self.key_file.read_bytes()
        else:
            key = Fernet.generate_key()
            self.key_file.write_bytes(key)
            try:
                os.chmod(self.key_file, 0o600)
            except Exception:
                pass
        return Fernet(key)
    
    def generate_qrcode(self):
        """生成登录二维码"""
        print("正在生成登录二维码...")
        
        try:
            response = self.session.get(self.QRCODE_GENERATE_URL, timeout=10)
            response.raise_for_status()
            
            # 调试信息
            if response.status_code != 200:
                raise Exception(f"HTTP状态码: {response.status_code}")
            
            try:
                data = response.json()
            except json.JSONDecodeError as e:
                print(f"\n❌ JSON解析失败")
                print(f"响应内容: {response.text[:500]}")
                raise Exception(f"API返回非JSON格式数据: {e}")
            
            if data['code'] != 0:
                raise Exception(f"生成二维码失败: {data.get('message', '未知错误')}")
            
            qrcode_url = data['data']['url']
            qrcode_key = data['data']['qrcode_key']
            
            return qrcode_url, qrcode_key
        except requests.RequestException as e:
            raise Exception(f"网络请求失败: {e}")
    
    def display_qrcode(self, qrcode_url):
        """在终端显示二维码"""
        qr = qrcode.QRCode(
            version=1,
            error_correction=qrcode.constants.ERROR_CORRECT_L,
            box_size=1,
            border=1,
        )
        qr.add_data(qrcode_url)
        qr.make(fit=True)
        
        print("\n" + "="*50)
        print("请使用 B站APP 扫描下方二维码登录")
        print("="*50 + "\n")
        
        # 终端打印二维码
        qr.print_ascii(invert=True)
        
        print("\n" + "="*50)
        print("扫码后请在手机上确认登录")
        print("="*50 + "\n")
    
    def poll_qrcode_status(self, qrcode_key, timeout=180):
        """轮询二维码扫描状态"""
        start_time = time.time()
        
        while time.time() - start_time < timeout:
            try:
                response = self.session.get(
                    self.QRCODE_POLL_URL,
                    params={'qrcode_key': qrcode_key},
                    timeout=10
                )
                data = response.json()
                
                code = data['data']['code']
                
                if code == 86101:
                    # 未扫描
                    print("⏳ 等待扫码中...", end='\r', flush=True)
                elif code == 86090:
                    # 已扫描未确认
                    print("📱 已扫描,请在手机上确认登录...", end='\r', flush=True)
                elif code == 0:
                    # 登录成功 - 直接从响应中获取Cookie
                    print("\n✅ 登录成功!                    ")
                    
                    # 新版API会在响应的cookies中直接返回
                    cookies = response.cookies.get_dict()
                    
                    # 如果响应cookies为空,尝试从url重定向获取
                    if not cookies or 'SESSDATA' not in cookies:
                        redirect_url = data['data']['url']
                        return {'type': 'redirect', 'url': redirect_url}
                    
                    return {'type': 'cookies', 'data': cookies}
                    
                elif code == 86038:
                    # 二维码过期
                    raise Exception("二维码已过期,请重新登录")
                else:
                    raise Exception(f"未知状态码: {code}")
                
                time.sleep(2)  # 每2秒轮询一次
            
            except requests.RequestException as e:
                print(f"\n⚠️  网络请求失败: {e}")
                time.sleep(2)
                continue
        
        raise TimeoutError("登录超时,请重新尝试")
    
    def extract_cookies(self, redirect_url):
        """从重定向URL中提取Cookie"""
        try:
            # 尝试多次访问重定向URL以获取Cookie
            response = self.session.get(redirect_url, allow_redirects=True, timeout=10)
            cookies = self.session.cookies.get_dict()
            
            # 如果第一次没有获取到,再尝试访问主站
            if 'SESSDATA' not in cookies:
                response = self.session.get('https://www.bilibili.com', timeout=10)
                cookies = self.session.cookies.get_dict()
            
            if 'SESSDATA' not in cookies:
                raise Exception("获取Cookie失败,请重试")
            
            return cookies
        except requests.RequestException as e:
            raise Exception(f"提取Cookie失败: {e}")
    
    def get_user_info(self, cookies):
        """获取用户信息"""
        try:
            response = self.session.get(
                self.USER_INFO_URL,
                cookies=cookies,
                timeout=10
            )
            data = response.json()
            
            if data['code'] != 0:
                raise Exception("获取用户信息失败")
            
            user_data = data['data']
            return {
                'uid': user_data['mid'],
                'username': user_data['uname'],
                'vip_type': user_data['vipType'],  # 0:无 1:月度大会员 2:年度大会员
                'vip_status': user_data['vipStatus'],  # 1:有效
            }
        except requests.RequestException as e:
            raise Exception(f"获取用户信息失败: {e}")
    
    def save_credentials(self, cookies, user_info):
        """加密保存登录凭证"""
        credentials = {
            'cookies': cookies,
            'user_info': user_info,
            'timestamp': time.time()
        }
        
        encrypted_data = self.cipher.encrypt(
            json.dumps(credentials).encode()
        )
        
        self.config_file.write_bytes(encrypted_data)
        try:
            os.chmod(self.config_file, 0o600)
        except Exception:
            pass
        
        print(f"\n登录信息已保存到: {self.config_file}")
    
    def load_credentials(self):
        """加载已保存的登录凭证"""
        if not self.config_file.exists():
            return None
        
        try:
            encrypted_data = self.config_file.read_bytes()
            decrypted_data = self.cipher.decrypt(encrypted_data)
            credentials = json.loads(decrypted_data.decode())
            
            # 检查Cookie是否过期(30天有效期)
            if time.time() - credentials['timestamp'] > 30 * 24 * 3600:
                print("⚠️  登录已过期,请重新登录")
                return None
            
            return credentials
        except Exception as e:
            print(f"⚠️  读取登录信息失败: {e}")
            return None
    
    def clear_credentials(self):
        """清除登录信息"""
        if self.config_file.exists():
            self.config_file.unlink()
        print("✅ 已退出登录")
    
    def login(self):
        """完整的登录流程"""
        try:
            # 1. 生成二维码
            qrcode_url, qrcode_key = self.generate_qrcode()
            
            # 2. 显示二维码
            self.display_qrcode(qrcode_url)
            
            # 3. 轮询扫码状态
            result = self.poll_qrcode_status(qrcode_key)
            
            # 4. 提取Cookie
            if result['type'] == 'cookies':
                cookies = result['data']
            else:
                cookies = self.extract_cookies(result['url'])
            
            # 5. 获取用户信息
            user_info = self.get_user_info(cookies)
            
            # 6. 保存凭证
            self.save_credentials(cookies, user_info)
            
            # 7. 显示登录信息
            self.display_user_info(user_info)
            
            return True
            
        except Exception as e:
            print(f"\n❌ 登录失败: {e}")
            return False
    
    def display_user_info(self, user_info):
        """显示用户信息"""
        print("\n" + "="*50)
        print("登录信息")
        print("="*50)
        print(f"用户名: {user_info['username']}")
        print(f"UID: {user_info['uid']}")
        
        vip_status = "❌ 非会员"
        if user_info['vip_status'] == 1:
            if user_info['vip_type'] == 2:
                vip_status = "👑 年度大会员"
            elif user_info['vip_type'] == 1:
                vip_status = "💎 月度大会员"
        
        print(f"会员状态: {vip_status}")
        print("="*50 + "\n")
        
        if user_info['vip_status'] == 1:
            print("✨ 可下载大会员专属清晰度(1080P+/4K)")
        else:
            print("ℹ️  当前可下载普通清晰度(最高1080P)")
