"""A2A error hierarchy and domain-to-A2A error mapping.

All A2A-specific exceptions inherit from :class:`A2aError` which itself
extends the project-wide :class:`CleverAgentsError`.  This mirrors the
pattern established by the LSP package.

The :func:`map_domain_error` function translates domain exceptions into
structured :class:`A2aErrorDetail` instances using a well-defined error
code taxonomy so that clients receive consistent, machine-readable error
responses regardless of which internal service produced the fault.
"""

from __future__ import annotations

from typing import Any

from cleveragents.core.exceptions import (
    AuthenticationError,
    AuthorizationError,
    BusinessRuleViolation,
    CleverAgentsError,
    ConfigurationError,
    PlanError,
    ResourceNotFoundError,
    ValidationError,
)

# ---------------------------------------------------------------------------
# A2A error code constants — JSON-RPC 2.0 integer codes
#
# Standard JSON-RPC 2.0 codes (Section 5.1):
#   -32700  Parse error
#   -32600  Invalid request
#   -32601  Method not found
#   -32602  Invalid params
#   -32603  Internal error
#
# Application-defined codes (per docs/reference/a2a.md §Error Code Taxonomy):
#   -32001  Resource not found
#   -32002  Authentication required
#   -32003  Authorization forbidden
#   -32004  Invalid state / business rule violation
#   -32005  Duplicate entity
#   -32006  Budget exceeded
#   -32007  Version mismatch
#   -32008  Plan error
#   -32009  Configuration error
# ---------------------------------------------------------------------------

NOT_FOUND: int = -32001
AUTH_ERROR: int = -32002
FORBIDDEN: int = -32003
INVALID_STATE: int = -32004
PLAN_ERROR: int = -32008
CONFIGURATION_ERROR: int = -32009
VALIDATION_ERROR: int = -32602
INTERNAL_ERROR: int = -32603


# ---------------------------------------------------------------------------
# Exception classes
# ---------------------------------------------------------------------------


class A2aError(CleverAgentsError):
    """Base exception for all A2A errors."""

    def __init__(
        self,
        message: str,
        details: dict[str, Any] | None = None,
    ) -> None:
        super().__init__(message, details)


class A2aNotAvailableError(A2aError):
    """Raised when a server-mode operation is attempted in local mode."""

    def __init__(
        self,
        message: str = "A2A server transport is not available in local mode",
        details: dict[str, Any] | None = None,
    ) -> None:
        super().__init__(message, details)


class A2aVersionMismatchError(A2aError):
    """Raised when client and server A2A versions are incompatible."""

    def __init__(
        self,
        message: str,
        requested_version: str,
        supported_versions: list[str],
        details: dict[str, Any] | None = None,
    ) -> None:
        super().__init__(message, details)
        self.requested_version = requested_version
        self.supported_versions = supported_versions


class A2aOperationNotFoundError(A2aError):
    """Raised when an unknown A2A operation is requested."""

    def __init__(
        self,
        message: str,
        operation: str,
        details: dict[str, Any] | None = None,
    ) -> None:
        super().__init__(message, details)
        self.operation = operation


# ---------------------------------------------------------------------------
# Domain-to-A2A error mapping
# ---------------------------------------------------------------------------


def map_domain_error(exc: Exception) -> tuple[int, str]:
    """Map a domain exception to an A2A error code and message.

    Returns a ``(code, message)`` tuple suitable for constructing an
    :class:`~cleveragents.a2a.models.A2aErrorDetail`.

    The mapping precedence is intentional: more specific exception types
    are checked before their base classes so that, for example, a
    ``PlanError`` is classified as ``PLAN_ERROR`` rather than the
    generic ``INVALID_STATE`` that would match its ``DomainError``
    ancestor.

    Per JSON-RPC 2.0 specification (Section 5.1), error codes are integers.
    The returned ``code`` is always a negative integer matching the
    JSON-RPC 2.0 standard taxonomy defined in ``docs/reference/a2a.md``.

    Args:
        exc: The exception raised by a domain or application service.

    Returns:
        A two-element tuple of ``(error_code, error_message)`` where
        ``error_code`` is a JSON-RPC 2.0 compliant integer error code.
    """
    if not isinstance(exc, Exception):
        raise TypeError("exc must be an Exception instance")

    if isinstance(exc, ResourceNotFoundError):
        return NOT_FOUND, str(exc)
    if isinstance(exc, ValidationError):
        return VALIDATION_ERROR, str(exc)
    if isinstance(exc, PlanError):
        return PLAN_ERROR, str(exc)
    if isinstance(exc, BusinessRuleViolation):
        return INVALID_STATE, str(exc)
    if isinstance(exc, AuthenticationError):
        return AUTH_ERROR, str(exc)
    if isinstance(exc, AuthorizationError):
        return FORBIDDEN, str(exc)
    if isinstance(exc, ConfigurationError):
        return CONFIGURATION_ERROR, str(exc)
    if isinstance(exc, CleverAgentsError):
        return INTERNAL_ERROR, str(exc)
    return INTERNAL_ERROR, str(exc)


__all__ = [
    "AUTH_ERROR",
    "CONFIGURATION_ERROR",
    "FORBIDDEN",
    "INTERNAL_ERROR",
    "INVALID_STATE",
    "NOT_FOUND",
    "PLAN_ERROR",
    "VALIDATION_ERROR",
    "A2aError",
    "A2aNotAvailableError",
    "A2aOperationNotFoundError",
    "A2aVersionMismatchError",
    "map_domain_error",
]
