"""Data-layer exceptions.

These exceptions are the stable public error surface for data and I/O traits.
Deprecated compatibility aliases are preserved through module attribute access.
"""

from __future__ import annotations

import warnings


class TraitError(Exception):
    """Base exception for the research-facing trait surface."""

    code = "trait_error"
    default_message = "Trait error."

    def __init__(self, message: str | None = None) -> None:
        super().__init__(message or self.default_message)


class ConnectionFailureError(TraitError):
    code = "connection_failure"
    default_message = "Connection failure."


class SubscriptionFailureError(TraitError):
    code = "subscription_failure"
    default_message = "Subscription failure."


class NotFoundError(TraitError):
    code = "not_found"
    default_message = "Requested resource was not found."


class AlreadyExistsError(TraitError):
    code = "already_exists"
    default_message = "Resource already exists."


class SourceUnavailableError(TraitError):
    code = "source_unavailable"
    default_message = "Source is unavailable."


class TraitTimeoutError(TraitError):
    code = "timeout"
    default_message = "Operation timed out."


class ParseError(TraitError):
    code = "parse"
    default_message = "Failed to parse or deserialize value."


class SerializationError(TraitError):
    code = "serialization"
    default_message = "Failed to serialize value."


class TraitIOError(TraitError):
    code = "io"
    default_message = "I/O failure."


class DatabaseError(TraitError):
    code = "database"
    default_message = "Database failure."


class InvalidInputError(TraitError):
    code = "invalid_input"
    default_message = "Invalid input."


class AuthenticationError(TraitError):
    code = "authentication"
    default_message = "Authentication failed."


class PermissionDeniedError(TraitError):
    code = "permission"
    default_message = "Permission denied."


class RateLimitError(TraitError):
    code = "rate_limit"
    default_message = "Rate limit exceeded."


class InternalError(TraitError):
    code = "internal_error"
    default_message = "Internal trait-layer error."


_DEPRECATED_ERROR_ALIASES: dict[str, type[TraitError]] = {
    "TimeoutError": TraitTimeoutError,
    "IOError": TraitIOError,
    "PermissionError": PermissionDeniedError,
}


def _deprecated_error_alias(name: str, *, module_name: str) -> type[TraitError]:
    """Resolve a deprecated compatibility alias and emit a warning."""
    alias = _DEPRECATED_ERROR_ALIASES.get(name)
    if alias is None:
        raise AttributeError(f"module {module_name!r} has no attribute {name!r}")
    warnings.warn(
        f"`{module_name}.{name}` is deprecated; use `{alias.__name__}`.",
        DeprecationWarning,
        stacklevel=2,
    )
    return alias


def __getattr__(name: str) -> object:
    """Expose deprecated aliases lazily for backwards compatibility."""
    return _deprecated_error_alias(name, module_name=__name__)


__all__ = [
    "AlreadyExistsError",
    "AuthenticationError",
    "ConnectionFailureError",
    "DatabaseError",
    "InternalError",
    "InvalidInputError",
    "NotFoundError",
    "ParseError",
    "PermissionDeniedError",
    "RateLimitError",
    "SerializationError",
    "SourceUnavailableError",
    "SubscriptionFailureError",
    "TraitIOError",
    "TraitTimeoutError",
    "TraitError",
]
