"""
Utilities for mapping exceptions to Anthropic error format.

Similar to litellm/litellm_core_utils/exception_mapping_utils.py but for Anthropic response format.
"""

from litellm.litellm_core_utils.safe_json_loads import safe_json_loads
from typing import Dict, Optional

from .exceptions import AnthropicErrorResponse, AnthropicErrorType

# HTTP status code -> Anthropic error type
# Source: https://docs.anthropic.com/en/api/errors
ANTHROPIC_ERROR_TYPE_MAP: Dict[int, AnthropicErrorType] = {
    400: "invalid_request_error",
    401: "authentication_error",
    403: "permission_error",
    404: "not_found_error",
    413: "request_too_large",
    429: "rate_limit_error",
    500: "api_error",
    529: "overloaded_error",
}


class AnthropicExceptionMapping:
    """
    Helper class for mapping exceptions to Anthropic error format.

    Similar pattern to ExceptionCheckers in litellm_core_utils/exception_mapping_utils.py
    """

    @staticmethod
    def get_error_type(status_code: int) -> AnthropicErrorType:
        """Map HTTP status code to Anthropic error type."""
        return ANTHROPIC_ERROR_TYPE_MAP.get(status_code, "api_error")

    @staticmethod
    def create_error_response(
        status_code: int,
        message: str,
        request_id: Optional[str] = None,
    ) -> AnthropicErrorResponse:
        """
        Create an Anthropic-formatted error response dict.

        Anthropic error format:
        {
            "type": "error",
            "error": {"type": "...", "message": "..."},
            "request_id": "req_..."
        }
        """
        error_type = AnthropicExceptionMapping.get_error_type(status_code)

        response: AnthropicErrorResponse = {
            "type": "error",
            "error": {
                "type": error_type,
                "message": message,
            },
        }

        if request_id:
            response["request_id"] = request_id

        return response

    @staticmethod
    def extract_error_message(raw_message: str) -> str:
        """
        Extract error message from various provider response formats.

        Handles:
        - Bedrock: {"detail": {"message": "..."}}
        - AWS: {"Message": "..."}
        - Generic: {"message": "..."}
        - Plain strings
        """
        parsed = safe_json_loads(raw_message)
        if isinstance(parsed, dict):
            # Bedrock format
            if "detail" in parsed and isinstance(parsed["detail"], dict):
                return parsed["detail"].get("message", raw_message)
            # AWS/generic format
            return parsed.get("Message") or parsed.get("message") or raw_message
        return raw_message

    @staticmethod
    def _is_anthropic_error_dict(parsed: dict) -> bool:
        """
        Check if a parsed dict is in Anthropic error format.

        Anthropic error format:
        {
            "type": "error",
            "error": {"type": "...", "message": "..."}
        }
        """
        return (
            parsed.get("type") == "error"
            and isinstance(parsed.get("error"), dict)
            and "type" in parsed["error"]
            and "message" in parsed["error"]
        )

    @staticmethod
    def _extract_message_from_dict(parsed: dict, raw_message: str) -> str:
        """
        Extract error message from a parsed provider-specific dict.

        Handles:
        - Bedrock: {"detail": {"message": "..."}}
        - AWS: {"Message": "..."}
        - Generic: {"message": "..."}
        """
        # Bedrock format
        if "detail" in parsed and isinstance(parsed["detail"], dict):
            return parsed["detail"].get("message", raw_message)
        # AWS/generic format
        return parsed.get("Message") or parsed.get("message") or raw_message

    @staticmethod
    def transform_to_anthropic_error(
        status_code: int,
        raw_message: str,
        request_id: Optional[str] = None,
    ) -> AnthropicErrorResponse:
        """
        Transform an error message to Anthropic format.

        - If already in Anthropic format: passthrough unchanged
        - Otherwise: extract message and create Anthropic error

        Parses JSON only once for efficiency.

        Args:
            status_code: HTTP status code
            raw_message: Raw error message (may be JSON string or plain text)
            request_id: Optional request ID to include

        Returns:
            AnthropicErrorResponse dict
        """
        # Try to parse as JSON once
        parsed: Optional[dict] = safe_json_loads(raw_message)
        if not isinstance(parsed, dict):
            parsed = None

        # If parsed and already in Anthropic format - passthrough
        if parsed is not None and AnthropicExceptionMapping._is_anthropic_error_dict(
            parsed
        ):
            # Optionally add request_id if provided and not present
            if request_id and "request_id" not in parsed:
                parsed["request_id"] = request_id
            return parsed  # type: ignore

        # Extract message - use parsed dict if available, otherwise raw string
        if parsed is not None:
            message = AnthropicExceptionMapping._extract_message_from_dict(
                parsed, raw_message
            )
        else:
            message = raw_message

        return AnthropicExceptionMapping.create_error_response(
            status_code=status_code,
            message=message,
            request_id=request_id,
        )
