"""Tests for RPC Server core."""

import pytest
from unittest.mock import Mock, AsyncMock

from styrene_bond_rpc.server import RPCServer
from styrened.protocols.base import LXMFMessage


@pytest.fixture
def mock_router():
    """Create mock LXMF router."""
    router = Mock()
    router.handle_outbound = Mock()
    return router


@pytest.fixture
def mock_identity():
    """Create mock identity."""
    identity = Mock()
    identity.hexhash = "server_identity"
    return identity


@pytest.fixture
def rpc_server(mock_router, mock_identity):
    """Create RPC server instance."""
    return RPCServer(router=mock_router, identity=mock_identity)


class TestRPCServerInitialization:
    """Test RPC server initialization."""

    def test_server_initialization(self, rpc_server: RPCServer) -> None:
        """RPCServer should initialize without errors."""
        assert rpc_server is not None
        assert rpc_server.protocol_id == "rpc"

    def test_server_can_handle_rpc_messages(self, rpc_server: RPCServer) -> None:
        """RPCServer should handle messages with protocol='rpc'."""
        msg = LXMFMessage(
            source_hash="client_hash",
            destination_hash="server_hash",
            timestamp=123.0,
            fields={"protocol": "rpc"},
        )
        assert rpc_server.can_handle(msg)

    def test_server_cannot_handle_non_rpc_messages(self, rpc_server: RPCServer) -> None:
        """RPCServer should not handle non-RPC messages."""
        msg = LXMFMessage(
            source_hash="client_hash",
            destination_hash="server_hash",
            timestamp=123.0,
            fields={"protocol": "chat"},
        )
        assert not rpc_server.can_handle(msg)


class TestHandlerRegistration:
    """Test command handler registration."""

    def test_register_handler(self, rpc_server: RPCServer) -> None:
        """Should register handler for command type."""
        async def mock_handler(msg: LXMFMessage) -> dict:
            return {"result": "ok"}

        rpc_server.register_handler("status", mock_handler)

        # Verify handler was registered
        assert "status" in rpc_server._handlers
        assert rpc_server._handlers["status"] == mock_handler

    def test_register_multiple_handlers(self, rpc_server: RPCServer) -> None:
        """Should register multiple handlers."""
        async def status_handler(msg: LXMFMessage) -> dict:
            return {"status": "ok"}

        async def exec_handler(msg: LXMFMessage) -> dict:
            return {"exec": "ok"}

        rpc_server.register_handler("status", status_handler)
        rpc_server.register_handler("exec", exec_handler)

        assert len(rpc_server._handlers) == 2
        assert "status" in rpc_server._handlers
        assert "exec" in rpc_server._handlers

    def test_register_handler_overwrites_existing(self, rpc_server: RPCServer) -> None:
        """Registering same command type should overwrite."""
        async def handler1(msg: LXMFMessage) -> dict:
            return {"v": 1}

        async def handler2(msg: LXMFMessage) -> dict:
            return {"v": 2}

        rpc_server.register_handler("status", handler1)
        rpc_server.register_handler("status", handler2)

        assert rpc_server._handlers["status"] == handler2


class TestMessageRouting:
    """Test message routing to handlers."""

    @pytest.mark.asyncio
    async def test_route_message_to_handler(
        self, rpc_server: RPCServer, mock_router
    ) -> None:
        """Should route message to registered handler."""
        # Register mock handler
        handler_called = False
        handler_message = None

        async def mock_handler(msg: LXMFMessage) -> dict:
            nonlocal handler_called, handler_message
            handler_called = True
            handler_message = msg
            return {"status": "ok", "type": "status_response"}

        rpc_server.register_handler("status_request", mock_handler)

        # Create RPC message
        msg = LXMFMessage(
            source_hash="client_hash",
            destination_hash="server_hash",
            timestamp=123.0,
            fields={"protocol": "rpc", "type": "status_request", "request_id": "req123"},
        )

        # Handle message
        await rpc_server.handle_message(msg)

        # Verify handler was called
        assert handler_called
        assert handler_message == msg

    @pytest.mark.asyncio
    async def test_unknown_command_type(
        self, rpc_server: RPCServer, mock_router
    ) -> None:
        """Should handle unknown command type gracefully."""
        msg = LXMFMessage(
            source_hash="client_hash",
            destination_hash="server_hash",
            timestamp=123.0,
            fields={
                "protocol": "rpc",
                "type": "unknown_command",
                "request_id": "req123",
            },
        )

        # Should not raise exception
        await rpc_server.handle_message(msg)

        # Should send error response
        assert mock_router.handle_outbound.called

    @pytest.mark.asyncio
    async def test_handler_response_sent_to_client(
        self, rpc_server: RPCServer, mock_router
    ) -> None:
        """Handler response should be sent back to client."""
        async def mock_handler(msg: LXMFMessage) -> dict:
            return {"status": "ok", "uptime": 12345}

        rpc_server.register_handler("status_request", mock_handler)

        msg = LXMFMessage(
            source_hash="client_hash",
            destination_hash="server_hash",
            timestamp=123.0,
            fields={"protocol": "rpc", "type": "status_request", "request_id": "req123"},
        )

        await rpc_server.handle_message(msg)

        # Verify outbound message sent
        assert mock_router.handle_outbound.called

        # Check response includes request_id
        call_args = mock_router.handle_outbound.call_args
        # Response should have request_id from original message
        # (actual verification depends on implementation)


class TestErrorHandling:
    """Test error handling in message routing."""

    @pytest.mark.asyncio
    async def test_handler_exception_handled(
        self, rpc_server: RPCServer, mock_router
    ) -> None:
        """Exception in handler should not crash server."""
        async def failing_handler(msg: LXMFMessage) -> dict:
            raise ValueError("Handler failed")

        rpc_server.register_handler("exec", failing_handler)

        msg = LXMFMessage(
            source_hash="client_hash",
            destination_hash="server_hash",
            timestamp=123.0,
            fields={"protocol": "rpc", "type": "exec", "request_id": "req123"},
        )

        # Should not raise exception
        await rpc_server.handle_message(msg)

        # Should send error response
        assert mock_router.handle_outbound.called

    @pytest.mark.asyncio
    async def test_missing_request_id(
        self, rpc_server: RPCServer, mock_router
    ) -> None:
        """Message without request_id should be handled gracefully."""
        async def mock_handler(msg: LXMFMessage) -> dict:
            return {"status": "ok"}

        rpc_server.register_handler("status_request", mock_handler)

        msg = LXMFMessage(
            source_hash="client_hash",
            destination_hash="server_hash",
            timestamp=123.0,
            fields={"protocol": "rpc", "type": "status_request"},
            # Missing request_id
        )

        # Should not crash
        await rpc_server.handle_message(msg)
