"""
Tests del CLI del Agent — comandos, output y estructura.
"""
import pytest
from typer.testing import CliRunner
from unittest.mock import patch, MagicMock

from jama_agent import __version__
from jama_agent.cli import app

runner = CliRunner()


class TestVersionCommand:
    """Tests del comando version."""

    def test_version_output(self):
        """El comando version muestra la versión actual."""
        result = runner.invoke(app, ["version"])
        assert result.exit_code == 0
        assert __version__ in result.output

    def test_version_format(self):
        """El output incluye el nombre del CLI."""
        result = runner.invoke(app, ["version"])
        assert "jama-agent" in result.output


class TestStatusCommand:
    """Tests del comando status."""

    @patch("jama_agent.cli.load_credentials", return_value=None)
    def test_status_no_credentials(self, mock_creds):
        """Sin credenciales → mensaje de error amigable."""
        result = runner.invoke(app, ["status"])
        assert result.exit_code == 1
        assert "login" in result.output.lower() or "credenciales" in result.output.lower()

    @patch("jama_agent.cli.load_config")
    @patch("jama_agent.cli.load_credentials")
    def test_status_with_credentials(self, mock_creds, mock_config):
        """Con credenciales → muestra info del agent."""
        mock_creds.return_value = {
            "server": "http://localhost:8000",
            "email": "test@example.com",
            "access_token": "fake-token-1234567890",
            "agent_id": "abc-123",
        }
        mock_config_obj = MagicMock()
        mock_config_obj.agent_name = "test-host"
        mock_config_obj.os_type = "linux"
        mock_config_obj.heartbeat_interval = 30
        mock_config.return_value = mock_config_obj

        result = runner.invoke(app, ["status"])
        assert result.exit_code == 0
        assert "localhost" in result.output
        assert "test@example.com" in result.output


class TestLoginCommand:
    """Tests del comando login."""

    @patch("jama_agent.cli.save_config")
    @patch("jama_agent.cli.load_config")
    @patch("jama_agent.cli.register_agent", return_value="agent-uuid-123")
    @patch("jama_agent.cli.do_login")
    @patch("jama_agent.cli.getpass.getpass", return_value="secret")
    def test_login_success(self, mock_pass, mock_login, mock_register, mock_config, mock_save):
        """Login exitoso guarda credenciales y registra agent."""
        mock_login.return_value = {
            "access_token": "tok",
            "refresh_token": "ref",
            "agent_id": "",
        }
        mock_config.return_value = MagicMock()

        result = runner.invoke(
            app, ["login", "--server", "http://test:8000", "--email", "a@b.com"]
        )
        assert result.exit_code == 0
        mock_login.assert_called_once_with("http://test:8000", "a@b.com", "secret")

    @patch("jama_agent.cli.do_login", side_effect=Exception("Connection refused"))
    @patch("jama_agent.cli.getpass.getpass", return_value="bad")
    def test_login_failure(self, mock_pass, mock_login):
        """Login fallido → error amigable."""
        result = runner.invoke(
            app, ["login", "--server", "http://bad:8000", "--email", "a@b.com"]
        )
        assert result.exit_code == 1
        assert "error" in result.output.lower() or "Error" in result.output


class TestDisconnectCommand:
    """Tests del comando disconnect."""

    @patch("jama_agent.cli.clear_credentials")
    def test_disconnect_clears_creds(self, mock_clear):
        """Disconnect limpia las credenciales."""
        result = runner.invoke(app, ["disconnect"])
        assert result.exit_code == 0
        mock_clear.assert_called_once()


class TestDiscoverCommand:
    """Tests del comando discover."""

    @patch("jama_agent.cli.discover_network")
    def test_discover_shows_hosts(self, mock_discover):
        """Discover muestra hosts encontrados en tabla."""
        mock_discover.return_value = {
            "hosts": [
                {"ip": "192.168.1.1", "mac": "aa:bb:cc:dd:ee:ff", "hostname": "router"},
                {"ip": "192.168.1.100", "mac": "11:22:33:44:55:66", "hostname": ""},
            ],
            "subnet": "192.168.1",
            "method": "arp",
            "total": 2,
        }

        result = runner.invoke(app, ["discover"])
        assert result.exit_code == 0
        assert "192.168.1.1" in result.output
        assert "192.168.1.100" in result.output

    @patch("jama_agent.cli.discover_network")
    def test_discover_empty(self, mock_discover):
        """Discover sin hosts → mensaje informativo."""
        mock_discover.return_value = {
            "hosts": [],
            "subnet": "192.168.1",
            "method": "arp",
            "total": 0,
        }

        result = runner.invoke(app, ["discover"])
        assert result.exit_code == 0


class TestAppStructure:
    """Tests de la estructura de la aplicación Typer."""

    def test_app_has_expected_commands(self):
        """La app tiene todos los comandos esperados."""
        # Typer usa callback.__name__ o el nombre de la función registrada
        command_names = [
            cmd.name or (cmd.callback.__name__ if cmd.callback else None)
            for cmd in app.registered_commands
        ]
        expected = ["login", "connect", "status", "disconnect", "discover", "forward", "version"]
        for cmd in expected:
            assert cmd in command_names, f"Falta comando: {cmd}"

    def test_no_args_shows_help(self):
        """Sin argumentos muestra ayuda (help text)."""
        result = runner.invoke(app, [])
        # Typer con no_args_is_help=True puede retornar 0 o 2
        assert result.exit_code in (0, 2)
        assert "Usage" in result.output or "usage" in result.output or "jama" in result.output.lower()
