from __future__ import annotations

"""Nexure SDK exception hierarchy.

NexureError
├── AuthenticationError       (401)
├── ValidationError           (422)
├── RateLimitError            (429)
├── NotFoundError             (404)
├── ServerError               (500)
├── NexureConnectionError     (transport)
└── NexureTimeoutError        (transport)
"""

import contextlib
from typing import Any


class NexureError(Exception):
    """Base exception for all Nexure SDK errors."""

    def __init__(
        self, message: str, status_code: int | None = None, body: dict[str, Any] | None = None
    ):
        self.message = message
        self.status_code = status_code
        self.body = body or {}
        super().__init__(message)


class AuthenticationError(NexureError):
    """401 — Invalid or missing API key."""

    pass


class ValidationError(NexureError):
    """422 — Request validation failed."""

    pass


class RateLimitError(NexureError):
    """429 — Rate limit exceeded."""

    def __init__(self, message: str, retry_after: float | None = None, **kwargs: Any):
        super().__init__(message, status_code=429, **kwargs)
        self.retry_after = retry_after


class NotFoundError(NexureError):
    """404 — Resource not found."""

    pass


class ServerError(NexureError):
    """500 — Internal server error."""

    pass


class NexureConnectionError(NexureError):
    """Could not connect to the Nexure API."""

    pass


class NexureTimeoutError(NexureError):
    """Request to the Nexure API timed out."""

    pass


def _extract_message(body: dict[str, Any]) -> str:
    """Extract a human-readable error message from an API response body.

    Handles FastAPI's nested format: {"detail": {"error": {"message": "..."}}}
    as well as the simpler {"error": {"message": "..."}} format.
    """
    # Try top-level {"error": {"message": "..."}}
    error_obj = body.get("error")
    if isinstance(error_obj, dict):
        msg = error_obj.get("message")
        if isinstance(msg, str):
            return msg

    # Try FastAPI nested {"detail": {"error": {"message": "..."}}}
    detail = body.get("detail")
    if isinstance(detail, dict):
        inner_error = detail.get("error")
        if isinstance(inner_error, dict):
            msg = inner_error.get("message")
            if isinstance(msg, str):
                return msg
        # {"detail": {"message": "..."}}
        msg = detail.get("message")
        if isinstance(msg, str):
            return msg
        # Fall back to stringifying the detail dict
        return str(detail)

    # FastAPI validation errors: {"detail": [{"msg": "...", ...}, ...]}
    if isinstance(detail, list):
        return str(detail)

    # {"detail": "some string"}
    if isinstance(detail, str):
        return detail

    return str(body)


def _handle_response(
    status_code: int, body: dict[str, Any], headers: dict[str, str] | None = None
) -> None:
    """Raise the appropriate exception for non-2xx responses."""
    if 200 <= status_code < 300:
        return

    message = _extract_message(body)

    if status_code == 401:
        raise AuthenticationError(message, status_code=401, body=body)
    elif status_code == 404:
        raise NotFoundError(message, status_code=404, body=body)
    elif status_code == 422:
        raise ValidationError(message, status_code=422, body=body)
    elif status_code == 429:
        retry_after = None
        if headers:
            ra = headers.get("retry-after") or headers.get("x-ratelimit-reset")
            if ra:
                with contextlib.suppress(ValueError):
                    retry_after = float(ra)
        raise RateLimitError(message, retry_after=retry_after, body=body)
    elif status_code >= 500:
        raise ServerError(message, status_code=status_code, body=body)
    else:
        raise NexureError(message, status_code=status_code, body=body)
