"""Tests for addon expose/unexpose/connect via gateway service."""

from __future__ import annotations

import pytest
from click.testing import CliRunner

from kuberoku.cli.main import cli
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 TestAddonExposeGateway:
    """Test addon expose_on with method=gateway."""

    def test_expose_on_gateway_creates_allocation(self, addon_factory: KuberokuFactory) -> None:
        endpoint = addon_factory.addons.expose_on("myapp", "postgres", method="gateway")
        assert endpoint.method == "gateway"
        assert endpoint.gateway_port is not None
        assert 10000 <= endpoint.gateway_port <= 10999

    def test_expose_on_gateway_creates_lb_service(self, addon_factory: KuberokuFactory) -> None:
        endpoint = addon_factory.addons.expose_on("myapp", "postgres", method="gateway")
        assert endpoint.gateway_port is not None
        gw_svc_name = f"{addon_factory.prefix}-gw-{endpoint.gateway_port}"
        gw_svc = addon_factory.k8s.get_service(addon_factory.namespace, gw_svc_name)
        assert gw_svc["spec"]["type"] == "LoadBalancer"

    def test_expose_on_gateway_is_idempotent(self, addon_factory: KuberokuFactory) -> None:
        e1 = addon_factory.addons.expose_on("myapp", "postgres", method="gateway")
        e2 = addon_factory.addons.expose_on("myapp", "postgres", method="gateway")
        assert e1.gateway_port == e2.gateway_port

    def test_expose_on_gateway_creates_network_policy(
        self, addon_factory: KuberokuFactory
    ) -> None:
        addon_factory.addons.expose_on("myapp", "postgres", method="gateway")
        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

    def test_expose_on_gateway_default_method(self, addon_factory: KuberokuFactory) -> None:
        """Default method for addons is now 'gateway'."""
        endpoint = addon_factory.addons.expose_on("myapp", "postgres")
        assert endpoint.method == "gateway"
        assert endpoint.gateway_port is not None

    def test_expose_on_loadbalancer_still_works(self, addon_factory: KuberokuFactory) -> None:
        endpoint = addon_factory.addons.expose_on("myapp", "postgres", method="loadbalancer")
        assert endpoint.method == "loadbalancer"
        assert endpoint.gateway_port is None
        svc_name = safe_name(addon_factory.prefix, "addon", "myapp-postgres")
        svc = addon_factory.k8s.get_service(addon_factory.namespace, svc_name)
        assert svc["spec"]["type"] == "LoadBalancer"


class TestAddonUnexposeGateway:
    """Test addon expose_off when exposed via gateway."""

    def test_expose_off_gateway_deallocates(self, addon_factory: KuberokuFactory) -> None:
        endpoint = addon_factory.addons.expose_on("myapp", "postgres", method="gateway")
        assert endpoint.gateway_port is not None

        result = addon_factory.addons.expose_off("myapp", "postgres")
        assert result.method == "clusterip"

        # Verify allocation is gone
        svc_name = safe_name(addon_factory.prefix, "addon", "myapp-postgres")
        alloc = addon_factory.gateway.get_allocation("myapp", svc_name)
        assert alloc is None

    def test_expose_off_gateway_removes_lb_service(self, addon_factory: KuberokuFactory) -> None:
        endpoint = addon_factory.addons.expose_on("myapp", "postgres", method="gateway")
        assert endpoint.gateway_port is not None
        gw_svc_name = f"{addon_factory.prefix}-gw-{endpoint.gateway_port}"

        addon_factory.addons.expose_off("myapp", "postgres")
        with pytest.raises(FakeNotFoundError):
            addon_factory.k8s.get_service(addon_factory.namespace, gw_svc_name)

    def test_expose_off_gateway_removes_network_policy(
        self, addon_factory: KuberokuFactory
    ) -> None:
        addon_factory.addons.expose_on("myapp", "postgres", method="gateway")
        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)


class TestAddonConnectGateway:
    """Test addon connect when exposed via gateway."""

    def test_connect_returns_gateway_service(self, addon_factory: KuberokuFactory) -> None:
        endpoint = addon_factory.addons.expose_on("myapp", "postgres", method="gateway")
        assert endpoint.gateway_port is not None

        target = addon_factory.addons.connect("myapp", "postgres")
        expected_svc = f"{addon_factory.prefix}-gw-{endpoint.gateway_port}"
        assert target.service_name == expected_svc
        assert target.ports[0].port == endpoint.gateway_port

    def test_connect_returns_direct_when_not_exposed(self, addon_factory: KuberokuFactory) -> None:
        target = addon_factory.addons.connect("myapp", "postgres")
        svc_name = safe_name(addon_factory.prefix, "addon", "myapp-postgres")
        assert target.service_name == svc_name


class TestAddonGatewayMultiple:
    """Test multiple addon allocations get unique ports."""

    def test_two_addons_get_different_ports(self, addon_factory: KuberokuFactory) -> None:
        addon_factory.addons.create("myapp", "redis")
        e1 = addon_factory.addons.expose_on("myapp", "postgres", method="gateway")
        e2 = addon_factory.addons.expose_on("myapp", "redis", method="gateway")
        assert e1.gateway_port is not None
        assert e2.gateway_port is not None
        assert e1.gateway_port != e2.gateway_port


class TestAddonGatewayCLI:
    """Test CLI commands for addon gateway expose."""

    def test_cli_expose_on_defaults_to_gateway(self, addon_factory: KuberokuFactory) -> None:
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["addons", "expose", "on", "postgres", "-a", "myapp"],
            obj={"factory": addon_factory},
        )
        assert result.exit_code == 0, result.output
        assert "gateway" in result.output.lower()

    def test_cli_expose_on_loadbalancer(self, addon_factory: KuberokuFactory) -> None:
        runner = CliRunner()
        result = runner.invoke(
            cli,
            [
                "addons",
                "expose",
                "on",
                "postgres",
                "-a",
                "myapp",
                "--method",
                "loadbalancer",
            ],
            obj={"factory": addon_factory},
        )
        assert result.exit_code == 0, result.output
        assert "LoadBalancer" in result.output or "loadbalancer" in result.output

    def test_cli_expose_off_gateway(self, addon_factory: KuberokuFactory) -> None:
        addon_factory.addons.expose_on("myapp", "postgres", method="gateway")
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["addons", "expose", "off", "postgres", "-a", "myapp"],
            obj={"factory": addon_factory},
        )
        assert result.exit_code == 0, result.output
        assert "Unexposed" in result.output or "ClusterIP" in result.output
