# -*- coding: utf-8 -*-
"""
Иерархия исключений API. Базовый ApiError и группы: Auth (только авторизация),
Register (только регистрация), Validation (валидация ответа). Все с текстами по умолчанию на русском.
Доступ: Exceptions — общий объект со всеми; Exceptions.Auth — только авторизация; Exceptions.Register — только регистрация.
"""
from typing import Dict, Any


# -----------------------------------------------------------------------------
# Базовое исключение
# -----------------------------------------------------------------------------

class ApiError(Exception):
    """Базовое исключение для любых ошибок API."""

    default_message: str = "Произошла ошибка при обращении к API"

    def __init__(self, message: str | None = None, **kwargs: Any):
        self.message = message if message is not None else self.default_message
        self.details = kwargs
        super().__init__(self.message)

    def __str__(self) -> str:
        return self.message


# -----------------------------------------------------------------------------
# Авторизация (Auth) — только ошибки, связанные с авторизацией
# -----------------------------------------------------------------------------

class AuthError(ApiError):
    """Ошибка авторизации."""

    default_message = "Произошла ошибка авторизации"


class ErrorCaptcha(AuthError):
    """Требуется прохождение капчи при авторизации."""

    default_message = "Требуется авторизация с капчей"


class ErrorCreds(AuthError):
    """Неверные учётные данные."""

    default_message = "Неверный логин или пароль"


class TokenExpired(AuthError):
    """Срок действия токена истёк."""

    default_message = "Срок действия токена истёк"


class Invalid2FACode(AuthError):
    """Неверный код двухфакторной аутентификации."""

    default_message = "Неверный код двухфакторной аутентификации"


class AccountBanned(AuthError):
    """Аккаунт заблокирован."""

    default_message = "Ваш аккаунт заблокирован"


class SuspiciousActivity(AuthError):
    """Обнаружена подозрительная активность."""

    default_message = "Обнаружена подозрительная активность. Обратитесь в поддержку"


class RateLimit(AuthError):
    """Слишком много попыток входа."""

    default_message = "Слишком много попыток входа. Попробуйте позже"


class SessionExpired(AuthError):
    """Сессия истекла."""

    default_message = "Сессия истекла. Войдите снова"


class AccountLocked(AuthError):
    """Аккаунт временно заблокирован после неудачных попыток."""

    default_message = "Аккаунт временно заблокирован. Попробуйте позже"


class AccountDisabled(AuthError):
    """Аккаунт отключён администратором."""

    default_message = "Ваш аккаунт отключён"


class MustChangePassword(AuthError):
    """Требуется смена пароля при первом входе."""

    default_message = "Необходимо сменить пароль"


# -----------------------------------------------------------------------------
# Регистрация (Register) — только ошибки, связанные с регистрацией
# -----------------------------------------------------------------------------

class RegisterError(ApiError):
    """Ошибка регистрации."""

    default_message = "Произошла ошибка при регистрации"


class UserExists(RegisterError):
    """Пользователь с такими данными уже существует."""

    default_message = "Пользователь с указанными данными уже существует"


class EmailExists(RegisterError):
    """Указанный адрес почты уже зарегистрирован."""

    default_message = "Указанный адрес электронной почты уже зарегистрирован"


class LoginRequirements(RegisterError):
    """Логин не соответствует требованиям."""

    default_message = "Логин не соответствует требованиям"


class PasswordRequirements(RegisterError):
    """Пароль не соответствует требованиям."""

    default_message = "Пароль не соответствует требованиям"


class ConfirmEmailRequired(RegisterError):
    """Требуется подтверждение почты."""

    default_message = "Необходимо подтвердить адрес электронной почты"


class InviteCodeInvalid(RegisterError):
    """Неверный или истёкший пригласительный код."""

    default_message = "Неверный или истёкший пригласительный код"


class UsernameTaken(RegisterError):
    """Такое имя пользователя уже занято."""

    default_message = "Такое имя пользователя уже занято"


class PhoneExists(RegisterError):
    """Указанный номер телефона уже зарегистрирован."""

    default_message = "Указанный номер телефона уже зарегистрирован"


class TermsNotAccepted(RegisterError):
    """Не приняты условия использования."""

    default_message = "Необходимо принять условия использования"


class RegisterRateLimit(RegisterError):
    """Слишком много попыток регистрации."""

    default_message = "Слишком много попыток. Попробуйте позже"


