#!/usr/bin/env python3
"""
Compare package versions in the smdlinux 3party repo against AUR.

Reads the repo database (.db.tar.gz) rather than the raw directory listing,
since the directory can contain stale/superseded package files while the
database reflects the actual current version served by the repo.
"""

import argparse
import json
import subprocess
import sys
import tarfile
import urllib.parse
import urllib.request
from pathlib import Path

AUR_RPC_URL = "https://aur.archlinux.org/rpc/v5/info"
BATCH_SIZE = 100  # AUR RPC accepts many arg[] params per request, keep it sane


def parse_repo_db(db_path: Path) -> dict[str, str]:
    """Return {pkgname: pkgver-pkgrel} from a repo .db.tar.gz file."""
    packages = {}
    with tarfile.open(db_path, "r:gz") as tar:
        for member in tar.getmembers():
            if not member.name.endswith("/desc"):
                continue
            content = tar.extractfile(member).read().decode("utf-8", "replace")
            fields = content.split("\n\n")
            name = None
            version = None
            for field in fields:
                lines = field.strip().split("\n")
                if not lines or not lines[0]:
                    continue
                key = lines[0]
                value = lines[1] if len(lines) > 1 else ""
                if key == "%NAME%":
                    name = value
                elif key == "%VERSION%":
                    version = value
            if name and version:
                packages[name] = version
    return packages


def query_aur(pkgnames: list[str]) -> dict[str, str]:
    """Return {pkgname: version} for packages found on AUR."""
    results = {}
    for i in range(0, len(pkgnames), BATCH_SIZE):
        chunk = pkgnames[i : i + BATCH_SIZE]
        query = "&".join(
            "arg[]=" + urllib.parse.quote(name) for name in chunk
        )
        url = f"{AUR_RPC_URL}?{query}"
        with urllib.request.urlopen(url, timeout=30) as resp:
            data = json.load(resp)
        for entry in data.get("results", []):
            results[entry["Name"]] = entry["Version"]
    return results


def vercmp(v1: str, v2: str) -> int:
    """Wrap pacman's vercmp: returns -1, 0, or 1."""
    out = subprocess.run(
        ["vercmp", v1, v2], capture_output=True, text=True, check=True
    ).stdout.strip()
    return int(out)


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "--repo-dir",
        default=Path(__file__).resolve().parent,
        type=Path,
        help="Path to the repo x86_64 directory (default: script's own directory)",
    )
    parser.add_argument(
        "--db-name",
        default="smdlinux_repo_3party.db.tar.gz",
        help="Repo database filename to read (default: %(default)s)",
    )
    parser.add_argument(
        "--all",
        action="store_true",
        help="Show all packages, not just outdated ones",
    )
    args = parser.parse_args()

    db_path = args.repo_dir / args.db_name
    if not db_path.exists():
        sys.exit(f"error: repo database not found at {db_path}")

    local_pkgs = parse_repo_db(db_path)
    print(f"Found {len(local_pkgs)} packages in local repo database.", file=sys.stderr)

    print("Querying AUR...", file=sys.stderr)
    aur_pkgs = query_aur(list(local_pkgs.keys()))
    print(f"Found {len(aur_pkgs)} matching packages on AUR.", file=sys.stderr)

    outdated = []
    up_to_date = []
    newer_local = []
    not_on_aur = []

    for name, local_ver in sorted(local_pkgs.items()):
        aur_ver = aur_pkgs.get(name)
        if aur_ver is None:
            not_on_aur.append(name)
            continue
        cmp = vercmp(local_ver, aur_ver)
        if cmp < 0:
            outdated.append((name, local_ver, aur_ver))
        elif cmp > 0:
            newer_local.append((name, local_ver, aur_ver))
        else:
            up_to_date.append((name, local_ver, aur_ver))

    def print_table(title, rows):
        if not rows:
            return
        print(f"\n{title} ({len(rows)})")
        print("-" * 70)
        for name, local_ver, aur_ver in rows:
            print(f"{name:<30} local: {local_ver:<20} aur: {aur_ver}")

    print_table("OUTDATED (AUR has a newer version)", outdated)
    print_table("LOCAL NEWER THAN AUR", newer_local)

    if args.all:
        print_table("UP TO DATE", up_to_date)

    if not_on_aur:
        print(f"\nNOT FOUND ON AUR ({len(not_on_aur)}, likely custom/official-repo packages)")
        print("-" * 70)
        for name in not_on_aur:
            print(name)

    print(
        f"\nSummary: {len(outdated)} outdated, {len(newer_local)} local-newer, "
        f"{len(up_to_date)} up to date, {len(not_on_aur)} not on AUR "
        f"(of {len(local_pkgs)} total)"
    )


if __name__ == "__main__":
    main()
