"""Error classification utilities for the ORO SDK.

Provides helpers to classify HTTP responses, check transient errors,
and extract structured error details from API responses.

This is a custom (non-generated) module that survives SDK regeneration
because generate-python.sh only deletes known generated directories
(api/, models/) and files (client.py, errors.py, types.py, py.typed).
"""

from __future__ import annotations

from enum import Enum
from typing import Any, Union

from ._generated_errors import ALL_ERROR_TYPES, OroErrorCode
from .types import UNSET, Response, Unset


class ErrorCategory(str, Enum):
    """High-level error category derived from HTTP status codes."""

    NETWORK = "NETWORK"
    AUTH = "AUTH"
    NOT_FOUND = "NOT_FOUND"
    VALIDATION = "VALIDATION"
    CONFLICT = "CONFLICT"
    RATE_LIMITED = "RATE_LIMITED"
    SERVER = "SERVER"
    UNKNOWN = "UNKNOWN"


def classify_status(status_code: int) -> ErrorCategory:
    """Classify an HTTP status code into an ErrorCategory."""
    if status_code == 0:
        return ErrorCategory.NETWORK
    if status_code in (401, 403):
        return ErrorCategory.AUTH
    if status_code == 404:
        return ErrorCategory.NOT_FOUND
    if status_code == 409:
        return ErrorCategory.CONFLICT
    if status_code == 422:
        return ErrorCategory.VALIDATION
    if status_code == 429:
        return ErrorCategory.RATE_LIMITED
    if 500 <= status_code < 600:
        return ErrorCategory.SERVER
    if 400 <= status_code < 500:
        return ErrorCategory.VALIDATION
    return ErrorCategory.UNKNOWN


def classify_error(response: Response[Any]) -> ErrorCategory:
    """Classify a Response object into an ErrorCategory."""
    return classify_status(response.status_code.value)


def is_transient_status(status_code: int) -> bool:
    """Return True if the status code represents a transient (retryable) error.

    Transient statuses: 429 (rate limited), 5xx (server errors), 0 (network failures).
    """
    if status_code == 0:
        return True
    if status_code == 429:
        return True
    if 500 <= status_code < 600:
        return True
    return False


def is_transient(response: Response[Any]) -> bool:
    """Return True if the response represents a transient (retryable) error."""
    return is_transient_status(response.status_code.value)


def get_error_detail(parsed: Any) -> Union[str, None]:
    """Extract the detail string from a parsed error model.

    Returns None if detail is missing, not a string (e.g. Pydantic validation arrays),
    or the parsed value is None.
    """
    if parsed is None:
        return None
    detail = getattr(parsed, "detail", None)
    if isinstance(detail, Unset):
        return None
    if isinstance(detail, str):
        return detail
    return None


def get_error_code(parsed: Any) -> Union[str, None]:
    """Extract the error_code from a parsed error model.

    Returns None if the field is missing, is Unset, or the parsed value is None.
    """
    if parsed is None:
        return None
    code = getattr(parsed, "error_code", None)
    if isinstance(code, Unset):
        return None
    if isinstance(code, str):
        return code
    return None


def is_error(parsed: Any) -> bool:
    """Return True if the parsed object is one of the known SDK error types."""
    return isinstance(parsed, ALL_ERROR_TYPES)


__all__ = [
    "OroErrorCode",
    "ALL_ERROR_TYPES",
    "ErrorCategory",
    "classify_status",
    "classify_error",
    "is_transient_status",
    "is_transient",
    "get_error_detail",
    "get_error_code",
    "is_error",
]