# -----------------------------------------------------------------------------
# Ресурс не найден
# -----------------------------------------------------------------------------

class NotFoundError(ApiError):
    """Ресурс или сущность не найдены."""

    default_message = "Запрашиваемый ресурс не найден"


# -----------------------------------------------------------------------------
# Валидация ответа (статус, заголовки, редиректы)
# -----------------------------------------------------------------------------

class ResponseValidationError(ApiError):
    """Нарушение правил валидации ответа (статус, заголовки, редирект)."""

    default_message = "Ответ не прошёл валидацию"

    def __init__(
        self,
        message: str | None = None,
        rule_type: str = "",
        details: Dict[str, Any] | None = None,
        **kwargs: Any,
    ):
        super().__init__(message=message, **kwargs)
        self.rule_type = rule_type
        self.details = details or {}


class StatusCodeError(ResponseValidationError):
    """Недопустимый статус-код ответа."""

    default_message = "Получен недопустимый статус-код ответа"


class RedirectError(ResponseValidationError):
    """Ошибка валидации редиректов."""

    default_message = "Ошибка при проверке редиректов (история или финальный URL)"


class HeaderError(ResponseValidationError):
    """Ошибка валидации заголовков ответа."""

    default_message = "Ошибка при проверке заголовков ответа"


# -----------------------------------------------------------------------------
# Именованные пространства: только свои исключения в каждом объекте
# -----------------------------------------------------------------------------

class _AuthNamespace:
    """
    Только ошибки авторизации. Exceptions.Auth.*
    """

    AuthError = AuthError
    ErrorCaptcha = ErrorCaptcha
    ErrorCreds = ErrorCreds
    TokenExpired = TokenExpired
    Invalid2FA = Invalid2FACode
    AccountBanned = AccountBanned
    SuspiciousActivity = SuspiciousActivity
    RateLimit = RateLimit
    SessionExpired = SessionExpired
    AccountLocked = AccountLocked
    AccountDisabled = AccountDisabled
    MustChangePassword = MustChangePassword


class _RegisterNamespace:
    """
    Только ошибки регистрации. Exceptions.Register.*
    """

    RegisterError = RegisterError
    UserExists = UserExists
    EmailExists = EmailExists
    LoginRequirements = LoginRequirements
    PasswordRequirements = PasswordRequirements
    ConfirmEmailRequired = ConfirmEmailRequired
    InviteCodeInvalid = InviteCodeInvalid
    UsernameTaken = UsernameTaken
    PhoneExists = PhoneExists
    TermsNotAccepted = TermsNotAccepted
    RateLimit = RateLimit


class _ValidationNamespace:
    """
    Только ошибки валидации ответа (статус, заголовки, редиректы). Exceptions.Validation.*
    """

    Error = ResponseValidationError
    StatusCode = StatusCodeError
    Redirect = RedirectError
    Header = HeaderError


# -----------------------------------------------------------------------------
# Общий объект: доступ ко всем исключениям (и по группам, и по имени)
# -----------------------------------------------------------------------------

class Exceptions:
    """
    Общий объект исключений. Отсюда доступны все группы и все классы.

    Группы (только свои исключения в каждой):
        Exceptions.Register — только регистрация (UserExists, EmailExists, LoginRequirements, ...)
        Exceptions.Auth     — только авторизация (Captcha, Creds, TokenExpired, Invalid2FA, AccountBanned, ...)
        Exceptions.Validation — только валидация ответа (StatusCode, Redirect, Header)

    Примеры:
        raise Exceptions.Auth.Captcha()
        raise Exceptions.Auth.AccountBanned("Забанен до 2025-01-01")
        raise Exceptions.Register.EmailExists()
        raise Exceptions.Register.PasswordRequirements("Минимум 8 символов")
        raise Exceptions.Validation.StatusCode("Получен 500")
        raise Exceptions.NotFound()
    """

    ApiError = ApiError

    # Группы: в каждой только свои исключения
    Register = _RegisterNamespace()
    Auth = _AuthNamespace()

    NotFound = NotFoundError

    # Все классы с верхнего уровня для прямого доступа (алиасы)
    AuthError = AuthError
    RegisterError = RegisterError

    Validation = _ValidationNamespace()
    ResponseValidationError = ResponseValidationError




