import subprocess
from types import SimpleNamespace
import json

from typer.testing import CliRunner

from l4d2host.cli import app


def test_help_lists_v1_commands() -> None:
    result = CliRunner().invoke(app, ["--help"])
    assert result.exit_code == 0
    for command in ["install", "initialize", "start", "stop", "delete"]:
        assert command in result.output


def test_install_uses_left4me_steamcmd_env_var(monkeypatch) -> None:
    captured: dict[str, str] = {}

    class FakeInstaller:
        def __init__(self, *, steamcmd):
            captured["steamcmd"] = steamcmd

        def install_or_update(self, **kwargs):
            del kwargs

    monkeypatch.setattr("l4d2host.cli.SteamInstaller", FakeInstaller)
    monkeypatch.setenv("LEFT4ME_STEAMCMD", "/var/lib/left4me/steam/steamcmd.sh")

    result = CliRunner().invoke(app, ["install"])

    assert result.exit_code == 0
    assert captured["steamcmd"] == "/var/lib/left4me/steam/steamcmd.sh"


def test_install_defaults_to_bare_steamcmd_when_env_unset(monkeypatch) -> None:
    captured: dict[str, str] = {}

    class FakeInstaller:
        def __init__(self, *, steamcmd):
            captured["steamcmd"] = steamcmd

        def install_or_update(self, **kwargs):
            del kwargs

    monkeypatch.setattr("l4d2host.cli.SteamInstaller", FakeInstaller)
    monkeypatch.delenv("LEFT4ME_STEAMCMD", raising=False)

    result = CliRunner().invoke(app, ["install"])

    assert result.exit_code == 0
    assert captured["steamcmd"] == "steamcmd"


def test_cli_propagates_subprocess_return_code(monkeypatch) -> None:
    def fail(*args, **kwargs):
        del args
        del kwargs
        raise subprocess.CalledProcessError(returncode=9, cmd=["x"], stderr="boom")

    monkeypatch.setattr("l4d2host.cli.start_instance", fail)

    result = CliRunner().invoke(app, ["start", "alpha"])

    assert result.exit_code == 9
    assert "boom" in result.stderr


def test_status_command_outputs_json(monkeypatch) -> None:
    monkeypatch.setattr(
        "l4d2host.cli.get_instance_status",
        lambda name: SimpleNamespace(state="running", raw_active_state="active", raw_sub_state="running"),
        raising=False,
    )

    result = CliRunner().invoke(app, ["status", "alpha", "--json"])

    assert result.exit_code == 0
    assert json.loads(result.output) == {
        "state": "running",
        "raw_active_state": "active",
        "raw_sub_state": "running",
    }


def test_logs_command_streams_lines(monkeypatch) -> None:
    monkeypatch.setattr(
        "l4d2host.cli.stream_instance_logs",
        lambda name, *, lines, follow: iter([f"{name}:{lines}:{follow}", "ready"]),
        raising=False,
    )

    result = CliRunner().invoke(app, ["logs", "alpha", "--lines", "25", "--no-follow"])

    assert result.exit_code == 0
    assert result.output.splitlines() == ["alpha:25:False", "ready"]


def test_logs_command_propagates_subprocess_return_code(monkeypatch) -> None:
    def fail_logs(*args, **kwargs):
        del args
        del kwargs
        raise subprocess.CalledProcessError(returncode=7, cmd=["logs"], stderr="sudo denied")

    monkeypatch.setattr("l4d2host.cli.stream_instance_logs", fail_logs, raising=False)

    result = CliRunner().invoke(app, ["logs", "alpha", "--no-follow"])

    assert result.exit_code == 7
    assert "sudo denied" in result.stderr
