"""
B站视频信息解析模块
"""
import re
import requests
from typing import Dict, List, Optional


class BilibiliParser:
    """B站视频信息解析器"""
    
    # API端点
    VIDEO_INFO_URL = "https://api.bilibili.com/x/web-interface/view"
    PLAY_URL = "https://api.bilibili.com/x/player/playurl"
    
    # User-Agent
    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'
    }
    
    # 清晰度映射
    QUALITY_MAP = {
        16: "360P",
        32: "480P",
        64: "720P",
        80: "1080P",
        112: "1080P+",
        116: "1080P60",
        120: "4K",
        125: "HDR",
        126: "杜比视界",
    }
    
    def __init__(self, cookies: Optional[Dict] = None):
        """初始化解析器"""
        self.cookies = cookies or {}
        self.session = requests.Session()
        self.session.headers.update(self.HEADERS)
        if self.cookies:
            self.session.cookies.update(self.cookies)
    
    @staticmethod
    def extract_bvid(url_or_bvid: str) -> tuple:
        """从URL或字符串中提取BV号和分P参数
        
        Returns:
            (bvid, page_number): BV号和分P编号的元组，page_number默认为1
        """
        # 如果已经是纯BV号（不包含查询参数）
        if url_or_bvid.startswith('BV') and '?' not in url_or_bvid:
            return url_or_bvid, 1
        
        # 提取分P参数（匹配 p=纯数字 格式，取所有匹配中的最后一个纯数字）
        page_matches = re.findall(r'[?&]p=(\d+)(?:&|$|(?=[&#]))', url_or_bvid)
        # 过滤掉超长的数字（可能是其他参数），通常分P不会超过1000
        valid_pages = [int(p) for p in page_matches if len(p) <= 4]
        page_number = valid_pages[-1] if valid_pages else 1
        
        # 清理查询参数后再提取BV号
        clean_url = re.sub(r'\?.*$', '', url_or_bvid)
        
        # 从URL中提取BV号
        patterns = [
            r'bilibili\.com/video/(BV[a-zA-Z0-9]+)',
            r'BV[a-zA-Z0-9]+',
            r'b23\.tv/(.+)',
        ]
        
        for pattern in patterns:
            match = re.search(pattern, clean_url)
            if match:
                bvid = match.group(1) if 'bilibili.com' in url_or_bvid else match.group(0)
                if bvid.startswith('BV'):
                    return bvid, page_number
        
        raise ValueError(f"无法从 '{url_or_bvid}' 中提取BV号")
    
    def get_video_info(self, bvid: str) -> Dict:
        """获取视频基本信息"""
        try:
            response = self.session.get(
                self.VIDEO_INFO_URL,
                params={'bvid': bvid},
                timeout=10
            )
            data = response.json()
            
            if data['code'] != 0:
                raise Exception(f"获取视频信息失败: {data.get('message', '未知错误')}")
            
            video_data = data['data']
            
            return {
                'bvid': video_data['bvid'],
                'aid': video_data['aid'],
                'title': video_data['title'],
                'desc': video_data['desc'],
                'duration': video_data['duration'],
                'owner': {
                    'name': video_data['owner']['name'],
                    'mid': video_data['owner']['mid']
                },
                'pic': video_data['pic'],  # 封面图
                'cid': video_data['cid'],  # 默认分P的cid
                'pages': video_data.get('pages', []),  # 分P信息
            }
        except requests.RequestException as e:
            raise Exception(f"网络请求失败: {e}")
    
    def get_video_stream(self, bvid: str, cid: int, quality: int = 80) -> Dict:
        """获取视频流信息
        
        Args:
            bvid: 视频BV号
            cid: 视频分P的cid
            quality: 清晰度代码 (16:360P, 32:480P, 64:720P, 80:1080P, 116:1080P60, 120:4K)
        
        Returns:
            视频流信息字典
        """
        try:
            params = {
                'bvid': bvid,
                'cid': cid,
                'qn': quality,
                'fnval': 16,  # 1:mp4 16:dash
                'fourk': 1,
            }
            
            response = self.session.get(
                self.PLAY_URL,
                params=params,
                timeout=10
            )
            data = response.json()
            
            if data['code'] != 0:
                raise Exception(f"获取视频流失败: {data.get('message', '未知错误')}")
            
            play_data = data['data']
            
            # 解析DASH格式
            if 'dash' in play_data:
                dash = play_data['dash']
                
                # 视频流
                video_streams = []
                for video in dash.get('video', []):
                    video_streams.append({
                        'quality': video['id'],
                        'quality_name': self.QUALITY_MAP.get(video['id'], f"未知({video['id']})"),
                        'url': video['baseUrl'],
                        'backup_urls': video.get('backupUrl', []),
                        'codecs': video['codecs'],
                        'bandwidth': video.get('bandwidth', 0),
                        'width': video.get('width', 0),
                        'height': video.get('height', 0),
                    })
                
                # 音频流
                audio_streams = []
                for audio in dash.get('audio', []):
                    audio_streams.append({
                        'quality': audio['id'],
                        'url': audio['baseUrl'],
                        'backup_urls': audio.get('backupUrl', []),
                        'codecs': audio['codecs'],
                        'bandwidth': audio.get('bandwidth', 0),
                    })
                
                return {
                    'type': 'dash',
                    'video_streams': video_streams,
                    'audio_streams': audio_streams,
                    'accept_quality': play_data.get('accept_quality', []),
                    'accept_description': play_data.get('accept_description', []),
                }
            
            # 解析MP4格式(旧版)
            elif 'durl' in play_data:
                return {
                    'type': 'mp4',
                    'url': play_data['durl'][0]['url'],
                    'quality': play_data.get('quality', quality),
                }
            
            else:
                raise Exception("未知的视频格式")
                
        except requests.RequestException as e:
            raise Exception(f"网络请求失败: {e}")
    
    def get_available_qualities(self, bvid: str, cid: int) -> List[Dict]:
        """获取可用的清晰度列表"""
        stream_info = self.get_video_stream(bvid, cid, quality=120)
        
        if stream_info['type'] == 'dash':
            qualities = []
            for qid in stream_info['accept_quality']:
                qualities.append({
                    'id': qid,
                    'name': self.QUALITY_MAP.get(qid, f"未知({qid})")
                })
            return qualities
        else:
            return [{'id': stream_info['quality'], 'name': self.QUALITY_MAP.get(stream_info['quality'], '默认')}]
    
    def select_stream(self, stream_info: Dict, target_quality: int = 80) -> Dict:
        """选择最佳的视频和音频流
        
        Args:
            stream_info: get_video_stream返回的流信息
            target_quality: 目标清晰度
        
        Returns:
            {'video_url': str, 'audio_url': str, 'quality_name': str}
        """
        if stream_info['type'] == 'mp4':
            return {
                'video_url': stream_info['url'],
                'audio_url': None,
                'quality_name': self.QUALITY_MAP.get(stream_info['quality'], '默认'),
                'merge_needed': False,
            }
        
        # DASH格式需要选择视频和音频
        video_streams = stream_info['video_streams']
        audio_streams = stream_info['audio_streams']
        
        # 选择目标清晰度的视频流
        selected_video = None
        for video in video_streams:
            if video['quality'] == target_quality:
                selected_video = video
                break
        
        # 如果目标清晰度不可用,选择最高清晰度
        if not selected_video and video_streams:
            selected_video = max(video_streams, key=lambda x: x['quality'])
        
        # 选择最高质量的音频流
        selected_audio = None
        if audio_streams:
            selected_audio = max(audio_streams, key=lambda x: x.get('bandwidth', 0))
        
        if not selected_video:
            raise Exception("没有可用的视频流")
        
        return {
            'video_url': selected_video['url'],
            'audio_url': selected_audio['url'] if selected_audio else None,
            'quality_name': selected_video['quality_name'],
            'merge_needed': selected_audio is not None,
        }
