#!/usr/bin/env python3

import locale
locale.setlocale(locale.LC_ALL, "en_US.UTF-8")

import os
import subprocess as sp
import re
import shutil
import tempfile
import yaml


def load_config():
    with open("packages.yml", "r") as config_file:
        return yaml.load(config_file)


PKG_NAME_PATTERN = r"[a-zA-Z0-9.+-]+"
EPOCH_PATTERN = r"[0-9]+:"
UPSTREAM_VERSION_PATTERN = r"[a-zA-Z0-9.+-:]+"
DEBIAN_VERSION_PATTERN = r"[a-zA-Z0-9.+~]+"
EXTRACT_PATTERN = (r"extracting (?P<name>{pkg_name}) in (?P<dir>{pkg_name}-{version})"
    .format(pkg_name=PKG_NAME_PATTERN, version=UPSTREAM_VERSION_PATTERN))


def run_checked(command, **kwargs):
    try:
        return sp.run(
            command, check=True,
            stdout=sp.PIPE, stderr=sp.PIPE, universal_newlines=True,
            **kwargs)
    except sp.CalledProcessError as error:
        print(error.stdout)
        print(error.stderr)
        raise


def apply_patches(abs_patch_dir):
    for patch in sorted(os.listdir(abs_patch_dir)):
        patch_file = os.path.join(abs_patch_dir, patch)
        run_checked(["quilt", "import", patch_file])


def build_package(repo_dir, package_dir, name,
        patch_dir=None, version=None, changelog=None, additional_content=None):
    with tempfile.TemporaryDirectory(dir=os.path.abspath(".")) as tempdir:
        os.chdir(tempdir)
        result = run_checked(["apt-get", "source", name])
        source_dir_candidates = [
            line for line in result.stdout.splitlines() if "extracting" in line]
        if len(source_dir_candidates) != 1:
            raise ValueError("Got inconclusive candidate directories: {}".format(
                source_dir_candidates))
        source_dir_match = re.search(EXTRACT_PATTERN, source_dir_candidates[0])
        if source_dir_match is None:
            raise ValueError("Cannot get extraction directory from {}".format(
                source_dir_candidates[0]))
        pkg_dir = source_dir_match.group("dir")
        pkg_name = source_dir_match.group("name")
        if pkg_name != name:
            raise ValueError(
                "Extracted package {} is not expected package {}".format(
                    pkg_name, name))

        workdir = os.path.join(tempdir, pkg_dir)
        os.chdir(workdir)

        print(os.listdir())

        if additional_content is not None:
            print("applying additional content")
            os.chdir(workdir)
            print(os.listdir())
            for content in additional_content:
                print("applying {}".format(content))
                os.chdir(content["target_dir"])
                print(os.listdir())
                if "patch_dir" in content:
                    abs_patch_dir = os.path.join(
                        repo_dir, content["patch_dir"])
                    apply_patches(abs_patch_dir)
                elif "git_url" in content:
                    run_checked(["git", "clone", content["git_url"]])
                else:
                    print(
                        "Unknown kind of additional content: {}".format(
                            content))


        command = ["debchange", "--preserve"]
        if version is None:
            command.append("--nmu")
        else:
            command.extend(["--newversion", version])
        def _get_log_entries():
            if changelog:
                yield changelog
            yield "Non-maintainer upload"
        for entry in _get_log_entries():
            run_checked(command + [entry])

        command = ["apt-get", "build-dep", "-y", name]
        run_checked(command)

        command = ["debuild", "-b", "-uc", "-us"]
        run_checked(command)

        deb_packages =  [
            filename
            for filename in os.listdir(tempdir)
            if filename.endswith(".deb")
        ]
        for filename in os.listdir(tempdir):
            if not filename.endswith(".deb"):
                continue
            shutil.move(os.path.join(tempdir, filename), package_dir)


def main():
    repo_dir = os.getcwd()
    config = load_config()
    maintainer = config.get("maintainer", None)
    if maintainer is not None:
        name = maintainer.get("name", None)
        mail = maintainer.get("mail", None)
        if name:
            os.environ["DEBFULLNAME"] = name
        if mail:
            os.environ["DEBEMAIL"] = mail

    package_dir = "packages"
    os.makedirs(package_dir, exist_ok=True)
    
    for package in config["packages"]:
        build_package(repo_dir=repo_dir, package_dir=package_dir, **package)
        os.chdir(repo_dir)

    print(os.listdir(package_dir))

if __name__ == "__main__":
    main()