#!/usr/bin/python3

import glob
import os
import subprocess
import sys

from datetime import datetime

# guard against humans running this
if len(sys.argv) == 1 or sys.argv[1] == "-h" or sys.argv[1] == "--help":
    print("This script should not be run by hand. It manages the local repository")
    print("for Homefarm.")
    sys.exit(0)

localrepo = sys.argv[1]
mirrorurl = sys.argv[2]

# switch to the local repo directory
os.chdir(localrepo)
# open log
log = open("update.log", 'w', buffering=1)
log.write("Beginning run: {}\n".format(datetime.now()))

repos = ["core", "extra", "community"]
pkgs = []     # installed packages
pkgfiles = {} # package filename-to-repo map
localpkgs = glob.glob("*.pkg.tar.xz") # currently downloaded packages
localpkgs.extend(glob.glob("*.pkg.tar.zst"))
log.write("Packages currently in repo: {}\n".format(len(localpkgs)))


# read our list of installed packages
with open("./db/pkgs.txt") as pkgfile:
    pkgs = [p.rstrip() for p in pkgfile.readlines()]
    log.write("Packages installed on nodes: {}\n".format(len(pkgs) - 1))


# gather data on all available packages
print("Processing package databases", flush=True)
for repo in repos:
    repopkgs = os.listdir("./db/{}".format(repo))
    # read the 'desc' file of each pkg in the repo. build a map of pkg
    # filenames to repo we're pulling them from.
    for pkg in repopkgs:
        pkgname = ""
        pkgfile = ""
        with open("./db/{}/{}/desc".format(repo, pkg)) as desc:
            while True:
                line = desc.readline()
                if line == '':
                    break
                line = line.rstrip()
                if line == "%FILENAME%":
                    pkgfile = desc.readline().rstrip()
                elif line == "%NAME%":
                    pkgname = desc.readline().rstrip()
        # only store data if this pkg is installed
        if pkgname in pkgs:
            pkgfiles[pkgfile] = repo


# if a filename is not in ${LOCALREPO}, download it
print("Downloading new/updated packages", flush=True)
for pkg in pkgfiles.keys():
    if pkg not in localpkgs:
        fetch_count = 0
        filetype = ""
        pkgurl = f"{mirrorurl}/{pkgfiles[pkg]}/os/x86_64/{pkg}"
        pkgdest = f"{localrepo}/{pkg}"
        # try to fetch until we get "XZ compressed" or 5 tries
        while ("XZ compressed" not in filetype and "Zstandard" not in filetype) and fetch_count < 5:
            fetch_count += 1
            rc = 1
            while rc != 0:
                log.write(f"Downloading '{pkgurl}' (try {fetch_count})\n")
                rc = subprocess.run(['curl', '-L', '-f', '-s', '-S',
                                     '--connect-timeout', '10', '--speed-time', '10', '--speed-limit', '1024',
                                     pkgurl, '-o', pkgdest], check=True).returncode
            filetype = subprocess.check_output(['file', pkgdest], text=True)
        # delete the file and fail (for this file) if we hit 5 fetch attempts
        if fetch_count == 5:
            os.remove(pkgdest)
            log.write("WARNING: could not fetch {}\n".format(pkgurl))
            continue
        # all good; carry on
        print("\t{}".format(pkg), flush=True)


# if a file in ${LOCALREPO} is not in the filename list, delete it
print("Deleting outdated/unused packages", flush=True)
for pkg in localpkgs:
    if pkg not in pkgfiles:
        log.write("Deleting '{}'\n".format(pkg))
        os.remove(pkg)
        print("\t{}".format(pkg), flush=True)

log.close()
