from subprocess import CalledProcessError

import pytest

from galaxy.tool_util.deps.container_classes import DOCKER_CONTAINER_TYPE
from galaxy.tool_util.deps.container_resolvers.mulled import (
    CachedMulledDockerContainerResolver,
    CachedMulledSingularityContainerResolver,
    MulledDockerContainerResolver,
)
from galaxy.tool_util.deps.containers import ContainerRegistry
from galaxy.tool_util.deps.dependencies import (
    AppInfo,
    ToolInfo,
)
from galaxy.tool_util.deps.requirements import ToolRequirement

SINGULARITY_IMAGES = (
    "foo:1.0--bar",
    "baz:2.22",
    "mulled-v2-fe8a3b846bc50d24e5df78fa0b562c43477fe9ce:9f946d13f673ab2903cb0da849ad42916d619d18-0",
)


@pytest.fixture
def appinfo() -> AppInfo:
    return AppInfo(
        involucro_auto_init=True,
        enable_mulled_containers=True,
        container_image_cache_path=".",
    )


@pytest.fixture
def container_registry():
    app_info = AppInfo(
        involucro_auto_init=True,
        enable_mulled_containers=True,
        container_image_cache_path=".",
    )
    return ContainerRegistry(app_info)


def test_container_registry(container_registry, mocker):
    mocker.patch("galaxy.tool_util.deps.mulled.util._get_namespace", return_value=["samtools"])
    tool_info = ToolInfo(requirements=[ToolRequirement(name="samtools", version="1.10", type="package")])
    container_description = container_registry.find_best_container_description(
        [DOCKER_CONTAINER_TYPE],
        tool_info,
        install=False,
    )
    assert container_description.type == "docker"
    assert "samtools:1.10" in container_description.identifier


def test_docker_container_resolver_detects_docker_cli_absent(appinfo, mocker):
    mocker.patch("galaxy.tool_util.deps.container_resolvers.mulled.which", return_value=None)
    resolver = CachedMulledDockerContainerResolver(appinfo)
    assert resolver._cli_available is False


def test_docker_container_resolver_detects_docker_cli(appinfo, mocker):
    """
    - CachedMulledDockerContainerResolver properly detects present docker binary
    """
    mocker.patch("galaxy.tool_util.deps.container_resolvers.mulled", return_value="/bin/docker")
    resolver = CachedMulledDockerContainerResolver(appinfo)
    assert resolver.cli_available


def test_cached_docker_container_docker_cli_absent_resolve(appinfo, mocker) -> None:
    mocker.patch("galaxy.tool_util.deps.container_resolvers.mulled.which", return_value=None)
    resolver = CachedMulledDockerContainerResolver(appinfo)
    assert resolver.cli_available is False
    assert resolver.resolve(enabled_container_types=[], tool_info=ToolInfo()) is None


def test_docker_container_docker_cli_absent_resolve(appinfo, mocker):
    mocker.patch("galaxy.tool_util.deps.container_resolvers.mulled.which", return_value=None)
    resolver = MulledDockerContainerResolver(appinfo)
    assert resolver.cli_available is False
    requirement = ToolRequirement(name="samtools", version="1.10", type="package")
    tool_info = ToolInfo(requirements=[requirement])
    mocker.patch(
        "galaxy.tool_util.deps.container_resolvers.mulled.targets_to_mulled_name",
        return_value="samtools:1.10--h2e538c0_3",
    )
    container_description = resolver.resolve(enabled_container_types=["docker"], tool_info=tool_info)
    assert container_description
    assert container_description.type == "docker"
    assert container_description.identifier == "quay.io/biocontainers/samtools:1.10--h2e538c0_3"


def test_docker_container_docker_cli_exception_resolve(appinfo, mocker):
    mocker.patch("galaxy.tool_util.deps.container_resolvers.mulled.which", return_value="/bin/docker")
    resolver = MulledDockerContainerResolver(appinfo)
    assert resolver.cli_available is True
    requirement = ToolRequirement(name="samtools", version="1.10", type="package")
    tool_info = ToolInfo(requirements=[requirement])
    mocker.patch(
        "galaxy.tool_util.deps.container_resolvers.mulled.targets_to_mulled_name",
        return_value="samtools:1.10--h2e538c0_3",
    )
    mocker.patch(
        "galaxy.tool_util.deps.container_resolvers.mulled.docker_cached_container_description",
        side_effect=CalledProcessError(1, "bla"),
    )
    container_description = resolver.resolve(enabled_container_types=["docker"], tool_info=tool_info, install=True)
    assert resolver.cli_available is True
    assert container_description
    assert container_description.type == "docker"
    assert container_description.identifier == "quay.io/biocontainers/samtools:1.10--h2e538c0_3"


def test_cached_singularity_container_resolver_uncached(mocker):
    mocker.patch("os.listdir", return_value=SINGULARITY_IMAGES)
    mocker.patch("os.path.exists", return_value=True)
    mocker.patch("galaxy.tool_util.deps.container_resolvers.mulled.safe_makedirs")
    resolver = CachedMulledSingularityContainerResolver(app_info=mocker.Mock(container_image_cache_path="/"))
    requirement = ToolRequirement(name="foo", version="1.0", type="package")
    tool_info = ToolInfo(requirements=[requirement])
    container_description = resolver.resolve(enabled_container_types=["singularity"], tool_info=tool_info)
    assert container_description
    assert container_description.type == "singularity"
    assert container_description.identifier == "/singularity/mulled/foo:1.0--bar"


def test_cached_singularity_container_resolver_dir_mtime_cached(mocker):
    mocker.patch("os.listdir", return_value=SINGULARITY_IMAGES)
    mocker.patch("os.path.exists", return_value=True)
    mocker.patch("galaxy.tool_util.deps.container_resolvers.mulled.safe_makedirs")
    mocker.patch("os.stat", return_value=mocker.Mock(st_mtime=42))
    resolver = CachedMulledSingularityContainerResolver(
        app_info=mocker.Mock(container_image_cache_path="/"), cache_directory_cacher_type="dir_mtime"
    )
    requirement = ToolRequirement(name="baz", version="2.22", type="package")
    tool_info = ToolInfo(requirements=[requirement])
    container_description = resolver.resolve(enabled_container_types=["singularity"], tool_info=tool_info)
    assert container_description
    assert container_description.type == "singularity"
    assert container_description.identifier == "/singularity/mulled/baz:2.22"
    requirement = ToolRequirement(name="foo", version="1.0", type="package")
    tool_info.requirements.append(requirement)
    container_description = resolver.resolve(enabled_container_types=["singularity"], tool_info=tool_info)
    assert container_description
    assert container_description.type == "singularity"
    assert (
        container_description.identifier
        == "/singularity/mulled/mulled-v2-fe8a3b846bc50d24e5df78fa0b562c43477fe9ce:9f946d13f673ab2903cb0da849ad42916d619d18-0"
    )
