"""
Tests for HostClient.
"""

from unittest.mock import MagicMock, patch

import grpc
import pytest

from daisi.clients.hosts import HostClient
from daisi.config import DaisiConfig
from daisi.exceptions import DaisiError
from protos.v1.models import HostModels_pb2


def create_grpc_error(details: str = "Test error") -> grpc.RpcError:
    """Create a mock gRPC error with details method."""
    error = grpc.RpcError()
    error.details = MagicMock(return_value=details)
    return error


class TestHostClientInitialization:
    """Test HostClient initialization."""

    def test_init_with_config(self) -> None:
        """Test initialization with config."""
        config = DaisiConfig(client_key="test-key")
        client = HostClient(config)
        assert client.config == config
        assert client._stub is None

    def test_stub_lazy_initialization(self) -> None:
        """Test stub is lazily initialized."""
        config = DaisiConfig(client_key="test-key")
        client = HostClient(config)

        with patch.object(client, "_get_channel") as mock_get_channel:
            mock_channel = MagicMock()
            mock_get_channel.return_value = mock_channel

            stub = client.stub
            assert stub is not None
            mock_get_channel.assert_called_once()


class TestHostClientGetHosts:
    """Test HostClient get_hosts method."""

    def test_get_hosts_success(self) -> None:
        """Test getting hosts successfully."""
        config = DaisiConfig(client_key="test-key")
        client = HostClient(config)

        mock_stub = MagicMock()
        mock_host1 = MagicMock()
        mock_host2 = MagicMock()
        mock_response = MagicMock()
        mock_response.Hosts = [mock_host1, mock_host2]
        mock_stub.GetHosts.return_value = mock_response

        client._stub = mock_stub
        result = client.get_hosts()

        assert len(result) == 2
        assert result == [mock_host1, mock_host2]
        mock_stub.GetHosts.assert_called_once()

    def test_get_hosts_empty(self) -> None:
        """Test getting hosts with empty result."""
        config = DaisiConfig(client_key="test-key")
        client = HostClient(config)

        mock_stub = MagicMock()
        mock_response = MagicMock()
        mock_response.Hosts = []
        mock_stub.GetHosts.return_value = mock_response

        client._stub = mock_stub
        result = client.get_hosts()

        assert len(result) == 0

    def test_get_hosts_grpc_error(self) -> None:
        """Test get hosts with gRPC error."""
        config = DaisiConfig(client_key="test-key")
        client = HostClient(config)

        mock_stub = MagicMock()
        mock_stub.GetHosts.side_effect = create_grpc_error()

        client._stub = mock_stub
        with pytest.raises(DaisiError, match="Failed to get hosts"):
            client.get_hosts()


class TestHostClientUpdateHost:
    """Test HostClient update_host method."""

    def test_update_host_success(self) -> None:
        """Test updating host successfully."""
        config = DaisiConfig(client_key="test-key")
        client = HostClient(config)

        mock_stub = MagicMock()
        mock_response = MagicMock()
        mock_response.Success = True
        mock_response.Message = "Updated successfully"
        mock_stub.UpdateHost.return_value = mock_response

        client._stub = mock_stub

        # Use patch to mock the UpdateHostRequest creation since protobuf messages
        # are strict about their inputs
        with patch("daisi.clients.hosts.HostModels_pb2.UpdateHostRequest"):
            mock_host = MagicMock()
            success, message = client.update_host(mock_host)

            assert success is True
            assert message == "Updated successfully"
            mock_stub.UpdateHost.assert_called_once()

    def test_update_host_failure(self) -> None:
        """Test updating host failure."""
        config = DaisiConfig(client_key="test-key")
        client = HostClient(config)

        mock_stub = MagicMock()
        mock_response = MagicMock()
        mock_response.Success = False
        mock_response.Message = "Update failed"
        mock_stub.UpdateHost.return_value = mock_response

        client._stub = mock_stub

        with patch("daisi.clients.hosts.HostModels_pb2.UpdateHostRequest"):
            mock_host = MagicMock()
            success, message = client.update_host(mock_host)

            assert success is False
            assert message == "Update failed"

    def test_update_host_grpc_error(self) -> None:
        """Test update host with gRPC error."""
        config = DaisiConfig(client_key="test-key")
        client = HostClient(config)

        mock_stub = MagicMock()
        mock_stub.UpdateHost.side_effect = create_grpc_error()

        client._stub = mock_stub

        with patch("daisi.clients.hosts.HostModels_pb2.UpdateHostRequest"):
            mock_host = MagicMock()
            with pytest.raises(DaisiError, match="Failed to update host"):
                client.update_host(mock_host)


class TestHostClientRegisterHost:
    """Test HostClient register_host method."""

    def test_register_host_success(self) -> None:
        """Test registering host successfully."""
        config = DaisiConfig(client_key="test-key")
        client = HostClient(config)

        mock_stub = MagicMock()
        mock_response = MagicMock()
        mock_response.SecretKey = "secret-123"
        mock_response.HostId = "host-456"
        mock_stub.RegisterHost.return_value = mock_response

        client._stub = mock_stub

        with patch("daisi.clients.hosts.HostModels_pb2.RegisterHostRequest"):
            mock_host = MagicMock()
            secret_key, host_id = client.register_host(mock_host)

            assert secret_key == "secret-123"
            assert host_id == "host-456"
            mock_stub.RegisterHost.assert_called_once()

    def test_register_host_grpc_error(self) -> None:
        """Test register host with gRPC error."""
        config = DaisiConfig(client_key="test-key")
        client = HostClient(config)

        mock_stub = MagicMock()
        mock_stub.RegisterHost.side_effect = create_grpc_error()

        client._stub = mock_stub

        with patch("daisi.clients.hosts.HostModels_pb2.RegisterHostRequest"):
            mock_host = MagicMock()
            with pytest.raises(DaisiError, match="Failed to register host"):
                client.register_host(mock_host)
