"""CLI tests for Phase 4 commands — deploy, releases, ps, config --no-restart."""

from __future__ import annotations

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 FakeK8sClient


def _make_factory(namespace: str) -> KuberokuFactory:
    """Create a factory with a pre-created app."""
    f = KuberokuFactory(k8s_client=FakeK8sClient(), namespace=namespace)
    f.apps.create("myapp")
    return f


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


class TestDeployCLI:
    """CLI tests for deploy command."""

    def test_deploy_image(self, tmp_namespace: str) -> None:
        factory = _make_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["deploy", "--app", "myapp", "--image", "myapp:v1", "--no-wait"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "v1" in result.output

    def test_deploy_help(self) -> None:
        runner = CliRunner()
        result = runner.invoke(cli, ["deploy", "--help"])
        assert result.exit_code == 0
        assert "Deploy an application" in result.output

    def test_deploy_type_flag(self, tmp_namespace: str) -> None:
        factory = _make_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["deploy", "--app", "myapp", "--image", "worker:v1", "--type", "worker", "--no-wait"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0

    def test_deploy_port_flag(self, tmp_namespace: str) -> None:
        factory = _make_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["deploy", "--app", "myapp", "--image", "myapp:v1", "--port", "3000/tcp", "--no-wait"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0

    def test_deploy_multi_port_flag(self, tmp_namespace: str) -> None:
        factory = _make_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            [
                "deploy",
                "--app",
                "myapp",
                "--image",
                "myapp:v1",
                "--type",
                "smtp",
                "--port",
                "25/tcp",
                "--port",
                "587/tcp",
                "--no-wait",
            ],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        # Verify both ports are on the Deployment
        dep = factory.k8s.get_deployment(
            factory.namespace, safe_name(factory.prefix, "myapp", "smtp")
        )
        container_ports = dep["spec"]["template"]["spec"]["containers"][0]["ports"]
        assert len(container_ports) == 2
        assert container_ports[0]["containerPort"] == 25
        assert container_ports[1]["containerPort"] == 587

    def test_deploy_no_procfile_flag(self, tmp_namespace: str) -> None:
        factory = _make_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["deploy", "--app", "myapp", "--image", "myapp:v1", "--no-procfile", "--no-wait"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0


class TestReleasesCLI:
    """CLI tests for releases commands."""

    def test_releases_list(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["releases", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0

    def test_releases_list_empty(self, tmp_namespace: str) -> None:
        factory = _make_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["releases", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "No releases" in result.output

    def test_releases_info(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["releases", "info", "1", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "v1" in result.output

    def test_releases_rollback(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        # Create a second release
        factory.deploy.deploy("myapp", image="myapp:v2", wait=False)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["releases", "rollback", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "Rolled back" in result.output

    def test_releases_prune(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["releases", "prune", "--keep", "50", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0

    def test_releases_colon_syntax(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["releases:info", "1", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0


class TestPsCLI:
    """CLI tests for ps commands."""

    def test_ps_list_empty(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["ps", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "No dynos" in result.output

    def test_ps_scale(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["ps", "scale", "web=3", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "web" in result.output

    def test_ps_restart(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["ps", "restart", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "Restarted" in result.output

    def test_ps_stop(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["ps", "stop", "web", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "Stopped" in result.output

    def test_ps_set(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["ps", "set", "web=gunicorn app:app", "--no-restart", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "web" in result.output

    def test_ps_commands(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["ps", "commands", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0

    def test_ps_type_show(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["ps", "type", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0

    def test_ps_type_set_cpu(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["ps", "type", "web", "--cpu", "250m", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
        assert "Updated" in result.output

    def test_ps_colon_syntax(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["ps:scale", "web=2", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0

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


class TestConfigNoRestart:
    """CLI tests for config --no-restart flag."""

    def test_config_set_no_restart(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["config", "set", "KEY=val", "--no-restart", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0

    def test_config_unset_no_restart(self, tmp_namespace: str) -> None:
        factory = _deployed_factory(tmp_namespace)
        # First set a key
        factory.config.set("myapp", {"KEY": "val"}, no_restart=True)
        runner = CliRunner()
        result = runner.invoke(
            cli,
            ["config", "unset", "KEY", "--no-restart", "--app", "myapp"],
            obj={"factory": factory},
        )
        assert result.exit_code == 0
