"""Pydantic-схемы для аутентификации и профиля пользователя."""

import uuid

from pydantic import BaseModel, ConfigDict, EmailStr, Field


class RegisterIn(BaseModel):
    """Тело запроса регистрации нового пользователя.

    `team_id` допустим только при включённой настройке инстанса
    `registration_team_choice` (см. `GET /auth/registration-options`) и
    существующей команде — иначе `POST /auth/register` вернёт 400.
    `consent_accepted` обязан быть `True`, если в настройках инстанса
    включено `consent_required` (согласие на обработку персональных
    данных) — иначе `POST /auth/register` вернёт 400. Игнорируется, если
    настройка выключена (второй эшелон проверки — фронт тоже блокирует
    кнопку, но сервер не полагается на это).
    """

    email: EmailStr
    name_user: str = Field(min_length=1, max_length=255)
    password: str = Field(min_length=8)
    team_id: uuid.UUID | None = None
    consent_accepted: bool = False


class VerifyEmailIn(BaseModel):
    """Тело запроса подтверждения email."""

    token: str = Field(min_length=1)


class TokenOut(BaseModel):
    """Ответ с access-токеном; refresh-токен передаётся отдельно в httpOnly cookie."""

    access_token: str
    token_type: str = "bearer"


class UserOut(BaseModel):
    """Публичное представление пользователя (профиль текущего пользователя).

    `email: str`, а не `EmailStr` — намеренно: это response-схема,
    отражающая уже сохранённые в БД данные, а не принимающая новый ввод.
    `EmailStr` дополнительно отсекает синтаксически валидные, но
    зарезервированные домены (`.local`, `.test` и т.п. из RFC 6761) — валидный
    email на входе (`RegisterIn`, ниже) мог быть заведён напрямую в БД (seed,
    ручная миграция) с таким доменом; строгая `EmailStr` на выходе привела бы
    к 500 `ResponseValidationError` для уже существующих пользователей.
    """

    model_config = ConfigDict(from_attributes=True)

    id: uuid.UUID
    email: str
    name_user: str
    role: str


class UserListItemOut(BaseModel):
    """Элемент списка пользователей (пикер участников конференции/мультиселект брони)."""

    id: uuid.UUID
    display_name: str
    avatar_url: str | None = None


class ProfileUpdateIn(BaseModel):
    """Тело правки профиля текущего пользователя.

    Email НЕ принимается — read-only поле профиля. `team_id` не различает
    «поле не передано» и «явный `null`» через сравнение с `None` — роутер
    читает `model_fields_set` (тот же приём, что `AdminUserUpdateIn.team_id`).
    """

    name_user: str | None = Field(default=None, min_length=1, max_length=255)
    team_id: uuid.UUID | None = None


class UserProfileOut(UserOut):
    """Профиль пользователя (свой либо открытый администратором) — `UserOut` + аватар/команда."""

    avatar_url: str | None = None
    team_id: uuid.UUID | None = None
    team_name: str | None = None


class UserBackgroundOut(BaseModel):
    """Своя картинка пользователя для замены фона видео (`GET /users/me/backgrounds`).

    Отдаётся только URL файла (`/media/backgrounds/...`, раздаёт nginx) и id для
    удаления — путь на диске наружу не показывается.
    """

    id: uuid.UUID
    url: str


class UserBackgroundsOut(BaseModel):
    """Список своих картинок фона вместе с лимитом.

    Лимит приезжает с сервера, а не зашит в интерфейс: он проверяется на
    сервере (`services/backgrounds.py`), и фронт не должен угадывать его
    отдельной константой, которая разъедется при первой же правке.
    """

    items: list[UserBackgroundOut]
    limit: int


class PasswordChangeIn(BaseModel):
    """Тело смены пароля текущим пользователем (`POST /users/me/password`).

    Политика сложности `new_password` — та же, что при регистрации
    (`RegisterIn.password`, min 8 символов).
    """

    current_password: str
    new_password: str = Field(min_length=8)


class RegistrationTeamOptionOut(BaseModel):
    """Команда в списке опций публичной карточки регистрации."""

    id: uuid.UUID
    name: str


class RegistrationOptionsOut(BaseModel):
    """Публичные опции формы регистрации (`GET /auth/registration-options`).

    `teams` отдаётся только при `team_choice_enabled=True` — иначе пустой
    список (справочник команд не раскрывается, пока выбор выключен).
    `email_domains` — эталонные домены при включённой верификации регистрации
    по домену email (настройка инстанса `registration_email_domain`), иначе
    пустой список.
    """

    team_choice_enabled: bool
    teams: list[RegistrationTeamOptionOut]
    email_domains: list[str] = Field(default_factory=list)
    # Согласие на обработку персональных данных: `consent_required` — обязательна
    # ли галочка на форме регистрации; `consent_text`/`consent_version` отдаются
    # ВСЕГДА, независимо от `consent_required` — той же строкой пользуется
    # публичная страница регламента, доступная и при выключенном модуле.
    consent_required: bool = False
    consent_text: str = ""
    consent_version: int = 1
