'''`bestdori.songs`

BanG Dream! 歌曲相关操作'''
from typing import Optional, Literal, Any
from requests.exceptions import HTTPError

from .charts import Chart
from .post import get_list
from .utils.utils import ASSETS, API
from .utils.network import Assets, Api
from .exceptions import (
    DiffNotExistError,
    SongNotExistError
)

# 获取总歌曲信息
def get_all(index: Literal[0, 5]=5, proxy: Optional[str]=None) -> dict[str, dict[str, Any]]:
    '''获取总歌曲信息

    参数:
        index (Literal[0, 5], optional): 指定获取哪种 `all.json`
            `0`: 仅获取所有已有歌曲 ID `all.0.json`
            `5`: 获取所有已有歌曲的简洁信息 `all.5.json`，默认为该项
        
        proxy (Optional[str], optional): 代理服务器

    返回:
        dict[str, dict[str, Any]]: 获取到的总歌曲信息
    '''
    return Api(API['songs']['all'].format(index=index), proxy=proxy).request('get').json()

# 歌曲类
class Song:
    '''歌曲类

    参数:
        id_ (int): 歌曲 ID
        
        proxy (Optional[str], optional): 代理服务器
    '''
    # 初始化
    def __init__(self, id_: int, proxy: Optional[str]=None) -> None:
        '''歌曲类

        参数:
            id_ (int): 歌曲 ID
            
            proxy (Optional[str], optional): 代理服务器
        '''
        self.id: int = id_
        '''歌曲 ID'''
        self._info: dict[str, Any] = {}
        '''歌曲信息'''
        self.proxy: Optional[str] = proxy
        '''代理服务器'''
        # 检测 ID 是否存在
        all_id = get_all(0, proxy=proxy)
        if not str(id_) in all_id.keys():
            raise SongNotExistError(id_)
        return
    
    # 获取歌曲信息
    def get_info(self) -> dict[str, Any]:
        '''获取歌曲信息

        返回:
            dict[str, Any]: 歌曲详细信息
        '''
        if len(self._info) <= 0:
            # 如果没有歌曲信息存储
            response = Api(
                API['songs']['info'].format(id=self.id), proxy=self.proxy
            ).request('get')
            self._info = dict(response.json())
        return self._info
    
    # 获取歌曲所在服务器
    @property
    def server(self) -> Literal['jp', 'en', 'tw', 'cn', 'kr']:
        '''获取歌曲所在服务器

        返回:
            Literal[&#39;jp&#39;, &#39;en&#39;, &#39;tw&#39;, &#39;cn&#39;, &#39;kr&#39;]: 歌曲所在服务器
        '''
        info = self.get_info()
        # 获取 publishedAt 数据
        if (published_at := info.get('publishedAt', None)) is None:
            raise Exception('无法获取歌曲发布时间。')
        # 根据 publishedAt 数据判断服务器
        if published_at[0] is not None: return 'jp'
        elif published_at[1] is not None: return 'en'
        elif published_at[2] is not None: return 'tw'
        elif published_at[3] is not None: return 'cn'
        elif published_at[4] is not None: return 'kr'
        else:
            raise Exception('无法获取歌曲服务器。')
    
    # 获取歌曲名称
    @property
    def name(self) -> str:
        '''获取歌曲名称

        返回:
            str: 歌曲名称
        '''
        info = self.get_info()
        # 获取 musicTitle 数据
        if (music_title := info.get('musicTitle', None)) is None:
            raise Exception('无法获取歌曲名称。')
        # 获取第一个非 None 歌曲名称
        try:
            return next(filter(lambda x: x is not None, music_title))
        except StopIteration:
            raise Exception('无法获取歌曲名称。')
    
    # 获取歌曲谱面
    def get_chart(
        self,
        diff: Literal['easy', 'normal', 'hard', 'expert', 'special']='expert'
    ) -> Chart:
        '''获取歌曲谱面

        参数:
            diff (Literal[&#39;easy&#39;, &#39;normal&#39;, &#39;hard&#39;, &#39;expert&#39;, &#39;special&#39;], optional): 难度名称

        返回:
            Chart: 获取到的谱面对象
        '''
        try:
            chart = Chart.get_chart(self.id, diff, self.proxy)
            return chart
        except HTTPError:
            # 难度不存在
            raise DiffNotExistError(diff)
    
    # 获取歌曲封面
    def get_jacket(self) -> list[bytes]:
        '''获取歌曲封面

        返回:
            bytes: 歌曲封面字节数据 `bytes`
        '''
        # 获取数据包序列号
        quotient, remainder = divmod(int(self.id), 10)
        if remainder == 0:
            index = self.id
        else:
            index = str((quotient + 1) * 10)
        
        info = self.get_info()
        if (jacket_image := info.get('jacketImage', None)) is None:
            raise Exception('歌曲封面资源未找到。')
        jacket: list[bytes] = []
        
        for image in jacket_image:
            jacket.append(
                Assets(
                    ASSETS['songs']['musicjacket'].format(
                        index=index, jacket_image=image
                    ), self.server, self.proxy
                ).get()
            )
        
        return jacket
    
    # 获取歌曲音频
    def get_bgm(self) -> bytes:
        '''获取歌曲音频

        返回:
            bytes: 歌曲音频字节数据 `bytes`
        '''
        return Assets(ASSETS['songs']['sound'].format(id=self.id), self.server, self.proxy).get()
    
    # 获取歌曲评论
    def get_comment(
        self,
        limit: int=20,
        offset: int=0,
        order: Literal['TIME_DESC', 'TIME_ASC']='TIME_ASC'
    ) -> dict[str, Any]:
        '''获取歌曲评论

        参数:
            limit (int, optional): 展示出的评论数，默认为 20
            
            offset (int, optional): 忽略前面的 `offset` 条评论，默认为 0
            
            order (Literal[&#39;TIME_DESC&#39;, &#39;TIME_ASC&#39;], optional): 排序顺序，默认时间顺序

        返回:
            dict[str, Any]: 搜索结果
            ```python
            result: bool # 是否有响应
            count: int # 搜索到的评论总数
            posts: list[dict[str, Any]] # 列举出的评论
            ```
        '''
        return get_list(
            proxy=self.proxy,
            category_name='SONG_COMMENT',
            category_id=str(self.id),
            order=order,
            limit=limit,
            offset=offset
        )
