import inspect
import json
import logging
from typing import Any, Callable, TypeVar

from arcade.worker.mcp.types import InitializeRequest, JSONRPCRequest, MCPMessage

logger = logging.getLogger("arcade.mcp")

T = TypeVar("T")

# Type definition for middleware functions
MessageProcessor = Callable[[Any, str], Any]


class MCPMessageProcessor:
    """
    Processes MCP messages through a chain of middleware.
    Supports both synchronous and asynchronous middleware.
    """

    def __init__(self) -> None:
        self.middleware: list[Callable[[MCPMessage, str], Any]] = []

    def add_middleware(self, mw: Callable[[MCPMessage, str], Any]) -> None:
        self.middleware.append(mw)

    async def process(self, message: Any, direction: str) -> Any:  # noqa: C901
        # First, try to parse the message if it's a string
        if isinstance(message, str):
            # Strip any whitespace including newlines
            message = message.strip()
            if not message:
                return None

            try:
                parsed = json.loads(message)
                if isinstance(parsed, dict):
                    method = parsed.get("method")
                    # Convert to appropriate message type
                    if method == "initialize" and "id" in parsed:
                        logger.debug(f"Parsed initialize request: {parsed}")
                        message = InitializeRequest(**parsed)
                    elif method and method.startswith("notifications/"):
                        # It's a notification, log it but pass through as dict
                        logger.debug(f"Received notification: {method}")
                        # Keep as parsed dict to avoid validation errors on unknown notifications
                        message = parsed
                    elif "method" in parsed and "id" in parsed:
                        # Regular method request
                        logger.debug(f"Parsed method request: {method}")
                        message = JSONRPCRequest(**parsed)
                    # Other message types can be handled similarly
            except json.JSONDecodeError:
                logger.warning(f"Failed to parse message as JSON: {message[:100]}...")
            except Exception:
                logger.exception("Error processing message")

        # Process through middleware chain
        result = message
        for mw in self.middleware:
            try:
                if inspect.iscoroutinefunction(mw):
                    result = await mw(result, direction)
                else:
                    result = mw(result, direction)
            except Exception:
                logger.exception(f"Error in middleware {mw}")
        return result

    async def process_request(self, message: Any) -> Any:
        return await self.process(message, "request")

    async def process_response(self, message: Any) -> Any:
        return await self.process(message, "response")


def create_message_processor(*middleware: MessageProcessor) -> MCPMessageProcessor:
    processor = MCPMessageProcessor()
    for m in middleware:
        if m is not None:
            processor.add_middleware(m)
    return processor
