# src/notebooklm_tools/core/errors.py
"""Exception classes for NotebookLM API artifacts and client errors.

This module contains exception classes used throughout the NotebookLM API client
for artifact operations (download, parse, status) and authentication errors.

For CLI-specific exceptions (with hint messages), see exceptions.py.
"""

from .exceptions import NLMError


class NotebookLMError(NLMError):
    """Base exception for NotebookLM errors.

    All artifact-related and client-level errors inherit from this class.
    """

    def __init__(self, message: str, hint: str | None = None):
        super().__init__(message=message, hint=hint)


class ArtifactError(NotebookLMError):
    """Base exception for artifact errors.

    Covers all errors related to studio artifacts (audio, video, reports, etc.)
    including generation, download, and parsing failures.
    """

    pass


class ArtifactNotReadyError(ArtifactError):
    """Raised when an artifact is not ready for download.

    This occurs when attempting to download an artifact that:
    - Is still being generated
    - Does not exist
    - Has failed generation
    """

    def __init__(self, artifact_type: str, artifact_id: str | None = None):
        msg = f"{artifact_type} is not ready or does not exist"
        if artifact_id:
            msg += f" (ID: {artifact_id})"
        super().__init__(msg)
        self.artifact_type = artifact_type
        self.artifact_id = artifact_id


class ArtifactParseError(ArtifactError):
    """Raised when artifact metadata cannot be parsed.

    This occurs when the API response structure has changed or
    contains unexpected data that cannot be processed.
    """

    def __init__(self, artifact_type: str, details: str = "", cause: Exception | None = None):
        msg = f"Failed to parse {artifact_type} metadata: {details}"
        super().__init__(msg)
        self.__cause__ = cause
        self.artifact_type = artifact_type
        self.details = details


class ArtifactDownloadError(ArtifactError):
    """Raised when artifact download fails.

    This occurs during HTTP download of binary artifacts when:
    - Server returns an error status
    - Network issues occur
    - Response is invalid
    """

    def __init__(self, artifact_type: str, details: str = ""):
        super().__init__(f"Failed to download {artifact_type}: {details}")
        self.artifact_type = artifact_type
        self.details = details


class ArtifactNotFoundError(ArtifactError):
    """Raised when a specific artifact ID is not found.

    This occurs when requesting a specific artifact by ID that
    doesn't exist in the notebook's studio artifacts.
    """

    def __init__(self, artifact_id: str, artifact_type: str = "artifact"):
        super().__init__(f"{artifact_type} not found: {artifact_id}")
        self.artifact_id = artifact_id
        self.artifact_type = artifact_type


class ClientAuthenticationError(Exception):
    """Raised when authentication fails (HTTP 401/403 or RPC Error 16).

    This is a client-level exception separate from NotebookLMError hierarchy.
    It indicates that the session/cookies have expired and re-authentication
    is required.

    Note: This class is aliased to `AuthenticationError` in client.py for
    backward compatibility, but that name also exists in exceptions.py with
    a different implementation (CLI-focused with hints).
    """

    pass


class RPCError(NotebookLMError):
    """Raised when a batchexecute RPC returns a structured error payload.

    Google's batchexecute protocol can return errors inside HTTP 200 responses.
    The error is encoded in item[5] of the response array, with structure:
        [error_code, null, [[detail_type_url, [sub_codes...]]]]

    Known error codes:
        3  — Transient/service error (e.g., DeepResearchErrorDetail)
        8  — Resource exhausted / rate limited (raised as ResourceExhaustedError)
        16 — Authentication expired (handled separately as ClientAuthenticationError)

    Attributes:
        error_code: The top-level error code from item[5][0]
        detail_type: The protobuf type URL from item[5][2] (e.g., "...DeepResearchErrorDetail")
        detail_data: Sub-error data from the detail payload (e.g., [4])
    """

    def __init__(self, message: str, error_code: int = 0, detail_type: str = "", detail_data=None):
        super().__init__(message)
        self.error_code = error_code
        self.detail_type = detail_type
        self.detail_data = detail_data


class ResourceExhaustedError(RPCError):
    """Raised when a batchexecute RPC returns error code 8 (RESOURCE_EXHAUSTED).

    This typically indicates rate limiting or capacity throttling from the
    NotebookLM backend (e.g., infographic generation during peak hours).

    Subclass of RPCError so existing ``except RPCError`` handlers still catch it.
    """

    def __init__(self, message: str, detail_type: str = "", detail_data=None):
        super().__init__(message, error_code=8, detail_type=detail_type, detail_data=detail_data)
