"""Tests for ServicesService.add_port()."""

from __future__ import annotations

import pytest

from kuberoku.exceptions import (
    AmbiguousProcessTypeError,
    AppNotFoundError,
    PortAlreadyExistsError,
    ProcessTypeNotFoundError,
)
from kuberoku.factory import KuberokuFactory
from kuberoku.k8s import resources


class TestAddPort:
    def test_add_port_basic(self, deployed_factory: KuberokuFactory) -> None:
        deployed_factory.services.add_port("myapp", "9090/tcp")
        result = deployed_factory.services.list_ports("myapp")
        ports = {(pm.port, pm.protocol) for pm in result["web"]}
        assert (9090, "TCP") in ports
        assert (8080, "TCP") in ports

    def test_add_port_udp(self, deployed_factory: KuberokuFactory) -> None:
        deployed_factory.services.add_port("myapp", "5353/udp")
        result = deployed_factory.services.list_ports("myapp")
        ports = {(pm.port, pm.protocol) for pm in result["web"]}
        assert (5353, "UDP") in ports

    def test_add_port_default_protocol(self, deployed_factory: KuberokuFactory) -> None:
        """Port spec without protocol defaults to TCP."""
        deployed_factory.services.add_port("myapp", "3000")
        result = deployed_factory.services.list_ports("myapp")
        ports = {(pm.port, pm.protocol) for pm in result["web"]}
        assert (3000, "TCP") in ports

    def test_add_port_already_exists(self, deployed_factory: KuberokuFactory) -> None:
        with pytest.raises(PortAlreadyExistsError) as exc_info:
            deployed_factory.services.add_port("myapp", "8080/tcp")
        assert exc_info.value.port == 8080
        assert exc_info.value.protocol == "TCP"
        assert exc_info.value.process_type == "web"

    def test_add_port_app_not_found(self, app_factory: KuberokuFactory) -> None:
        with pytest.raises(AppNotFoundError):
            app_factory.services.add_port("nonexistent", "9090/tcp")

    def test_add_port_process_type_not_found(
        self,
        deployed_factory: KuberokuFactory,
    ) -> None:
        with pytest.raises(ProcessTypeNotFoundError):
            deployed_factory.services.add_port(
                "myapp",
                "9090/tcp",
                process_type="nonexistent",
            )

    def test_add_port_auto_resolves_single_type(
        self,
        deployed_factory: KuberokuFactory,
    ) -> None:
        """With one process type, --type is not needed."""
        deployed_factory.services.add_port("myapp", "9090/tcp")
        result = deployed_factory.services.list_ports("myapp")
        assert (9090, "TCP") in {(pm.port, pm.protocol) for pm in result["web"]}

    def test_add_port_ambiguous_multi_type(
        self,
        multi_process_factory: KuberokuFactory,
    ) -> None:
        """Multiple process types without --type raises error."""
        with pytest.raises(AmbiguousProcessTypeError) as exc_info:
            multi_process_factory.services.add_port("myapp", "9090/tcp")
        assert "web" in str(exc_info.value)
        assert "worker" in str(exc_info.value)

    def test_add_port_with_explicit_type(
        self,
        multi_process_factory: KuberokuFactory,
    ) -> None:
        """With --type specified, add_port works for multi-type apps."""
        multi_process_factory.services.add_port(
            "myapp",
            "9090/tcp",
            process_type="web",
        )
        result = multi_process_factory.services.list_ports("myapp")
        assert (9090, "TCP") in {(pm.port, pm.protocol) for pm in result["web"]}

    def test_add_port_updates_deployment(
        self,
        deployed_factory: KuberokuFactory,
    ) -> None:
        """Verify the Deployment container ports are updated."""
        deployed_factory.services.add_port("myapp", "9090/tcp")
        dep_name = resources.safe_name(
            deployed_factory.prefix,
            "myapp",
            "web",
        )
        dep = deployed_factory.k8s.get_deployment(
            deployed_factory.namespace,
            dep_name,
        )
        container_ports = dep["spec"]["template"]["spec"]["containers"][0]["ports"]
        port_nums = [p["containerPort"] for p in container_ports]
        assert 9090 in port_nums

    def test_add_port_updates_network_policy(
        self,
        deployed_factory: KuberokuFactory,
    ) -> None:
        """Verify the process-allow NetworkPolicy is updated."""
        deployed_factory.services.add_port("myapp", "9090/tcp")
        np_name = resources.safe_name(
            deployed_factory.prefix,
            "netpol-allow",
            "myapp-web",
        )
        np = deployed_factory.k8s.get_network_policy(
            deployed_factory.namespace,
            np_name,
        )
        np_ports = np["spec"]["ingress"][0]["ports"]
        port_nums = [p["port"] for p in np_ports]
        assert 8080 in port_nums
        assert 9090 in port_nums

    def test_add_port_on_step_callback(
        self,
        deployed_factory: KuberokuFactory,
    ) -> None:
        steps: list[str] = []
        deployed_factory.services.add_port(
            "myapp",
            "9090/tcp",
            on_step=steps.append,
        )
        assert len(steps) >= 1
