from argparse import Namespace
from pathlib import Path

from different_wrapping.compose import Container, normalize
from different_wrapping.challenge import Challenge


def make_container(name, container_dict, challenge=None):
    """Helper to create a Container with a minimal mock challenge."""

    class FakeChallenge:
        def __init__(self):
            self.path = Path("/fake/challenge")

        def name(self):
            return "fake-challenge"

        def safe_name(self):
            return "fake-challenge"

    return Container(name, challenge or FakeChallenge(), container_dict)


# --- Port normalization ---


def test_patch_normalizes_host_container_port():
    c = make_container("web", {"ports": ["80:80"]})
    c.patch()
    assert c.container_dict["ports"] == ["80"]


def test_patch_normalizes_mapped_port():
    c = make_container("web", {"ports": ["8080:80"]})
    c.patch()
    assert c.container_dict["ports"] == ["80"]


def test_patch_leaves_single_port_alone():
    c = make_container("web", {"ports": ["80"]})
    c.patch()
    assert c.container_dict["ports"] == ["80"]


def test_patch_normalizes_triple_port():
    """ip:hostPort:containerPort format"""
    c = make_container("web", {"ports": ["127.0.0.1:8080:80"]})
    c.patch()
    assert c.container_dict["ports"] == ["80"]


# --- patch_build ---


def test_patch_build_simple_dockerfile(tmp_path):
    """A build with context=. and default Dockerfile produces a clean image URL."""
    dockerfile = tmp_path / "Dockerfile"
    dockerfile.touch()

    c = make_container(
        "web",
        {"build": {"context": ".", "dockerfile": "Dockerfile"}},
    )
    args = Namespace(docker_image_repo="registry.example.com", docker_tag="latest")

    image = c.patch_build(args, tmp_path, "latest")

    assert "build" not in c.container_dict
    assert c.container_dict["image"] == f"{image.url}:latest"
    # No dockerfile-name suffix when Dockerfile is the default name
    assert image.url == f"registry.example.com/{normalize(tmp_path.name)}"


def test_patch_build_custom_dockerfile(tmp_path):
    """A custom dockerfile name gets an 'f' prefix in the image URL."""
    docker_dir = tmp_path / "docker"
    docker_dir.mkdir()
    (docker_dir / "Challserver.dockerfile").touch()

    c = make_container(
        "dynamic-challserver",
        {"build": {"context": "docker", "dockerfile": "docker/Challserver.dockerfile"}},
    )
    args = Namespace(docker_image_repo="registry.example.com", docker_tag="v1")

    image = c.patch_build(args, tmp_path, "v1")

    assert c.container_dict["image"] == f"{image.url}:v1"
    # The image name should contain the 'f' prefix for custom dockerfiles
    assert "/f" in image.url


# --- regcred ---


def test_patch_adds_regcred():
    c = make_container(
        "web",
        {"ports": ["80"], "labels": {"some-label": "value"}},
    )
    c.patch(regcred="my-registry-secret")
    assert (
        c.container_dict["labels"]["kompose.image-pull-secret"] == "my-registry-secret"
    )


# --- Label handling ---


def test_has_label_dict_format():
    c = make_container("web", {"labels": {"no.tghack.expose_type": "http"}})
    assert c.has_label("no.tghack.expose_type")
    assert c.get_label("no.tghack.expose_type") == "http"


def test_has_label_list_format():
    c = make_container("web", {"labels": ["no.tghack.expose_type=http"]})
    assert c.has_label("no.tghack.expose_type")
    assert c.get_label("no.tghack.expose_type") == "http"


def test_has_label_missing():
    c = make_container("web", {"labels": {"other": "value"}})
    assert not c.has_label("no.tghack.expose_type")


# --- Full challenge patch using real test data ---


def test_full_challenge_patch_xor():
    """Load the xor test challenge and verify patching produces expected results."""
    challenge = Challenge(Path("test_competitions/basic/crypto/xor"))

    args = Namespace(
        regcred=None, docker_image_repo="registry.example.com", docker_tag="latest"
    )
    challenge.patch(args)

    # webserver: port 80:80 should be normalized to 80
    web = challenge.containers["webserver"]
    assert web.container_dict["ports"] == ["80"]

    # static-webserver: port 80 stays as-is, has expose_type label
    static_web = challenge.containers["static-webserver"]
    assert static_web.has_label("no.tghack.expose_type")
    assert static_web.get_label("no.tghack.expose_type") == "http"


def test_full_challenge_patch_build_produces_images():
    """Load the re/js challenge which has a build section and verify image patching."""
    challenge = Challenge(Path("test_competitions/basic/re/js"))

    args = Namespace(
        regcred=None,
        docker_image_repo="registry.example.com",
        docker_tag="v2",
    )

    images = challenge.get_images_to_be_built(args)
    assert len(images) == 1

    image = images[0]
    assert image.url.startswith("registry.example.com/")
    assert image.tags == ["v2"]

    # build key should be gone, replaced with image
    js_server = challenge.containers["js-server"]
    assert "build" not in js_server.container_dict
    assert js_server.container_dict["image"] == f"{image.url}:v2"
