"""Typed exception hierarchy for EB1-GNN gateway communication.

Provides structured errors that carry HTTP status codes and request IDs,
enabling precise error dispatch in server handlers. The hierarchy is:

    GatewayError (base, any non-200 from gateway)
    ├── GatewayTimeoutError (504, request timed out)
    ├── GatewayConnectionError (502, TCP connect failed)
    └── GatewayRateLimitError (429, with optional Retry-After)

    RequestValidationError (400, client sent invalid payload)

Design: All exceptions extend built-in Exception so existing broad
``except Exception`` handlers continue to work during incremental
migration.
"""

from __future__ import annotations


class GatewayError(Exception):
    """Base for all gateway communication errors.

    Attributes:
        status: HTTP status code describing the failure.
        detail: Human-readable error description.
        request_id: Correlation ID for tracing, when available.
    """

    def __init__(
        self,
        status: int,
        detail: str,
        request_id: str | None = None,
    ) -> None:
        self.status = status
        self.detail = detail
        self.request_id = request_id
        super().__init__(f"Gateway {status}: {detail}")


class GatewayTimeoutError(GatewayError):
    """Gateway request timed out (maps to 504)."""

    def __init__(
        self,
        detail: str = "request timed out",
        request_id: str | None = None,
    ) -> None:
        super().__init__(status=504, detail=detail, request_id=request_id)


class GatewayConnectionError(GatewayError):
    """Could not establish TCP connection to gateway (maps to 502)."""

    def __init__(
        self,
        detail: str,
        request_id: str | None = None,
    ) -> None:
        super().__init__(status=502, detail=detail, request_id=request_id)


class GatewayRateLimitError(GatewayError):
    """Gateway returned 429 Too Many Requests.

    Attributes:
        retry_after: Seconds to wait before retrying, when provided
            by the upstream gateway.
    """

    def __init__(
        self,
        detail: str,
        retry_after: float | None = None,
        request_id: str | None = None,
    ) -> None:
        self.retry_after = retry_after
        super().__init__(status=429, detail=detail, request_id=request_id)


class RequestValidationError(Exception):
    """Client sent a malformed or invalid request (maps to 400).

    Attributes:
        detail: Human-readable description of the validation failure.
        param: Name of the invalid parameter, when applicable.
    """

    def __init__(
        self,
        detail: str,
        param: str | None = None,
    ) -> None:
        self.detail = detail
        self.param = param
        super().__init__(detail)
