#!/usr/bin/python3
# pylint: disable=invalid-name  # https://github.com/PyCQA/pylint/issues/516

import os
import re
import tempfile
import subprocess

import debian.deb822
import debian.debian_support

import mini_buildd
import mini_buildd.cli

#: Needed for man page hack in setup.py
DESCRIPTION = "Import package from an old mini-buildd 08x distribution into a reprepro distribution in cwd."


def call(cmd, show=True, dry_run=False):
    if show:
        print(f"Calling (dry_run={dry_run}): {cmd}")

    if dry_run:
        return 0, f"Dry run, skipped: {cmd}"

    with tempfile.TemporaryFile() as output:
        res = subprocess.call(cmd.split(), stdout=output, stderr=subprocess.STDOUT)
        output.seek(0)
        output_str = output.read().decode("UTF-8")
        if show:
            print(f"Result={res}:")
            print(output_str)
        return res, output_str


def cmp_versions(pair0, pair1):
    """Compare Debian package versions (on first item of pair)."""
    return debian.debian_support.version_compare(pair0[0], pair1[0])


class CLI(mini_buildd.cli.CLI):
    def __init__(self):
        super().__init__("mini-buildd-import-08x", DESCRIPTION)
        self.parser.add_argument("path", help="complete path to a 08x dist repo")
        self.parser.add_argument("distribution", help="reprepro distribution to import to")
        self.parser.add_argument("-n", "--dry-run", action='store_true',
                                 help="dry run, just show what commands would be run")

    def runcli(self):
        # Check we are in a reprepro dist
        assert os.path.exists("conf/distributions"), "No reprepro repository detected in cwd."

        # Check 08x path
        old_dist = os.path.basename(self.args.path)
        old_split = old_dist.split("-")
        assert len(old_split) >= 2 and len(old_split) <= 3, f"Malformed 08x dist '{old_dist}'"

        old_codename, old_repoid = old_split[0], old_split[1]

        codename, repoid, _suite = self.args.distribution.split("-")
        assert old_codename == codename, f"08x and 1.0 base dist (codename) are DIFFERENT: {old_codename} vs {codename}"

        print(f"""
Parsing packages from '{self.args.path}'...
""")

        # { PACKAGE: {VERSION: {"dsc": dsc, 'debs': [deb, deb,...]}}
        packages = {}

        for dsc in debian.deb822.Sources.iter_paragraphs(mini_buildd.fopen(os.path.join(self.args.path, "Sources"))):
            print("Scanning source", dsc["package"], dsc["version"])
            v = packages.setdefault(dsc["package"], {})
            v[dsc["version"]] = {"dsc": dsc, "debs": []}
            for deb in debian.deb822.Packages.iter_paragraphs(mini_buildd.fopen(os.path.join(self.args.path, "Packages"))):
                if deb.get("source", deb["package"]) == dsc["package"] and deb["version"] == dsc["version"]:
                    v[dsc["version"]]["debs"].append(deb)

        if old_repoid != repoid:
            input(f"""
WARN: Old/new repo ids different: '{old_repoid}' vs '{repoid}'.
WARN: If this is just a typo, STOP HERE.
WARN: If you did not create a new repo with the same id, and/or want to force the packages in, force continue.
WARN: Continue (Ctr-C to cancel)?""")

        print(f"""
{old_dist}: {len(packages)} source packages. Trying to import to '{self.args.distribution}' in reprepo/cwd
""")
        input(f"Start import (dry_run={self.args.dry_run}) (Ctrl-C to cancel)?")
        print()

        for package, versions in packages.items():
            # The only possible check is to check that repropro's ls output is empty
            res, output = call(f"reprepro list {self.args.distribution} {package}", show=False)
            if output:
                print(f"** Skipping {package}: Already in dist '{self.args.distribution}'")
            else:
                print(f"** Importing {package}")
                r = -1
                for v, items in sorted(list(versions.items()), cmp=cmp_versions, reverse=True):
                    dist = self.args.distribution
                    if r >= 0:
                        dist += f"-rollback{r}"
                    r += 1
                    print(f"* Importing {package}-{v} to {dist}")

                    dsc_file = None
                    for s in items["dsc"]["files"]:
                        if re.compile(r"^.*\.dsc$").match(s["name"]):
                            dsc_file = os.path.join(self.args.path, os.path.basename(s["name"]))
                            break

                    res, output = call(f"reprepro includedsc {dist} {dsc_file}", dry_run=self.args.dry_run)
                    if res != 0:
                        print("WARN: includedsc failed; retrying with priority/section=extra/misc...")
                        call(f"reprepro --priority extra --section misc includedsc {dist} {dsc_file}", dry_run=self.args.dry_run)

                    for deb in items["debs"]:
                        deb_file = os.path.join(self.args.path, os.path.basename(deb["filename"]))
                        _dummy, ext = os.path.splitext(deb_file)
                        typ = ext[1:]
                        call(f"reprepro include{typ} {dist} {deb_file}", dry_run=self.args.dry_run)
            print()


CLI().run()
