"""
Copyright (C) 2025 Luciano Guerche

This file is part of rabbitmq-mcp-server.

rabbitmq-mcp-server is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

rabbitmq-mcp-server is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with rabbitmq-mcp-server. If not, see <https://www.gnu.org/licenses/>.
"""

"""JSON-RPC 2.0 message validation and response builders."""

import json
from typing import Any

# JSON-RPC 2.0 Error Codes
PARSE_ERROR = -32700
INVALID_REQUEST = -32600
METHOD_NOT_FOUND = -32601
INVALID_PARAMS = -32602
SERVER_ERROR = -32000

JSONRPC_VERSION = "2.0"


class JSONRPCError(Exception):
    """Base exception for JSON-RPC errors."""

    def __init__(self, code: int, message: str, data: Any = None) -> None:
        self.code = code
        self.message = message
        self.data = data
        super().__init__(message)


class ParseError(JSONRPCError):
    """Invalid JSON was received."""

    def __init__(self, data: Any = None) -> None:
        super().__init__(PARSE_ERROR, "Parse error", data)


class InvalidRequestError(JSONRPCError):
    """The JSON sent is not a valid Request object."""

    def __init__(self, message: str = "Invalid Request", data: Any = None) -> None:
        super().__init__(INVALID_REQUEST, message, data)


class MethodNotFoundError(JSONRPCError):
    """The method does not exist / is not available."""

    def __init__(self, method: str, data: Any = None) -> None:
        super().__init__(METHOD_NOT_FOUND, f"Method not found: {method}", data)


class InvalidParamsError(JSONRPCError):
    """Invalid method parameter(s)."""

    def __init__(self, message: str = "Invalid params", data: Any = None) -> None:
        super().__init__(INVALID_PARAMS, message, data)


class ServerError(JSONRPCError):
    """Internal JSON-RPC error."""

    def __init__(self, message: str = "Server error", data: Any = None) -> None:
        super().__init__(SERVER_ERROR, message, data)


def validate_request(data: str) -> dict[str, Any]:
    """
    Validate and parse JSON-RPC 2.0 request.

    Args:
        data: Raw JSON string

    Returns:
        Parsed request dictionary

    Raises:
        ParseError: If JSON is invalid
        InvalidRequestError: If request structure is invalid
    """
    try:
        request = json.loads(data)
    except json.JSONDecodeError as e:
        raise ParseError(str(e))

    if not isinstance(request, dict):
        raise InvalidRequestError("Request must be an object")

    # Validate jsonrpc field
    if request.get("jsonrpc") != JSONRPC_VERSION:
        raise InvalidRequestError(f"jsonrpc must be '{JSONRPC_VERSION}'")

    # Validate id field
    if "id" not in request:
        raise InvalidRequestError("Missing required field: id")

    request_id = request["id"]
    if not isinstance(request_id, int | str | type(None)):
        raise InvalidRequestError("id must be a number, string, or null")

    # Validate method field
    if "method" not in request:
        raise InvalidRequestError("Missing required field: method")

    method = request["method"]
    if not isinstance(method, str):
        raise InvalidRequestError("method must be a string")

    # Validate params field (optional)
    if "params" in request:
        params = request["params"]
        if not isinstance(params, dict | list):
            raise InvalidRequestError("params must be an object or array")

    return request


def build_response(request_id: int | str | None, result: Any) -> dict[str, Any]:
    """
    Build JSON-RPC 2.0 success response.

    Args:
        request_id: ID from the request
        result: Result value

    Returns:
        Response dictionary
    """
    return {"jsonrpc": JSONRPC_VERSION, "id": request_id, "result": result}


def build_error_response(
    request_id: int | str | None, error: JSONRPCError, correlation_id: str | None = None
) -> dict[str, Any]:
    """
    Build JSON-RPC 2.0 error response.

    Args:
        request_id: ID from the request (may be None for parse errors)
        error: The JSON-RPC error
        correlation_id: Optional correlation ID for tracing

    Returns:
        Error response dictionary
    """
    error_data = error.data

    # Include correlation_id in error data if provided
    if correlation_id:
        if error_data is None:
            error_data = {"correlation_id": correlation_id}
        elif isinstance(error_data, dict):
            error_data["correlation_id"] = correlation_id
        else:
            error_data = {"original_data": error_data, "correlation_id": correlation_id}

    return {
        "jsonrpc": JSONRPC_VERSION,
        "id": request_id,
        "error": {"code": error.code, "message": error.message, "data": error_data},
    }


def serialize_response(response: dict[str, Any]) -> str:
    """
    Serialize response to JSON string.

    Args:
        response: Response dictionary

    Returns:
        JSON string
    """
    return json.dumps(response, separators=(",", ":"))
