#! /usr/bin/python3

"""
Take the JSON provided by Mock, download corresponding RPMs, and put them into
an RPM repository.
"""

# pylint: disable=invalid-name

import argparse
import concurrent.futures
import json
import logging
import os
import shutil
import subprocess
import sys
from urllib.parse import quote

import requests

logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger(__name__)


def download_file(url, outputdir):
    """
    Download a single file (pool worker)
    """
    basename = os.path.basename(url)
    file_name = os.path.join(outputdir, basename)
    dirname = os.path.dirname(url)

    # basename often contains '+' character, e.g., g++ package, url-encode it
    url = dirname + "/" + quote(basename)
    log.info("Downloading %s", url)

    try:
        with requests.get(url, stream=True, timeout=30) as response:
            if response.status_code != 200:
                return False
            with open(file_name, "wb") as fd:
                shutil.copyfileobj(response.raw, fd)
            return True
    except:  # noqa: E722
        log.exception("Exception raised for %s", url)
        raise


def _argparser():
    parser = argparse.ArgumentParser(
        prog='mock-hermetic-repo',
        description=(
            "Prepare a repository for a `mock --hermetic-build` build. "
            "Given a Mock buildroot \"lockfile\"\n\n"
            "  a) create an output repo directory,\n"
            "  b) download and place all the necessary RPM files there,\n"
            "  c) create a local RPM repository there (run createrepo), and\n"
            "  d) dump there also the previously used bootstrap image as "
            "a tarball.\n\n"
            "Lockfile is a buildroot_lock.json file from Mock's "
            "result directory; it is a JSON file generated by the "
            "--calculate-build-dependencies option/buildroot_lock "
            "plugin."),
        formatter_class=argparse.RawTextHelpFormatter,
    )
    parser.add_argument("--lockfile", required=True,
                        help=(
                            "Select buildroot_lock.json filename on your system, "
                            "typically located in the Mock's result directory "
                            "upon the --calculate-build-dependencies mode "
                            "execution."))
    parser.add_argument("--output-repo", required=True,
                        help=(
                            "Download RPMs into this directory, and then run "
                            "/bin/createrepo_c utility there to populate the "
                            "RPM repo metadata."))
    return parser


def prepare_image(image_specification, outputdir):
    """
    Store the tarball into the same directory where the RPMs are
    """
    subprocess.check_output(["podman", "pull", image_specification])
    subprocess.check_output(["podman", "save", "--format=oci-archive", "--quiet",
                             "-o", os.path.join(outputdir, "bootstrap.tar"),
                             image_specification])


def _main():
    options = _argparser().parse_args()

    with open(options.lockfile, "r", encoding="utf-8") as fd:
        data = json.load(fd)

    try:
        os.makedirs(options.output_repo)
    except FileExistsError:
        pass

    failed = False
    urls = [i["url"] for i in data["buildroot"]["rpms"]]
    with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
        for i, out in zip(urls, executor.map(download_file, urls,
                                             [options.output_repo for _ in urls])):
            if out is False:
                log.error("Download failed: %s", i)
                failed = True
    if failed:
        log.error("RPM deps downloading failed")
        sys.exit(1)

    subprocess.check_call(["createrepo_c", options.output_repo])

    prepare_image(data["config"]["bootstrap_image"], options.output_repo)


if __name__ == "__main__":
    _main()
