"""
Error classes for credoai.

Structured error handling with rich context and proper exception hierarchy.
"""

from typing import Any, Dict, Optional

import httpx
from pydantic import BaseModel


class CredoAIError(Exception):
    """Base exception class for all credoai errors."""

    def __init__(self, message: str, context: Optional[Dict[str, Any]] = None):
        super().__init__(message)
        self.message = message
        self.context = context or {}


class ApiError(CredoAIError):
    """
    Exception raised when the API returns an error status code.

    This includes structured error information from the API response.
    """

    def __init__(
        self,
        status_code: int,
        message: str,
        response: Optional[httpx.Response] = None,
        error_details: Optional[Dict[str, Any]] = None,
    ):
        super().__init__(message)
        self.status_code = status_code
        self.response = response
        self.error_details = error_details or {}
        self.request_id = None

        if response:
            self.request_id = response.headers.get("X-Request-ID")

            # Try to parse error details from response
            try:
                if response.content:
                    error_body = response.json()
                    if isinstance(error_body, dict):
                        self.error_details.update(error_body)
            except (ValueError, Exception):
                # Can't parse response as JSON, keep as is
                pass

    def __str__(self) -> str:
        base_msg = f"API Error {self.status_code}: {self.message}"
        if self.request_id:
            base_msg += f" (Request ID: {self.request_id})"
        return base_msg


class ValidationError(CredoAIError):
    """Exception raised when request/response validation fails."""

    def __init__(self, message: str, validation_errors: Optional[list] = None):
        super().__init__(message)
        self.validation_errors = validation_errors or []


class ClientValidationError(ValidationError):
    """Exception raised when client-side validation fails."""

    pass


class AuthenticationError(ApiError):
    """Exception raised when authentication fails (401/403 responses)."""

    def __init__(
        self,
        message: str = "Authentication failed",
        response: Optional[httpx.Response] = None,
    ):
        super().__init__(
            status_code=response.status_code if response else 401,
            message=message,
            response=response,
        )


class RateLimitError(ApiError):
    """Exception raised when rate limit is exceeded (429 response)."""

    def __init__(
        self,
        message: str = "Rate limit exceeded",
        response: Optional[httpx.Response] = None,
    ):
        super().__init__(status_code=429, message=message, response=response)

        # Extract rate limit headers
        self.retry_after = None
        self.limit = None
        self.remaining = None
        self.reset_time = None

        if response:
            headers = response.headers
            self.retry_after = headers.get("Retry-After")
            self.limit = headers.get("X-RateLimit-Limit")
            self.remaining = headers.get("X-RateLimit-Remaining")
            self.reset_time = headers.get("X-RateLimit-Reset")


class ServerError(ApiError):
    """Exception raised for server errors (5xx responses)."""

    def __init__(
        self, message: str = "Server error", response: Optional[httpx.Response] = None
    ):
        super().__init__(
            status_code=response.status_code if response else 500,
            message=message,
            response=response,
        )


class TimeoutError(CredoAIError):
    """Exception raised when a request times out."""

    def __init__(
        self, message: str = "Request timed out", timeout: Optional[float] = None
    ):
        super().__init__(message)
        self.timeout = timeout


class ConnectionError(CredoAIError):
    """Exception raised when connection to the API fails."""

    def __init__(
        self,
        message: str = "Connection failed",
        original_error: Optional[Exception] = None,
    ):
        super().__init__(message)
        self.original_error = original_error


# Error response models for structured API errors
class ValidationErrorDetail(BaseModel):
    """Detail for a validation error."""

    loc: list[str]
    msg: str
    type: str


class HTTPValidationErrorResponse(BaseModel):
    """HTTP validation error response from the API."""

    detail: list[ValidationErrorDetail]


class ErrorResponse(BaseModel):
    """Generic error response from the API."""

    error: str
    message: str
    details: Optional[Dict[str, Any]] = None
    request_id: Optional[str] = None


def create_error_from_response(response: httpx.Response) -> ApiError:
    """
    Create appropriate error instance from HTTP response.

    Args:
        response: The HTTP response that triggered the error

    Returns:
        Appropriate error instance based on status code
    """
    status_code = response.status_code

    # Try to extract error message from response
    message = f"HTTP {status_code}"
    try:
        if response.content:
            error_data = response.json()
            if isinstance(error_data, dict):
                message = error_data.get("message", error_data.get("error", message))
    except Exception:
        pass

    # Create specific error types based on status code
    if status_code in (401, 403):
        return AuthenticationError(message, response)
    elif status_code == 429:
        return RateLimitError(message, response)
    elif status_code >= 500:
        return ServerError(message, response)
    else:
        return ApiError(status_code, message, response)
