"""CLI tests for services:expose:on/off, services:ports, addons:expose/connect commands."""

from __future__ import annotations

from click.testing import CliRunner

from kuberoku.cli.main import cli
from kuberoku.factory import KuberokuFactory
from tests.backends.fake import FakeK8sClient


def _deployed_factory(namespace: str) -> KuberokuFactory:
    """Create a factory with a deployed app."""
    f = KuberokuFactory(k8s_client=FakeK8sClient(), namespace=namespace)
    f.apps.create("myapp")
    f.deploy.deploy("myapp", image="myapp:v1", wait=False)
    return f


def _addon_factory(namespace: str) -> KuberokuFactory:
    """Create a factory with a deployed app + postgres addon."""
    f = _deployed_factory(namespace)
    f.addons.create("myapp", "postgres")
    return f


class TestServicesExposeCLI:
    def test_expose_on_process(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["services", "expose", "on", "web", "--app", "myapp", "--method", "loadbalancer"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "Exposed" in result.output
        assert "loadbalancer" in result.output.lower()

    def test_expose_on_nodeport(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["services", "expose", "on", "web", "--app", "myapp", "--method", "nodeport"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "NodePort" in result.output

    def test_expose_on_colon_syntax(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["services:expose:on", "web", "--app", "myapp", "--method", "loadbalancer"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "Exposed" in result.output

    def test_expose_off_process(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        factory.services.expose_on("myapp", "web")
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["services", "expose", "off", "web", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "Unexposed" in result.output
        assert "ClusterIP" in result.output

    def test_expose_off_not_exposed_errors(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["services", "expose", "off", "web", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code != 0

    def test_expose_help(self) -> None:
        runner = CliRunner()
        result = runner.invoke(cli, ["services", "expose", "--help"])
        assert result.exit_code == 0
        assert "Expose" in result.output


class TestServicesPortsCLI:
    def test_ports_list(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["services", "ports", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "web" in result.output
        assert "8080" in result.output

    def test_ports_list_empty(self, tmp_namespace: str) -> None:
        f = KuberokuFactory(k8s_client=FakeK8sClient(), namespace=tmp_namespace)
        f.apps.create("myapp")
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["services", "ports", "--app", "myapp"],
            obj={"factory": f},
        )
        assert result.exit_code == 0
        assert "No ports" in result.output

    def test_ports_add(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["services", "ports", "add", "9090/tcp", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "Added" in result.output

    def test_ports_add_colon_syntax(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["services:ports:add", "9090/tcp", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "Added" in result.output

    def test_ports_remove(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        factory.services.add_port("myapp", "9090/tcp")
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["services", "ports", "remove", "9090/tcp", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "Removed" in result.output

    def test_ports_remove_colon_syntax(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        factory.services.add_port("myapp", "9090/tcp")
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["services:ports:remove", "9090/tcp", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "Removed" in result.output

    def test_ports_help(self) -> None:
        runner = CliRunner()
        result = runner.invoke(cli, ["services", "ports", "--help"])
        assert result.exit_code == 0
        assert "ports" in result.output.lower()

    def test_ports_add_help(self) -> None:
        runner = CliRunner()
        result = runner.invoke(cli, ["services", "ports", "add", "--help"])
        assert result.exit_code == 0
        assert "PORT_SPEC" in result.output


class TestAddonsExposeCLI:
    def test_addons_expose_on(self, tmp_namespace: str) -> None:
        factory = _addon_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["addons", "expose", "on", "postgres", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "Exposed" in result.output
        assert "internet" in result.output.lower()

    def test_addons_expose_on_colon_syntax(self, tmp_namespace: str) -> None:
        factory = _addon_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["addons:expose:on", "postgres", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "Exposed" in result.output

    def test_addons_expose_off(self, tmp_namespace: str) -> None:
        factory = _addon_factory(tmp_namespace)
        factory.addons.expose_on("myapp", "postgres")
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["addons", "expose", "off", "postgres", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "Unexposed" in result.output


class TestAddonsConnectCLI:
    def test_addons_connect_help(self) -> None:
        runner = CliRunner()
        result = runner.invoke(cli, ["addons", "connect", "--help"])
        assert result.exit_code == 0
        assert "port-forward" in result.output.lower()


class TestServicesConnectCLI:
    def test_services_connect_help(self) -> None:
        runner = CliRunner()
        result = runner.invoke(cli, ["services", "connect", "--help"])
        assert result.exit_code == 0
        assert "port-forward" in result.output.lower()
