"""
IPC protocol implementation (JSON-RPC 2.0 based).
"""

import asyncio
from typing import Any, Optional, Union

import orjson

from airctl.models.protocol import JSONRPCError, JSONRPCRequest, JSONRPCResponse


class IPCProtocol:
    """
    JSON-RPC 2.0 based IPC protocol handler.
    
    Handles serialization/deserialization and request/response matching.
    """

    def __init__(self) -> None:
        self._request_id = 0
        self._pending_requests: dict[Union[int, str], asyncio.Future] = {}

    def create_request(
        self, method: str, params: Optional[dict[str, Any]] = None
    ) -> JSONRPCRequest:
        """Create a new request with auto-incrementing ID."""
        self._request_id += 1
        return JSONRPCRequest(id=self._request_id, method=method, params=params)

    def create_response(
        self,
        request_id: Union[int, str],
        result: Optional[Any] = None,
        error: Optional[JSONRPCError] = None,
    ) -> JSONRPCResponse:
        """Create a response for a request."""
        return JSONRPCResponse(id=request_id, result=result, error=error)

    def create_error_response(
        self,
        request_id: Optional[Union[int, str]],
        code: int,
        message: str,
        data: Optional[Any] = None,
    ) -> JSONRPCResponse:
        """Create an error response."""
        return JSONRPCResponse(
            id=request_id,
            error=JSONRPCError(code=code, message=message, data=data),
        )

    def serialize(self, obj: Union[JSONRPCRequest, JSONRPCResponse]) -> bytes:
        """Serialize a request or response to bytes."""
        if hasattr(obj, "model_dump"):
            data = obj.model_dump(exclude_none=True)
        else:
            data = obj
        return orjson.dumps(data) + b"\n"

    def deserialize(self, data: bytes) -> Union[JSONRPCRequest, JSONRPCResponse]:
        """Deserialize bytes to a request or response."""
        obj = orjson.loads(data)
        
        if "method" in obj:
            return JSONRPCRequest(**obj)
        else:
            return JSONRPCResponse(**obj)

    def register_pending_request(
        self, request_id: Union[int, str], future: asyncio.Future
    ) -> None:
        """Register a pending request for response matching."""
        self._pending_requests[request_id] = future

    def get_pending_request(
        self, request_id: Union[int, str]
    ) -> Optional[asyncio.Future]:
        """Get and remove a pending request future."""
        return self._pending_requests.pop(request_id, None)

    def has_pending_requests(self) -> bool:
        """Check if there are pending requests."""
        return len(self._pending_requests) > 0


class IPCError(Exception):
    """IPC error exception."""

    def __init__(self, code: int, message: str, data: Optional[Any] = None):
        self.code = code
        self.message = message
        self.data = data
        super().__init__(f"IPC Error {code}: {message}")


class IPCErrorCode:
    """Standard JSON-RPC error codes."""

    PARSE_ERROR = -32700
    INVALID_REQUEST = -32600
    METHOD_NOT_FOUND = -32601
    INVALID_PARAMS = -32602
    INTERNAL_ERROR = -32603
    
    DAEMON_NOT_RUNNING = -32001
    DEVICE_NOT_FOUND = -32002
    CHARACTERISTIC_NOT_FOUND = -32003
    CONNECTION_FAILED = -32004
    OPERATION_FAILED = -32005
