#!/usr/bin/env python3
import argparse
from functools import cmp_to_key
import glob
import lxml.etree
import os
from nullroute.scrape import Scraper, urljoin
import requests
import sys
import subprocess
from urllib.parse import unquote as urlunquote

class Downgrader(Scraper):
    def enum_cache(self, pkg):
        for path in glob.glob("/var/cache/pacman/pkg/%s-*.pkg.tar*" % pkg):
            name = os.path.basename(path)
            if pkg_matches_filename(pkg, name):
                yield {
                    "name": name,
                    "src": "cache",
                    "url": path,
                }

    def enum_ala(self, pkg):
        page_url = "https://archive.archlinux.org/packages/%s/%s/" % (pkg[0], pkg)
        try:
            page = self.get_page(page_url)
        except requests.exceptions.HTTPError:
            return
        for tag in page.select("a"):
            href = str(tag["href"])
            name = urlunquote(os.path.basename(href))
            if pkg_matches_filename(pkg, name):
                yield {
                    "name": name,
                    "src": "ala",
                    "url": urljoin(page_url, href),
                }

    def enum_ia(self, pkg):
        page_url = "https://archive.org/download/archlinux_pkg_%s/" % pkg
        meta_url = page_url + "archlinux_pkg_%s_files.xml" % pkg
        try:
            page = self.get(meta_url)
        except requests.exceptions.HTTPError:
            return
        meta = page.content
        meta = lxml.etree.fromstring(meta)
        for name in meta.xpath("/files/file/@name"):
            if pkg_matches_filename(pkg, name):
                yield {
                    "name": name,
                    "src": "ia",
                    "url": urljoin(page_url, name),
                }

    def enum_all(self, pkg):
        yield from self.enum_cache(pkg)
        yield from self.enum_ala(pkg)
        yield from self.enum_ia(pkg)

    def enum_compatible(self, pkgname, want_arch):
        seen = set()
        for rec in self.enum_all(pkgname):
            file = rec["name"]
            (name, ver, rel, arch) = parse_pkg_filename(file)
            if arch != this_arch:
                continue
            if file in seen:
                continue
            seen.add(file)
            yield {**rec, "ver": ver+"-"+rel}

def parse_pkg_filename(pkg):
    pkg = pkg.rsplit(".pkg.tar", 1)[0]
    name, ver, rel, arch = pkg.rsplit("-", 3)
    return (name, ver, rel, arch)

def pkg_matches_filename(pkg, filename):
    if filename.endswith((".part", ".sig")):
        return False
    if ".pkg.tar" not in filename:
        return False
    pkgname, *_ = parse_pkg_filename(filename)
    return pkgname == pkg

def cmp_version(a, b):
    res = subprocess.run(["vercmp", a, b], stdout=subprocess.PIPE)
    return int(res.stdout.strip())

def sort_by_version(alist, key):
    return sorted(alist, key=key)

def show_choices(alist):
    for i, text in enumerate(alist):
        print("%4d  %s" % (i+1, text))
    choice = input("> ")
    if choice in {"l", "last", "latest"}:
        return len(alist)-1
    else:
        return int(choice)-1

def install_from_url(url, *, asdeps=False):
    deps_arg = ["--asdeps"] if asdeps else []
    cmd = ["sudo", "pacman", "-U", *deps_arg, url]
    print(cmd)
    subprocess.run(cmd)

parser = argparse.ArgumentParser()
parser.add_argument("-d", "--download", action="store_true",
                    help="only download the package")
parser.add_argument("--asdeps", action="store_true",
                    help="mark installed package as dependency")
parser.add_argument("package")
args = parser.parse_args()

d = Downgrader()
this_arch = os.popen("pacman-conf Architecture").read().strip()

pkg = args.package
results = d.enum_compatible(pkg, this_arch)
results = [*results]
if results:
    cmp = lambda a, b: cmp_version(a["ver"], b["ver"])
    results = sorted(results, key=cmp_to_key(cmp))
    menu = ["%(ver)-20s <%(url)s>" % r for r in results]
    choice = show_choices(menu)
    chosen = results[choice]
    url = chosen["url"]
    if args.download:
        print("Saving", chosen["url"])
        d.save_file(chosen["url"],
                    name=os.path.basename(chosen["url"]),
                    progress=True,
                    save_msg=True,
                    keep_mtime=True)
    else:
        install_from_url(chosen["url"], asdeps=args.asdeps)
else:
    exit("error: No results for %r." % pkg)
