#!/usr/bin/env python3

import argparse
import json
import logging
import os
import requests
import re
import threading
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor, as_completed

# 全局配置变量
CONFIG = {
    "data_dir": "data",
    "output_dir": ".",
    "proxy": "",
    "thread": 5,
    "architecture": "amd64",
    "dry_run": False,
}
version_lock = threading.Lock()
# 日志等级，若需要展示每次请求结果请使用 INFO 等级
# logging.basicConfig(level=logging.INFO)


# 读取命令行参数
def read_args():
    parser = argparse.ArgumentParser(
        description="Github Downloader - Github Releases 更新下载器"
    )
    parser.add_argument(
        "-a", "--arch", default="amd64", help="架构，用于包的命名，默认是 amd64"
    )
    parser.add_argument("-d", "--data", default="data", help="从 <DATA> 读取仓库配置")
    parser.add_argument(
        "-o", "--output", default=".", help="将文件保存到 <OPTPUT>，默认为当前文件夹"
    )
    parser.add_argument(
        "-p", "--proxy", default="", help="Github 代理，<PROXY> 必须以 / 结尾"
    )
    parser.add_argument(
        "-t", "--thread", type=int, default=5, help="并发下载线程数量，默认为 5"
    )
    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="运行但不下载文件",
    )

    args = parser.parse_args()

    CONFIG.update(
        {
            "architecture": args.arch,
            "data_dir": args.data,
            "output_dir": args.output,
            "proxy": args.proxy,
            "thread": args.thread,
            "dry_run": args.dry_run,
        }
    )


# 读取 JSON 文件
def read_json(filename):
    try:
        with open(os.path.join(CONFIG["data_dir"], filename), "r") as f:
            return json.load(f)
    except (FileNotFoundError, json.JSONDecodeError):
        return {}


# 保存到 github-local.json
def save_version(version_list, filename):
    with open(os.path.join(CONFIG["data_dir"], filename), "w") as f:
        json.dump(version_list, f, indent=4)


# 获取最新版本标签
def latest_version_tag(repo_url):
    url = f"{repo_url}/releases/latest"
    try:
        response = requests.head(url)
        location = response.headers.get("Location", "")
        match = re.search(r".*releases/tag/([^/]+)", location)
        return match.group(1) if match else ""
    except requests.RequestException:
        return ""


# 下载文件
def download(url, file_path):
    # 检查是否为 dry-run 模式
    if CONFIG["dry_run"]:
        response = requests.head(url, allow_redirects=True)
        if response.status_code != 200:
            logging.error(
                f"Can't download {url} because received {response.status_code}"
            )
            return False, ""
        size = int(response.headers.get("Content-Length", 0))
        # 记录文件大小
        # size use kB/MB not KiB/MiB
        size_str = (
            f"{size / 1000:.2f}kB" if size < 1000000 else f"{size / 1000000:.2f}MB"
        )
        logging.info(f"Dry-run download: {url}")
        return True, size_str

    response = requests.get(url, stream=True)
    if response.status_code != 200:
        logging.error(f"Can't download {url} because received {response.status_code}")
        return False, ""
    # 记录文件大小
    size = int(response.headers.get("Content-Length", 0))
    size_str = f"{size / 1000:.2f}kB" if size < 1000000 else f"{size / 1000000:.2f}MB"
    logging.info(f"Downloading: {url}")

    with open(file_path, "wb") as f:
        for chunk in response.iter_content(chunk_size=8192):
            f.write(chunk)
    return True, size_str


# 格式化文件名
def format_file_name(name, version):
    return f"{name}_{version}_{CONFIG['architecture']}.deb"


# 检查版本并下载新版本文件
def check(name, repo, version_list):
    repo_url = f"{CONFIG['proxy']}https://github.com/{repo['repo']}"
    version_tag = latest_version_tag(repo_url)
    if version_tag == "":
        logging.error(f"Can't get latest version tag of {name}")
        return
    logging.info(f"{name} = {version_tag}")
    # 判断是否需要更新
    local_version = version_list.get(name, "")
    if local_version != version_tag:
        # 确定本地文件目录并确保目录存在
        file_dir = os.path.join(CONFIG["output_dir"], name)
        os.makedirs(file_dir, exist_ok=True)
        # 拼接得到文件名
        release_file = str.format(
            repo["file_template"],
            version_tag=version_tag,  # 若存在则用完整 tag 替换
            stripped_version=version_tag[1:],  # 若存在则用删去首字母的 tag 替换
        )
        file_url = f"{repo_url}/releases/download/{version_tag}/{release_file}"
        version = version_tag[1:] if version_tag[0].lower() == "v" else version_tag
        filename = format_file_name(name, version)
        file_path = os.path.join(file_dir, filename)

        downloaded, size = download(file_url, file_path)
        if downloaded:
            # 判断是否是新添加应用
            if local_version == "":
                print(f"AddNew: {name} ({version_tag})")
            else:
                print(f"Update: {name} ({local_version} -> {version_tag})")
                # 删除旧版本文件
                old_file_path = os.path.join(
                    file_dir, format_file_name(name, local_version)
                )
                if os.path.exists(old_file_path):
                    os.remove(old_file_path)
            # 更新 app.json
            repo["app.json"].update(
                {
                    "Pkgname": name,
                    "Version": version,
                    "Filename": filename,
                    "Update": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                    "Size": size,
                    "Contributor": "https://gitee.com/spark-building-service/github",
                }
            )
            # 保存 app.json
            with open(os.path.join(file_dir, "app.json.update"), "w") as f:
                json.dump(repo["app.json"], f, indent=4, ensure_ascii=False)
            # 更新版本号
            with version_lock:
                version_list[name] = version_tag


if __name__ == "__main__":
    read_args()
    repo_list = read_json("github.json")
    version_list = read_json("github-local.json")
    with ThreadPoolExecutor(max_workers=CONFIG["thread"]) as executor:
        futures = [
            executor.submit(check, name, repo, version_list)
            for name, repo in repo_list.items()
        ]
        for future in as_completed(futures):
            future.result()

    save_version(version_list, "github-local.json")
