"""
Exceptions for user OAuth operations.

This module defines a hierarchy of exceptions for credential storage,
retrieval, and lifecycle operations.
"""


class UserOAuthError(Exception):
    """
    Base exception for user OAuth operations.

    All user OAuth related exceptions inherit from this class,
    making it easy to catch all credential-related errors.
    """

    pass


class UserCredentialNotFoundError(UserOAuthError):
    """
    Raised when a user hasn't connected to the requested system.

    This typically means the user needs to complete an OAuth flow
    to authorize access to the external system.

    Attributes:
        user_arn: The user who doesn't have a credential
        system_key: The system they tried to access
    """

    def __init__(self, user_arn: str, system_key: str):
        self.user_arn = user_arn
        self.system_key = system_key
        super().__init__(
            f"User {user_arn} has not connected to {system_key}. "
            "OAuth authorization required."
        )


class CredentialDecryptionError(UserOAuthError):
    """
    Raised when credential decryption fails.

    This may indicate:
    - Key mismatch (different encryption key than what was used to encrypt)
    - Corrupted ciphertext in database
    - Tampering detected by authentication tag validation
    """

    pass


class CredentialExpiredError(UserOAuthError):
    """
    Raised when a token is expired and cannot be refreshed.

    This occurs when:
    - Access token expired and no refresh token available
    - Refresh token also expired (for providers with refresh token expiry)
    """

    def __init__(self, user_arn: str, system_key: str, message: str = None):
        self.user_arn = user_arn
        self.system_key = system_key
        msg = message or f"Token for {user_arn} on {system_key} is expired and cannot be refreshed"
        super().__init__(msg)


class RefreshTokenRevokedError(UserOAuthError):
    """
    Raised when the refresh token has been revoked.

    This occurs when:
    - User revoked access in the external system
    - Admin revoked the application's access
    - Refresh token exceeded maximum lifetime

    The user must re-authorize to continue using the integration.
    """

    def __init__(self, user_arn: str, system_key: str, reason: str = None):
        self.user_arn = user_arn
        self.system_key = system_key
        self.reason = reason
        msg = f"Refresh token for {user_arn} on {system_key} has been revoked"
        if reason:
            msg += f": {reason}"
        super().__init__(msg)


class CredentialStorageError(UserOAuthError):
    """
    Raised when storing a credential fails.

    This may indicate:
    - Database connection issues
    - Constraint violations
    - Encryption failures
    """

    pass


class TokenRefreshError(UserOAuthError):
    """
    Raised when token refresh fails for a recoverable reason.

    Unlike RefreshTokenRevokedError, this may be temporary
    (e.g., network timeout, rate limiting) and retryable.
    """

    def __init__(self, user_arn: str, system_key: str, reason: str, retryable: bool = True):
        self.user_arn = user_arn
        self.system_key = system_key
        self.reason = reason
        self.retryable = retryable
        super().__init__(
            f"Token refresh failed for {user_arn} on {system_key}: {reason}"
        )
