"""RPC Server for device management over LXMF.

This module implements the server-side RPC handler that receives RPC messages
and routes them to registered command handlers.
"""

import inspect
import logging
import sys
from pathlib import Path
from typing import Any, Awaitable, Callable

# Add parent src to path for development (must be before styrene imports)
parent_src = Path(__file__).parent.parent.parent.parent.parent / "src"
if parent_src.exists():
    sys.path.insert(0, str(parent_src))

from styrened.protocols.base import LXMFMessage, Protocol  # noqa: E402  # type: ignore[import-untyped]

logger = logging.getLogger(__name__)


class RPCServer(Protocol):  # type: ignore[misc]
    """RPC server for handling device management commands.

    Routes incoming RPC messages to registered command handlers and
    sends responses back to clients.

    Attributes:
        _router: LXMF router for sending responses
        _identity: Local identity
        _handlers: Dict mapping command types to handler functions
    """

    def __init__(self, router: Any, identity: Any) -> None:
        """Initialize RPC server.

        Args:
            router: LXMF router for sending outbound messages
            identity: Local LXMF identity
        """
        self._router = router
        self._identity = identity
        self._handlers: dict[str, Callable[[LXMFMessage], Awaitable[dict[str, Any]]]] = {}

        logger.info("RPCServer initialized")

    @property
    def protocol_id(self) -> str:
        """Protocol identifier for RPC messages."""
        return "rpc"

    def can_handle(self, message: LXMFMessage) -> bool:
        """Determine if this is an RPC message.

        Args:
            message: LXMF message to evaluate

        Returns:
            True if message.fields["protocol"] == "rpc"
        """
        result: bool = message.get_protocol() == "rpc"
        return result

    def register_handler(
        self,
        command_type: str,
        handler: Callable[[LXMFMessage], Awaitable[dict[str, Any]]],
    ) -> None:
        """Register command handler for specific command type.

        Args:
            command_type: Command type identifier (e.g., "status_request", "exec")
            handler: Async function that takes LXMFMessage and returns response dict
        """
        self._handlers[command_type] = handler
        logger.debug(f"Registered handler for command type: {command_type}")

    async def handle_message(self, message: LXMFMessage) -> None:
        """Handle incoming RPC message.

        Routes message to appropriate handler based on command type,
        then sends response back to client.

        Args:
            message: Incoming LXMF RPC message
        """
        # Extract command type and request_id from message fields
        fields = message.fields
        command_type = fields.get("type")
        request_id = fields.get("request_id")

        logger.debug(
            f"Handling RPC message: type={command_type}, request_id={request_id}, "
            f"source={message.source_hash}"
        )

        # Validate message has required fields
        if not command_type:
            logger.warning("Received RPC message without command type")
            await self._send_error_response(
                message.source_hash,
                request_id,
                "Missing command type",
            )
            return

        # Check if handler exists for this command type
        if command_type not in self._handlers:
            logger.warning(f"No handler registered for command type: {command_type}")
            await self._send_error_response(
                message.source_hash,
                request_id,
                f"Unknown command type: {command_type}",
            )
            return

        # Get handler and execute
        handler = self._handlers[command_type]

        try:
            # Call handler with message
            response_data = await handler(message)

            # Add request_id to response if present in request
            if request_id:
                response_data["request_id"] = request_id

            # Add protocol field
            response_data["protocol"] = "rpc"

            # Send response
            await self._send_response(message.source_hash, response_data)

        except Exception as e:
            # Handler raised exception
            logger.error(f"Handler for {command_type} raised exception: {e}")
            await self._send_error_response(
                message.source_hash,
                request_id,
                f"Handler error: {str(e)}",
            )

    async def _send_response(self, destination: str, response_data: dict[str, Any]) -> None:
        """Send response message to client.

        Args:
            destination: Client identity hash
            response_data: Response payload dict
        """
        logger.debug(f"Sending RPC response to {destination}: {response_data.get('type')}")

        # For now, we use the router's handle_outbound method
        # In production, this would create a proper LXMF message
        try:
            # Include destination for routing (used by mock transport in tests)
            response_with_dest = {**response_data, "_destination": destination}
            # Mock implementation - actual LXMF message creation would go here
            result = self._router.handle_outbound(response_with_dest)
            # Await if it's a coroutine (for async mock routers in tests)
            if inspect.iscoroutine(result):
                await result
        except Exception as e:
            logger.error(f"Failed to send response: {e}")

    async def _send_error_response(
        self,
        destination: str,
        request_id: str | None,
        error_message: str,
    ) -> None:
        """Send error response to client.

        Args:
            destination: Client identity hash
            request_id: Request ID (if available)
            error_message: Human-readable error message
        """
        error_response = {
            "protocol": "rpc",
            "type": "error",
            "error": error_message,
        }

        if request_id:
            error_response["request_id"] = request_id

        await self._send_response(destination, error_response)

    async def send_message(self, destination: str, content: Any) -> None:
        """Send RPC message (Protocol interface method).

        This is a simplified interface for Protocol compliance.
        For server-side, we primarily receive messages and send responses.

        Args:
            destination: Destination identity hash
            content: Message content
        """
        # Server primarily receives, not sends
        # This method exists for Protocol interface compliance
        logger.warning("RPCServer.send_message called - servers typically only respond")
