"""SDK exceptions for error handling.

This module provides exception types for both the API client and pipeline SDK.
"""

from typing import Any, cast


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

    def __init__(self, message: str, *, error_data: dict[str, Any] | None = None):
        super().__init__(message)
        self.error_data = error_data
        self.transcription_id = _extract_transcription_id(error_data)


class AuthenticationError(SDKError):
    """Raised when authentication fails (401)."""

    pass


class ForbiddenError(SDKError):
    """Raised when access is forbidden (403)."""

    pass


class NotFoundError(SDKError):
    """Raised when resource is not found (404)."""

    pass


class ValidationError(SDKError):
    """Raised when request validation fails (422)."""

    def __init__(
        self,
        message: str,
        errors: list[dict[str, Any]] | None = None,
        *,
        error_data: dict[str, Any] | None = None,
    ):
        super().__init__(message, error_data=error_data)
        self.errors = errors or []


class ConflictError(SDKError):
    """Raised when resource conflict occurs (409)."""

    pass


class RateLimitError(SDKError):
    """Raised when rate limited by the server (429)."""

    pass


class ServerError(SDKError):
    """Raised when server returns 5xx error."""

    pass


class RequestError(SDKError):
    """Raised when request fails (network, timeout, etc.)."""

    pass


class DocumentProcessingError(SDKError):
    """Raised when document processing fails.

    This exception is raised by wait_for_document_processing when the document
    enters the 'failed' processing status.

    Attributes:
        document_id: UUID of the document that failed
        processing_error: Error message from the processing pipeline
    """

    def __init__(self, document_id: str, processing_error: str | None):
        self.document_id = document_id
        self.processing_error = processing_error
        super().__init__(
            f"Document {document_id} processing failed: {processing_error or 'Unknown error'}"
        )


class DocumentProcessingTimeout(SDKError):
    """Raised when document processing times out.

    This exception is raised by wait_for_document_processing when the document
    doesn't reach a terminal state within the specified timeout.

    Attributes:
        document_id: UUID of the document
        last_status: Last observed processing status
        timeout: Timeout value in seconds
    """

    def __init__(self, document_id: str, last_status: str, timeout: float):
        self.document_id = document_id
        self.last_status = last_status
        self.timeout = timeout
        super().__init__(
            f"Document {document_id} processing timed out after {timeout}s. "
            f"Last status: {last_status}"
        )


class CollectionClusteringError(SDKError):
    def __init__(self, collection_id: str, error_message: str | None):
        self.collection_id = collection_id
        self.error_message = error_message
        super().__init__(
            f"Collection {collection_id} clustering failed: {error_message or 'Unknown error'}"
        )


class CollectionClusteringTimeout(SDKError):
    def __init__(self, collection_id: str, last_status: str, timeout: float):
        self.collection_id = collection_id
        self.last_status = last_status
        self.timeout = timeout
        super().__init__(
            f"Collection {collection_id} clustering timed out after {timeout}s. "
            f"Last status: {last_status}"
        )


class GenerationFailedError(SDKError):
    """Raised when async generation fails.

    This exception is raised by wait_for_generation when the generation
    reaches the 'failed' status.

    Attributes:
        execution_id: UUID of the execution that failed
        error_message: Error message from the generation pipeline
        error_type: Type/category of the error
    """

    def __init__(
        self, execution_id: str, error_message: str | None, error_type: str | None
    ):
        self.execution_id = execution_id
        self.error_message = error_message
        self.error_type = error_type
        super().__init__(
            f"Generation {execution_id} failed: {error_message or 'Unknown error'}"
        )


class GenerationTimeoutError(SDKError):
    """Raised when async generation polling times out.

    This exception is raised by wait_for_generation when the generation
    doesn't reach a terminal state within the specified timeout.

    Attributes:
        execution_id: UUID of the execution
        last_status: Last observed generation status
        timeout: Timeout value in seconds
    """

    def __init__(self, execution_id: str, last_status: str, timeout: float):
        self.execution_id = execution_id
        self.last_status = last_status
        self.timeout = timeout
        super().__init__(
            f"Generation {execution_id} timed out after {timeout}s. "
            f"Last status: {last_status}"
        )


def _extract_transcription_id(error_data: dict[str, Any] | None) -> str | None:
    if not isinstance(error_data, dict):
        return None

    detail = error_data.get("detail")
    if not isinstance(detail, dict):
        return None

    detail_dict = cast(dict[str, Any], detail)
    transcription_id = detail_dict.get("transcription_id")
    if isinstance(transcription_id, str):
        return transcription_id

    return None
