"""
Centrifugo RPC Proxy View.

Handles RPC calls proxied from Centrifugo server to Django.
When a client calls centrifuge.rpc('method', data), Centrifugo
forwards the request to this endpoint.

Flow:
1. Client calls: centrifuge.rpc('terminal.input', {session_id: '...', data: '...'})
2. Centrifugo proxies to: POST /centrifugo/rpc/
3. This view routes to registered @websocket_rpc handler
4. Response returned to client via Centrifugo
"""

import json
import logging
import traceback
from typing import Any

from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from pydantic import BaseModel, Field, ValidationError

from ..router import get_global_router

logger = logging.getLogger(__name__)


class RPCProxyRequest(BaseModel):
    """Request from Centrifugo RPC proxy."""

    client: str = Field(..., description="Client connection ID")
    transport: str = Field("websocket", description="Transport type")
    protocol: str = Field("json", description="Protocol format")
    encoding: str = Field("json", description="Encoding type")
    user: str = Field("", description="User ID from auth")
    method: str = Field("", description="RPC method name")
    data: dict[str, Any] = Field(default_factory=dict, description="RPC params")
    meta: dict[str, Any] | None = Field(None, description="Connection metadata")


class RPCConnection:
    """
    Connection context passed to RPC handlers.

    Provides user_id and metadata from Centrifugo connection.
    """

    def __init__(self, request: RPCProxyRequest):
        self.client_id = request.client
        self.user_id = request.user or None
        self.transport = request.transport
        self.protocol = request.protocol
        self.meta = request.meta or {}

    def __repr__(self):
        return f"<RPCConnection user={self.user_id} client={self.client_id[:8]}...>"


@method_decorator(csrf_exempt, name='dispatch')
class RPCProxyView(View):
    """
    Centrifugo RPC Proxy endpoint.

    Receives RPC calls from Centrifugo and routes them to
    registered @websocket_rpc handlers.

    Request format (from Centrifugo):
    {
        "client": "uuid",
        "user": "user_id",
        "method": "terminal.input",
        "data": {"session_id": "...", "data": "..."}
    }

    Response format (to Centrifugo):
    Success: {"result": {"data": {...}}}
    Error: {"error": {"code": 100, "message": "error"}}
    """

    async def post(self, request):
        """Handle RPC proxy request from Centrifugo."""
        try:
            # Parse request body
            body = json.loads(request.body)
            rpc_request = RPCProxyRequest(**body)

            logger.debug(
                f"RPC proxy: method={rpc_request.method} "
                f"user={rpc_request.user} client={rpc_request.client[:8]}..."
            )

            # Get router and check if handler exists
            router = get_global_router()

            if not rpc_request.method:
                return self._error_response(101, "Method name required")

            if not router.has_handler(rpc_request.method):
                logger.warning(f"RPC method not found: {rpc_request.method}")
                return self._error_response(102, f"Method '{rpc_request.method}' not found")

            # Create connection context
            conn = RPCConnection(rpc_request)

            # Get handler and extract param type
            handler = router.get_handler(rpc_request.method)

            # Try to validate params using handler's type hints
            params = rpc_request.data
            try:
                # Get param type from handler signature
                import inspect
                from typing import get_type_hints

                hints = get_type_hints(handler)
                sig = inspect.signature(handler)
                params_list = list(sig.parameters.values())

                if len(params_list) >= 2:
                    param_name = params_list[1].name
                    param_type = hints.get(param_name)

                    if param_type and hasattr(param_type, 'model_validate'):
                        # Pydantic model - validate
                        params = param_type.model_validate(params)

            except ValidationError as e:
                logger.warning(f"RPC param validation error: {e}")
                return self._error_response(103, f"Invalid params: {e.errors()}")
            except Exception as e:
                # Continue without validation
                logger.debug(f"Could not validate params: {e}")

            # Call handler
            try:
                result = await handler(conn, params)

                # Serialize result
                if hasattr(result, 'model_dump'):
                    result_data = result.model_dump()
                elif isinstance(result, dict):
                    result_data = result
                else:
                    result_data = {"result": result}

                logger.debug(f"RPC success: {rpc_request.method}")
                return self._success_response(result_data)

            except Exception as e:
                logger.error(
                    f"RPC handler error: {rpc_request.method}: {e}",
                    exc_info=True
                )
                return self._error_response(500, str(e))

        except json.JSONDecodeError as e:
            logger.error(f"RPC proxy: invalid JSON: {e}")
            return self._error_response(100, "Invalid JSON")

        except ValidationError as e:
            logger.error(f"RPC proxy: validation error: {e}")
            return self._error_response(100, f"Invalid request: {e.errors()}")

        except Exception as e:
            logger.error(f"RPC proxy error: {e}", exc_info=True)
            return self._error_response(500, "Internal server error")

    def _success_response(self, data: dict) -> JsonResponse:
        """Return success response in Centrifugo format."""
        return JsonResponse({
            "result": {
                "data": data
            }
        })

    def _error_response(self, code: int, message: str) -> JsonResponse:
        """Return error response in Centrifugo format."""
        return JsonResponse({
            "error": {
                "code": code,
                "message": message
            }
        })


__all__ = ["RPCProxyView", "RPCConnection"]
