import aiohttp
import os
import zipfile
import shutil
from astrbot.core.utils.io import on_error, download_file
from astrbot.core import logger

class ReleaseInfo():
    version: str
    published_at: str
    body: str
    
    def __init__(self, version: str = '', published_at: str = '', body: str = '') -> None:
        self.version = version
        self.published_at = published_at
        self.body = body
    
    def __str__(self) -> str:
        return f"新版本: {self.version}, 发布于: {self.published_at}, 详细内容: {self.body}"

class RepoZipUpdator():
    def __init__(self, repo_mirror: str = "") -> None:
        self.repo_mirror = repo_mirror
        self.rm_on_error = on_error
            
    async def fetch_release_info(self, url: str, latest: bool = True) -> list:
        '''
        请求版本信息。
        返回一个列表，每个元素是一个字典，包含版本号、发布时间、更新内容、commit hash等信息。
        '''
        try:
            async with aiohttp.ClientSession(trust_env=True) as session:
                async with session.get(url) as response:
                    result = await response.json()
            if not result: 
                return []
            # if latest:
            #     ret = self.github_api_release_parser([result[0]])
            # else:
            #     ret = self.github_api_release_parser(result)
            ret = []
            for release in result:
                ret.append({
                    "version": release['name'],
                    "published_at": release['published_at'],
                    "body": release['body'],
                    "tag_name": release['tag_name'],
                    "zipball_url": release['zipball_url']
                })
        except BaseException:
            raise Exception("解析版本信息失败")
        return ret
    
    def github_api_release_parser(self, releases: list) -> list:
        '''
        解析 GitHub API 返回的 releases 信息。
        返回一个列表，每个元素是一个字典，包含版本号、发布时间、更新内容、commit hash等信息。
        '''
        ret = []
        for release in releases:
            ret.append({
                "version": release['name'],
                "published_at": release['published_at'],
                "body": release['body'],
                "tag_name": release['tag_name'],
                "zipball_url": release['zipball_url']
            })
        return ret

    def unzip(self):
        raise NotImplementedError()
    
    async def update(self):
        raise NotImplementedError()
    
    def compare_version(self, v1: str, v2: str) -> int:
        '''
        比较两个版本号的大小。
        返回 1 表示 v1 > v2，返回 -1 表示 v1 < v2，返回 0 表示 v1 = v2。
        '''
        v1 = v1.replace('v', '')
        v2 = v2.replace('v', '')
        v1 = v1.split('.')
        v2 = v2.split('.')

        for i in range(3):
            if int(v1[i]) > int(v2[i]):
                return 1
            elif int(v1[i]) < int(v2[i]):
                return -1
        return 0
    
    async def check_update(self, url: str, current_version: str) -> ReleaseInfo:
        update_data = await self.fetch_release_info(url)
        tag_name = update_data[0]['tag_name']
        
        if self.compare_version(current_version, tag_name) >= 0:
            return None
        return ReleaseInfo(
            version=tag_name,
            published_at=update_data[0]['published_at'],
            body=update_data[0]['body']
        )
        
    async def download_from_repo_url(self, target_path: str, repo_url: str, proxy=""):
        repo_namespace = repo_url.split("/")[-2:]
        author = repo_namespace[0]
        repo = repo_namespace[1]

        logger.info(f"正在下载更新 {repo} ...")
        release_url = f"https://api.github.com/repos/{author}/{repo}/releases"
        releases = await self.fetch_release_info(url=release_url)
        if not releases:
            # download from the default branch directly. 
            logger.info(f"正在从默认分支下载 {author}/{repo} ")
            release_url = f"https://github.com/{author}/{repo}/archive/refs/heads/master.zip"
        else:
            release_url = releases[0]['zipball_url']
        
        if proxy:
            release_url = f"{proxy}/{release_url}"
            logger.info(f"使用代理下载: {release_url}")

        await download_file(release_url, target_path + ".zip")
        
        
    def unzip_file(self, zip_path: str, target_dir: str):
        '''
        解压缩文件, 并将压缩包内**第一个**文件夹内的文件移动到 target_dir
        '''
        os.makedirs(target_dir, exist_ok=True)
        update_dir = ""
        logger.info(f"解压文件: {zip_path}")
        with zipfile.ZipFile(zip_path, 'r') as z:
            update_dir = z.namelist()[0]
            z.extractall(target_dir)

        files = os.listdir(os.path.join(target_dir, update_dir))
        for f in files:
            logger.info(f"移动更新文件/目录: {f}")
            if os.path.isdir(os.path.join(target_dir, update_dir, f)):
                if os.path.exists(os.path.join(target_dir, f)):
                    shutil.rmtree(os.path.join(target_dir, f), onerror=on_error)
            else:
                if os.path.exists(os.path.join(target_dir, f)):
                    os.remove(os.path.join(target_dir, f))
            shutil.move(os.path.join(target_dir, update_dir, f), target_dir)
        
        try:
            logger.info(f"删除临时更新文件: {zip_path} 和 {os.path.join(target_dir, update_dir)}")
            shutil.rmtree(os.path.join(target_dir, update_dir), onerror=on_error)
            os.remove(zip_path)
        except BaseException:
            logger.warn(f"删除更新文件失败，可以手动删除 {zip_path} 和 {os.path.join(target_dir, update_dir)}")

    def format_repo_name(self, repo_url: str) -> str:
        if repo_url.endswith("/"):
            repo_url = repo_url[:-1]

        repo_namespace = repo_url.split("/")[-2:]
        repo = repo_namespace[1]
        
        repo = self.format_name(repo)

        return repo
    
    def format_name(self, name: str) -> str:
        return name.replace("-", "_").lower()