"""Exceptions for screenshot client.

Provides a hierarchy of domain-specific exceptions for better error handling
and categorization.

Example:
    >>> from screenshot_client.exceptions import ScreenshotAPIError
    >>> try:
    ...     # API call that fails
    ...     pass
    ... except ScreenshotAPIError as e:
    ...     print(f"API error: {e.status_code}")
"""

from __future__ import annotations

from typing import Any


class ScreenshotClientError(Exception):
    """Base exception for all screenshot client errors.

    All exceptions raised by the screenshot client inherit from this base class,
    allowing catch-all error handling when needed.
    """

    pass


class ScreenshotConfigError(ScreenshotClientError):
    """Invalid client configuration.

    Raised when client initialization fails due to invalid configuration,
    such as missing environment variables or invalid URLs.

    Example:
        >>> from screenshot_client import ScreenshotServiceClient
        >>> try:
        ...     client = ScreenshotServiceClient.from_env()
        ... except ScreenshotConfigError as e:
        ...     print(f"Configuration error: {e}")
    """

    pass


class ScreenshotAPIError(ScreenshotClientError):
    """API returned non-success status.

    Raised when the screenshot service API returns an error response.
    Includes the HTTP status code and response content for debugging.

    Attributes:
        status_code: HTTP status code from the API response.
        content: Raw response content (bytes or string).
        message: Human-readable error message.

    Example:
        >>> try:
        ...     client.start_batch("invalid", {})
        ... except ScreenshotAPIError as e:
        ...     print(f"API error {e.status_code}: {e.message}")
    """

    def __init__(self, status_code: int, content: bytes | str, message: str | None = None):
        self.status_code = status_code
        self.content = content
        self.message = message or f"API error {status_code}"
        super().__init__(self.message)

    def __repr__(self) -> str:
        return f"ScreenshotAPIError(status_code={self.status_code}, message={self.message!r})"


class ScreenshotTimeoutError(ScreenshotClientError):
    """Operation timed out.

    Raised when an operation (such as polling) exceeds the specified timeout
    without reaching a terminal state.

    Attributes:
        timeout: The timeout value that was exceeded (in seconds).
        last_response: The last response received before timeout (if any).

    Example:
        >>> try:
        ...     client.poll_run("batch", "job", timeout=10.0)
        ... except ScreenshotTimeoutError as e:
        ...     print(f"Timed out after {e.timeout}s")
    """

    def __init__(self, timeout: float, last_response: dict[str, Any] | None = None):
        self.timeout = timeout
        self.last_response = last_response
        super().__init__(f"Operation timed out after {timeout}s")


class ScreenshotBatchError(ScreenshotClientError):
    """Batch processing failed.

    Raised when a screenshot batch fails during processing.

    Attributes:
        batch_id: The batch identifier.
        job_id: The job identifier.
        reason: Human-readable failure reason.
        details: Additional error details (if available).

    Example:
        >>> try:
        ...     result = client.poll_run("batch", "job")
        ... except ScreenshotBatchError as e:
        ...     print(f"Batch {e.batch_id} failed: {e.reason}")
    """

    def __init__(
        self,
        batch_id: str,
        job_id: str,
        reason: str,
        details: dict[str, Any] | None = None,
    ):
        self.batch_id = batch_id
        self.job_id = job_id
        self.reason = reason
        self.details = details or {}
        super().__init__(f"Batch {batch_id}/{job_id} failed: {reason}")

    def __repr__(self) -> str:
        return f"ScreenshotBatchError(batch_id={self.batch_id!r}, job_id={self.job_id!r}, reason={self.reason!r})"
