import pytest


from render import Render


@pytest.fixture
def mock_values():
    return {
        "images": {
            "test_image": {
                "repository": "nginx",
                "tag": "latest",
            }
        },
    }


tests = [
    {
        "name": "add_ports_should_work",
        "inputs": [
            {"values": (8081, 8080), "expect_error": False},
            {"values": (8082, 8080, {"protocol": "udp"}), "expect_error": False},
        ],
        "expected": [
            {"published": 8081, "target": 8080, "protocol": "tcp", "mode": "ingress"},
            {"published": 8082, "target": 8080, "protocol": "udp", "mode": "ingress"},
        ],
    },
    {
        "name": "add_duplicate_ports_should_fail",
        "inputs": [
            {"values": (8081, 8080), "expect_error": False},
            {"values": (8081, 8080), "expect_error": True},
        ],
    },
    {
        "name": "adding_duplicate_port_different_protocol_should_work",
        "inputs": [
            {"values": (8081, 8080), "expect_error": False},
            {"values": (8081, 8080, {"protocol": "udp"}), "expect_error": False},
        ],
        "expected": [
            {"published": 8081, "target": 8080, "protocol": "tcp", "mode": "ingress"},
            {"published": 8081, "target": 8080, "protocol": "udp", "mode": "ingress"},
        ],
    },
    {
        "name": "adding_same_port_for_both_wildcard_families_should_work",
        "inputs": [
            {"values": (8081, 8080, {"host_ip": "0.0.0.0"}), "expect_error": False},
            {"values": (8081, 8080, {"host_ip": "::"}), "expect_error": False},
        ],
        "expected": [
            {"published": 8081, "target": 8080, "protocol": "tcp", "mode": "ingress"},
        ],
    },
    {
        "name": "adding_duplicate_port_for_v4_ip_and_v4_wildcard_should_fail",
        "inputs": [
            {"values": (8081, 8080, {"host_ip": "192.168.1.10"}), "expect_error": False},
            {"values": (8081, 8080, {"host_ip": "0.0.0.0"}), "expect_error": True},
        ],
    },
    {
        "name": "adding_duplicate_port_for_v4_wildcard_and_v4_ip_should_fail",
        "inputs": [
            {"values": (8081, 8080, {"host_ip": "0.0.0.0"}), "expect_error": False},
            {"values": (8081, 8080, {"host_ip": "192.168.1.10"}), "expect_error": True},
        ],
    },
    {
        "name": "adding_duplicate_port_for_v4_wildcard_and_v6_ip_should_work",
        "inputs": [
            {"values": (8081, 8080, {"host_ip": "192.168.1.10"}), "expect_error": False},
            {"values": (8081, 8080, {"host_ip": "fd00:1234:5678:abcd::10"}), "expect_error": False},
        ],
        "expected": [
            {"published": 8081, "target": 8080, "protocol": "tcp", "mode": "ingress", "host_ip": "192.168.1.10"},
            {
                "published": 8081,
                "target": 8080,
                "protocol": "tcp",
                "mode": "ingress",
                "host_ip": "fd00:1234:5678:abcd::10",
            },
        ],
    },
    {
        "name": "adding_duplicate_port_for_v6_wildcard_and_v4_ip_should_work",
        "inputs": [
            {"values": (8081, 8080, {"host_ip": "::"}), "expect_error": False},
            {"values": (8081, 8080, {"host_ip": "192.168.1.10"}), "expect_error": False},
        ],
        "expected": [
            {"published": 8081, "target": 8080, "protocol": "tcp", "mode": "ingress", "host_ip": "192.168.1.10"},
            {"published": 8081, "target": 8080, "protocol": "tcp", "mode": "ingress", "host_ip": "::"},
        ],
    },
    {
        "name": "adding_duplicate_port_for_v6_wildcard_and_v6_ip_should_fail",
        "inputs": [
            {"values": (8081, 8080, {"host_ip": "::"}), "expect_error": False},
            {"values": (8081, 8080, {"host_ip": "fd00:1234:5678:abcd::10"}), "expect_error": True},
        ],
    },
    {
        "name": "adding_duplicate_port_for_v6_ip_and_v6_wildcard_should_fail",
        "inputs": [
            {"values": (8081, 8080, {"host_ip": "fd00:1234:5678:abcd::10"}), "expect_error": False},
            {"values": (8081, 8080, {"host_ip": "::"}), "expect_error": True},
        ],
    },
    {
        "name": "adding_duplicate_port_with_different_v4_ip_should_work",
        "inputs": [
            {"values": (8081, 8080, {"host_ip": "192.168.1.10"}), "expect_error": False},
            {"values": (8081, 8080, {"host_ip": "192.168.1.11"}), "expect_error": False},
        ],
        "expected": [
            {"published": 8081, "target": 8080, "protocol": "tcp", "mode": "ingress", "host_ip": "192.168.1.10"},
            {"published": 8081, "target": 8080, "protocol": "tcp", "mode": "ingress", "host_ip": "192.168.1.11"},
        ],
    },
    {
        "name": "adding_port_with_invalid_protocol_should_fail",
        "inputs": [
            {"values": (8081, 8080, {"protocol": "invalid_protocol"}), "expect_error": True},
        ],
    },
    {
        "name": "adding_port_with_invalid_mode_should_fail",
        "inputs": [
            {"values": (8081, 8080, {"mode": "invalid_mode"}), "expect_error": True},
        ],
    },
    {
        "name": "adding_port_with_invalid_ip_should_fail",
        "inputs": [
            {"values": (8081, 8080, {"host_ip": "invalid_ip"}), "expect_error": True},
        ],
    },
    {
        "name": "adding_port_with_invalid_host_port_should_fail",
        "inputs": [
            {"values": (-1, 8080), "expect_error": True},
        ],
    },
    {
        "name": "adding_port_with_invalid_container_port_should_fail",
        "inputs": [
            {"values": (8081, -1), "expect_error": True},
        ],
    },
    {
        "name": "adding_duplicate_ports_with_different_host_ip_should_work",
        "inputs": [
            {"values": (8081, 8080, {"host_ip": "192.168.1.10"}), "expect_error": False},
            {"values": (8081, 8080, {"host_ip": "192.168.1.10", "protocol": "udp"}), "expect_error": False},
            {"values": (8081, 8080, {"host_ip": "192.168.1.11"}), "expect_error": False},
            {"values": (8081, 8080, {"host_ip": "192.168.1.11", "protocol": "udp"}), "expect_error": False},
            {"values": (8081, 8080, {"host_ip": "fd00:1234:5678:abcd::10"}), "expect_error": False},
            {"values": (8081, 8080, {"host_ip": "fd00:1234:5678:abcd::11"}), "expect_error": False},
        ],
        # fmt: off
        "expected": [
            {"published": 8081, "target": 8080, "protocol": "tcp", "mode": "ingress", "host_ip": "192.168.1.10"},
            {"published": 8081, "target": 8080, "protocol": "tcp", "mode": "ingress", "host_ip": "192.168.1.11"},
            {"published": 8081, "target": 8080, "protocol": "tcp", "mode": "ingress", "host_ip": "fd00:1234:5678:abcd::10"}, # noqa
            {"published": 8081, "target": 8080, "protocol": "tcp", "mode": "ingress", "host_ip": "fd00:1234:5678:abcd::11"}, # noqa
            {"published": 8081, "target": 8080, "protocol": "udp", "mode": "ingress", "host_ip": "192.168.1.10"},
            {"published": 8081, "target": 8080, "protocol": "udp", "mode": "ingress", "host_ip": "192.168.1.11"},
        ],
        # fmt: on
    },
]


@pytest.mark.parametrize("test", tests)
def test_ports(test):
    mock_values = {
        "images": {
            "test_image": {
                "repository": "nginx",
                "tag": "latest",
            }
        },
    }

    render = Render(mock_values)
    c1 = render.add_container("test_container", "test_image")
    c1.healthcheck.disable()

    errored = False
    for input in test["inputs"]:
        if input["expect_error"]:
            with pytest.raises(Exception):
                c1.ports.add_port(*input["values"])
                errored = True
        else:
            c1.ports.add_port(*input["values"])

    errored = True if [i["expect_error"] for i in test["inputs"]].count(True) > 0 else False
    if errored:
        return

    output = render.render()
    assert output["services"]["test_container"]["ports"] == test["expected"]
