"""Tests for AddonsService.expose_on() and expose_off()."""

from __future__ import annotations

import pytest

from kuberoku.exceptions import AddonNotFoundError, NotExposedError
from kuberoku.factory import KuberokuFactory
from kuberoku.k8s.resources import safe_name
from tests.backends.fake import FakeNotFoundError


@pytest.fixture()
def addon_factory(app_factory: KuberokuFactory) -> KuberokuFactory:
    """Factory with a postgres addon attached."""
    app_factory.addons.create("myapp", "postgres")
    return app_factory


class TestExposeAddon:
    """Expose an addon (postgres)."""

    def test_expose_postgres(self, addon_factory: KuberokuFactory) -> None:
        ep = addon_factory.addons.expose_on("myapp", "postgres")
        assert ep.name == "postgres"
        assert ep.kind == "addon"
        assert ep.service_type == "LoadBalancer"

    def test_expose_nodeport(self, addon_factory: KuberokuFactory) -> None:
        ep = addon_factory.addons.expose_on("myapp", "postgres", method="nodeport")
        assert ep.service_type == "NodePort"
        assert ep.method == "nodeport"

    def test_expose_addon_creates_addon_external_policy(
        self,
        addon_factory: KuberokuFactory,
    ) -> None:
        addon_factory.addons.expose_on("myapp", "postgres")
        np_name = safe_name(
            addon_factory.prefix,
            "netpol-external",
            "myapp-postgres",
        )
        np = addon_factory.k8s.get_network_policy(addon_factory.namespace, np_name)
        assert np is not None
        labels = np.get("metadata", {}).get("labels", {})
        assert f"{addon_factory.domain}/addon-instance" in labels

    def test_expose_addon_policy_uses_addon_selector(
        self,
        addon_factory: KuberokuFactory,
    ) -> None:
        addon_factory.addons.expose_on("myapp", "postgres")
        np_name = safe_name(
            addon_factory.prefix,
            "netpol-external",
            "myapp-postgres",
        )
        np = addon_factory.k8s.get_network_policy(addon_factory.namespace, np_name)
        selector = np["spec"]["podSelector"]["matchLabels"]
        assert f"{addon_factory.domain}/addon-instance" in selector
        assert selector[f"{addon_factory.domain}/addon-instance"] == "postgres"

    def test_expose_idempotent(self, addon_factory: KuberokuFactory) -> None:
        ep1 = addon_factory.addons.expose_on("myapp", "postgres")
        ep2 = addon_factory.addons.expose_on("myapp", "postgres")
        assert ep1.service_type == ep2.service_type

    def test_expose_on_step_callback(self, addon_factory: KuberokuFactory) -> None:
        steps: list[str] = []
        addon_factory.addons.expose_on("myapp", "postgres", on_step=steps.append)
        assert len(steps) >= 2

    def test_expose_addon_not_found(self, app_factory: KuberokuFactory) -> None:
        with pytest.raises(AddonNotFoundError):
            app_factory.addons.expose_on("myapp", "nonexistent")


class TestUnexposeAddon:
    """Unexpose an addon (postgres)."""

    def test_unexpose_addon(self, addon_factory: KuberokuFactory) -> None:
        addon_factory.addons.expose_on("myapp", "postgres")
        ep = addon_factory.addons.expose_off("myapp", "postgres")
        assert ep.service_type == "ClusterIP"
        assert ep.kind == "addon"

    def test_unexpose_addon_deletes_external_policy(
        self,
        addon_factory: KuberokuFactory,
    ) -> None:
        addon_factory.addons.expose_on("myapp", "postgres")
        addon_factory.addons.expose_off("myapp", "postgres")
        np_name = safe_name(
            addon_factory.prefix,
            "netpol-external",
            "myapp-postgres",
        )
        with pytest.raises(FakeNotFoundError):
            addon_factory.k8s.get_network_policy(
                addon_factory.namespace,
                np_name,
            )

    def test_unexpose_not_exposed_raises(
        self,
        addon_factory: KuberokuFactory,
    ) -> None:
        with pytest.raises(NotExposedError):
            addon_factory.addons.expose_off("myapp", "postgres")

    def test_unexpose_on_step_callback(
        self,
        addon_factory: KuberokuFactory,
    ) -> None:
        addon_factory.addons.expose_on("myapp", "postgres")
        steps: list[str] = []
        addon_factory.addons.expose_off("myapp", "postgres", on_step=steps.append)
        assert len(steps) >= 2

    def test_unexpose_preserves_policy_if_other_owners(
        self,
        addon_factory: KuberokuFactory,
    ) -> None:
        """If domains:add also owns the policy, unexpose removes 'expose'
        but keeps the policy for the 'domains' owner."""
        addon_factory.addons.expose_on("myapp", "postgres")

        np_name = safe_name(
            addon_factory.prefix,
            "netpol-external",
            "myapp-postgres",
        )
        np = addon_factory.k8s.get_network_policy(
            addon_factory.namespace,
            np_name,
        )
        np["metadata"]["annotations"][f"{addon_factory.domain}/exposed-by"] = "expose,domains"
        addon_factory.k8s.update_network_policy(
            addon_factory.namespace,
            np_name,
            np,
        )

        addon_factory.addons.expose_off("myapp", "postgres")

        np = addon_factory.k8s.get_network_policy(
            addon_factory.namespace,
            np_name,
        )
        annotations = np.get("metadata", {}).get("annotations", {})
        assert annotations[f"{addon_factory.domain}/exposed-by"] == "domains"
