"""Tests for ServicesService.expose_on()."""

from __future__ import annotations

import pytest

from kuberoku.exceptions import AppNotFoundError, NameNotFoundError
from kuberoku.factory import KuberokuFactory
from kuberoku.k8s.resources import safe_name


class TestExposeProcess:
    """Expose a process type (web)."""

    def test_expose_web_loadbalancer(self, deployed_factory: KuberokuFactory) -> None:
        ep = deployed_factory.services.expose_on("myapp", "web")
        assert ep.name == "web"
        assert ep.app == "myapp"
        assert ep.kind == "process"
        assert ep.service_type == "LoadBalancer"
        assert ep.method == "loadbalancer"

    def test_expose_web_nodeport(self, deployed_factory: KuberokuFactory) -> None:
        ep = deployed_factory.services.expose_on("myapp", "web", method="nodeport")
        assert ep.service_type == "NodePort"
        assert ep.method == "nodeport"

    def test_expose_updates_service_type(self, deployed_factory: KuberokuFactory) -> None:
        deployed_factory.services.expose_on("myapp", "web")
        svc_name = safe_name(deployed_factory.prefix, "myapp", "web")
        svc = deployed_factory.k8s.get_service(deployed_factory.namespace, svc_name)
        assert svc["spec"]["type"] == "LoadBalancer"

    def test_expose_creates_external_policy(self, deployed_factory: KuberokuFactory) -> None:
        deployed_factory.services.expose_on("myapp", "web")
        np_name = safe_name(deployed_factory.prefix, "netpol-external", "myapp-web")
        np = deployed_factory.k8s.get_network_policy(deployed_factory.namespace, np_name)
        assert np is not None
        # External policy has no `from` in ingress
        ingress = np["spec"]["ingress"]
        assert len(ingress) == 1
        assert "from" not in ingress[0]

    def test_expose_policy_has_exposed_by_annotation(
        self,
        deployed_factory: KuberokuFactory,
    ) -> None:
        deployed_factory.services.expose_on("myapp", "web")
        np_name = safe_name(deployed_factory.prefix, "netpol-external", "myapp-web")
        np = deployed_factory.k8s.get_network_policy(deployed_factory.namespace, np_name)
        annotations = np.get("metadata", {}).get("annotations", {})
        assert f"{deployed_factory.domain}/exposed-by" in annotations
        assert annotations[f"{deployed_factory.domain}/exposed-by"] == "expose"

    def test_expose_idempotent(self, deployed_factory: KuberokuFactory) -> None:
        ep1 = deployed_factory.services.expose_on("myapp", "web")
        ep2 = deployed_factory.services.expose_on("myapp", "web")
        assert ep1.service_type == ep2.service_type

    def test_expose_reports_ports(self, deployed_factory: KuberokuFactory) -> None:
        ep = deployed_factory.services.expose_on("myapp", "web")
        assert len(ep.ports) > 0

    def test_expose_on_step_callback(self, deployed_factory: KuberokuFactory) -> None:
        steps: list[str] = []
        deployed_factory.services.expose_on("myapp", "web", on_step=steps.append)
        assert len(steps) >= 2
        assert any("Resolving" in s for s in steps)
        assert any("Patching" in s for s in steps)


class TestExposeErrors:
    """Error cases for expose."""

    def test_expose_app_not_found(self, deployed_factory: KuberokuFactory) -> None:
        with pytest.raises(AppNotFoundError):
            deployed_factory.services.expose_on("noapp", "web")

    def test_expose_name_not_found(self, deployed_factory: KuberokuFactory) -> None:
        with pytest.raises(NameNotFoundError):
            deployed_factory.services.expose_on("myapp", "nonexistent")

    def test_expose_process(
        self,
        full_factory: KuberokuFactory,
    ) -> None:
        ep = full_factory.services.expose_on("myapp", "web")
        assert ep.kind == "process"
