import subprocess
import shutil
import os
from urllib.parse import urlparse
import hashlib
from distutils.version import LooseVersion

from snakemake import conda
from snakemake.common import lazy_property
from snakemake.exceptions import WorkflowError
from snakemake.logging import logger


class Image:
    def __init__(self, url, dag):
        if not shutil.which("singularity"):
            raise WorkflowError("The singularity command has to be "
                                "available in order to use singularity "
                                "integration.")
        try:
            v = subprocess.check_output(["singularity", "--version"],
                                        stderr=subprocess.PIPE).decode()
        except subprocess.CalledProcessError as e:
            raise WorkflowError(
                "Failed to get singularity version:\n{}".format(
                    e.stderr.decode()))
        if not LooseVersion(v) >= LooseVersion("2.4"):
            raise WorkflowError("Minimum singularity version is 2.4.")

        self.url = url
        self._img_dir = dag.workflow.persistence.singularity_img_path

    @property
    def is_local(self):
        scheme = urlparse(self.url).scheme
        return not scheme or scheme == "file"

    @lazy_property
    def hash(self):
        md5hash = hashlib.md5()
        md5hash.update(self.url.encode())
        return md5hash.hexdigest()

    def pull(self, dryrun=False):
        if self.is_local:
            return
        if dryrun:
            logger.info("Singularity image {} will be pulled.".format(self.url))
            return
        logger.debug("Singularity image location: {}".format(self.path))
        if not os.path.exists(self.path):
            logger.info("Pulling singularity image {}.".format(self.url))
            try:
                p = subprocess.check_output(["singularity", "pull",
                                         "--name", self.hash, self.url],
                                        cwd=self._img_dir,
                                        stderr=subprocess.STDOUT)
            except subprocess.CalledProcessError as e:
                raise WorkflowError("Failed to pull singularity image "
                                    "from {}:\n{}".format(self.url,
                                                          e.stdout.decode()))


    @property
    def path(self):
        if self.is_local:
            return urlparse(self.url).path
        return os.path.join(self._img_dir, self.hash) + ".simg"


def shellcmd(img_path, cmd, args):
    return "singularity exec {} {} bash -c \"{}\"".format(
        args, img_path, cmd.replace("\"", r"\""))
