# This code is stolen directly from lxd-images, for expediency's sake.
import atexit
import hashlib
import io
import json
import os
import shutil
import subprocess
import tarfile
import tempfile
import uuid


def find_on_path(command):
    """Is command on the executable search path?"""

    if "PATH" not in os.environ:
        return False
    path = os.environ["PATH"]
    for element in path.split(os.pathsep):
        if not element:
            continue
        filename = os.path.join(element, command)
        if os.path.isfile(filename) and os.access(filename, os.X_OK):
            return True
    return False


class Busybox:
    workdir = None

    def __init__(self):
        # Create our workdir
        self.workdir = tempfile.mkdtemp()

    def cleanup(self):
        if self.workdir:
            shutil.rmtree(self.workdir)

    def create_tarball(self, split=False):
        xz = "pxz" if find_on_path("pxz") else "xz"

        destination_tar = os.path.join(self.workdir, "busybox.tar")
        target_tarball = tarfile.open(destination_tar, "w:")

        if split:
            destination_tar_rootfs = os.path.join(self.workdir, "busybox.rootfs.tar")
            target_tarball_rootfs = tarfile.open(destination_tar_rootfs, "w:")

        metadata = {
            "architecture": os.uname()[4],
            "creation_date": int(os.stat("/bin/busybox").st_ctime),
            "properties": {
                "os": "Busybox",
                "architecture": os.uname()[4],
                "description": f"Busybox {os.uname()[4]}",
                "name": f"busybox-{os.uname()[4]}",
                # Don't overwrite actual busybox images.
                "obfuscate": str(uuid.uuid4()),
            },
        }

        # Add busybox
        with open("/bin/busybox", "rb") as fd:
            busybox_file = tarfile.TarInfo()
            busybox_file.size = os.stat("/bin/busybox").st_size
            busybox_file.mode = 0o755
            if split:
                busybox_file.name = "bin/busybox"
                target_tarball_rootfs.addfile(busybox_file, fd)
            else:
                busybox_file.name = "rootfs/bin/busybox"
                target_tarball.addfile(busybox_file, fd)

        # Add symlinks
        busybox = subprocess.Popen(
            ["/bin/busybox", "--list-full"],
            stdout=subprocess.PIPE,
            universal_newlines=True,
        )
        busybox.wait()

        for path in busybox.stdout.read().split("\n"):
            if not path.strip():
                continue
            if path.strip() == "bin/busybox":
                # Don't try and symlink to ourselves!
                continue
            symlink_file = tarfile.TarInfo()
            symlink_file.type = tarfile.SYMTYPE
            symlink_file.linkname = "/bin/busybox"
            if split:
                symlink_file.name = f"{path.strip()}"
                target_tarball_rootfs.addfile(symlink_file)
            else:
                symlink_file.name = f"rootfs/{path.strip()}"
                target_tarball.addfile(symlink_file)

        # Add directories
        for path in ("dev", "mnt", "proc", "root", "sys", "tmp"):
            directory_file = tarfile.TarInfo()
            directory_file.type = tarfile.DIRTYPE
            if split:
                directory_file.name = f"{path}"
                target_tarball_rootfs.addfile(directory_file)
            else:
                directory_file.name = f"rootfs/{path}"
                target_tarball.addfile(directory_file)

        # Add the metadata file
        metadata_yaml = (
            json.dumps(
                metadata,
                sort_keys=True,
                indent=4,
                separators=(",", ": "),
                ensure_ascii=False,
            ).encode("utf-8")
            + b"\n"
        )

        metadata_file = tarfile.TarInfo()
        metadata_file.size = len(metadata_yaml)
        metadata_file.name = "metadata.yaml"
        target_tarball.addfile(metadata_file, io.BytesIO(metadata_yaml))

        # Add an /etc/inittab; this is to work around:
        # http://lists.busybox.net/pipermail/busybox/2015-November/083618.html
        # Basically, since there are some hardcoded defaults that misbehave, we
        # just pass an empty inittab so those aren't applied, and then busybox
        # doesn't spin forever.
        inittab = tarfile.TarInfo()
        inittab.size = 1
        inittab.name = "/rootfs/etc/inittab"
        target_tarball.addfile(inittab, io.BytesIO(b"\n"))

        target_tarball.close()
        if split:
            target_tarball_rootfs.close()

        # Compress the tarball
        r = subprocess.call([xz, "-9", destination_tar])
        if r:
            raise Exception(f"Failed to compress: {destination_tar}")

        if split:
            r = subprocess.call([xz, "-9", destination_tar_rootfs])
            if r:
                raise Exception(f"Failed to compress: {destination_tar_rootfs}")
            return destination_tar + ".xz", destination_tar_rootfs + ".xz"
        else:
            return destination_tar + ".xz"


def create_busybox_image():
    busybox = Busybox()
    atexit.register(busybox.cleanup)

    path = busybox.create_tarball()

    with open(path, "rb") as fd:
        fingerprint = hashlib.sha256(fd.read()).hexdigest()

    return path, fingerprint
