"""Pydantic-схемы админ-API (`/api/v1/admin/*`)."""

import uuid
from datetime import datetime
from typing import Literal

from pydantic import BaseModel, ConfigDict, EmailStr, Field

from core.plugins.config import AiLevel, PublishQualityCap, StageMaxTiles, SummaryRecipientsMode
from schemas.conferences import ConferenceOut
from services.ai_levels import AiLevelStatus


class AdminConferenceOut(ConferenceOut):
    """Конференция в ответах админ-API — `ConferenceOut` + данные владельца.

    `owner_name`/`owner_email` — `None` для конференции без владельца
    (`conferences.owner_id IS NULL`, ADR-001) — колонка «Владелец» в таблице
    админки (`frontend/src/api/admin.ts`).
    """

    owner_name: str | None = None
    owner_email: str | None = None


class AdminConferenceListOut(BaseModel):
    """Страница списка конференций (`GET /admin/conferences`)."""

    items: list[AdminConferenceOut]
    total: int


class AdminUserOut(BaseModel):
    """Пользователь в ответах админ-API (профиль + служебные поля модерации).

    `avatar_url`/`team_name` заполняются явно роутером (не через
    `from_attributes` — оба поля вычисляемые, не хранятся как атрибут ORM),
    см. `api/admin.py::_to_admin_user_out`.
    """

    model_config = ConfigDict(from_attributes=True)

    id: uuid.UUID
    email: str
    name_user: str
    role: str
    is_blocked: bool
    email_verified: bool
    created_at: datetime
    team_id: uuid.UUID | None = None
    avatar_url: str | None = None
    team_name: str | None = None


class AdminUserListOut(BaseModel):
    """Страница списка пользователей (`GET /admin/users`)."""

    items: list[AdminUserOut]
    total: int


class AdminUserCreateIn(BaseModel):
    """Тело создания пользователя администратором (`POST /admin/users`).

    Та же политика пароля, что при самостоятельной регистрации
    (`RegisterIn.password`, min 8 символов). `team_id` — опциональная
    привязка к команде, в отличие от публичной регистрации не завязана на
    настройку `registration_team_choice` (админ назначает команду всегда).
    """

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


class AdminUserUpdateIn(BaseModel):
    """Тело правки пользователя администратором — роль, блокировка, ФИО и/или команда.

    `team_id` не различает «поле не передано» и «явный `null`» через
    сравнение с `None` — роутер читает `model_fields_set`, чтобы явный сброс
    команды (`{"team_id": null}`) отличался от отсутствия поля в запросе
    (тот же паттерн, что `summary_recipients` в `schemas/conferences.py`).
    `name_user` — та же правка, что доступна пользователю в своём профиле
    (редактирование по тем же правилам).
    """

    role: Literal["admin", "user"] | None = None
    is_blocked: bool | None = None
    team_id: uuid.UUID | None = None
    name_user: str | None = Field(default=None, min_length=1, max_length=255)


class TeamOut(BaseModel):
    """Команда в ответах админ-API."""

    model_config = ConfigDict(from_attributes=True)

    id: uuid.UUID
    name: str
    created_at: datetime


class TeamListOut(BaseModel):
    """Список команд (`GET /admin/teams`), отсортирован по названию."""

    items: list[TeamOut]
    total: int


class TeamCreateIn(BaseModel):
    """Тело создания команды; `name` обрезается от пробелов до проверки длины."""

    model_config = ConfigDict(str_strip_whitespace=True)

    name: str = Field(min_length=1, max_length=255)


class TeamUpdateIn(BaseModel):
    """Тело переименования команды; `name` обрезается от пробелов до проверки длины."""

    model_config = ConfigDict(str_strip_whitespace=True)

    name: str = Field(min_length=1, max_length=255)


class InvitationsSendIn(BaseModel):
    """Тело ручной рассылки .ics-приглашений (`POST /admin/conferences/{id}/invitations`).

    Пустой список получателей не отличается от отсутствующего поля — оба
    трактуются как «получатели по умолчанию» (см. `workers/tasks/invitations.py`).
    """

    emails: list[EmailStr] | None = None


class SettingsOut(BaseModel):
    """Эффективные настройки инстанса для отображения в админке.

    `transcription_queue_served` — обслуживается ли очередь `transcription`
    хотя бы одним воркером Celery
    прямо сейчас (`services.pipeline_producer.transcription_queue_served`);
    `False` при включённой транскрибации — сигнал админке показать
    предупреждение рядом с чекбоксом AI («модуль включён, но задачи некому
    обрабатывать»), не связано с доступностью уровня AI (`ai_levels`,
    которая смотрит только на железо/скачанные модели).
    """

    chat_enabled: bool
    hand_queue_enabled: bool
    transcription_enabled: bool
    ai_level: AiLevel
    ai_levels: list[AiLevelStatus]
    transcription_queue_served: bool
    summary_recipients: SummaryRecipientsMode
    display_timezone: str
    registration_team_choice: bool
    registration_email_domain_enabled: bool
    registration_email_domains: list[str] = Field(default_factory=list)
    contact_email_enabled: bool
    contact_email: str | None = None
    # Рычаги нагрузки медиа (`InstanceConfig.media_limits`) — потолок
    # качества публикации и максимум плиток сцены, см. `core/plugins/config.py`.
    publish_quality_cap: PublishQualityCap
    stage_max_tiles: StageMaxTiles
    # Согласие на обработку персональных данных при регистрации — см.
    # `core/plugins/config.py::InstanceConfig`.
    consent_required: bool
    consent_policy_text: str
    consent_policy_version: int
    # Проверка устройств на входе (сессия 33) — см. `core/plugins/config.py::InstanceConfig`.
    device_check_enabled: bool
    # Замена фона видео (сессия 35) — см. `core/plugins/config.py::InstanceConfig`.
    virtual_background_enabled: bool


class TestEmailIn(BaseModel):
    """Тело запроса тестовой отправки (`POST /admin/settings/test-email`).

    `to` не задан — получатель по умолчанию: email текущего администратора
    (см. `api/admin.py::send_test_email`).
    """

    to: EmailStr | None = None


class TestEmailOut(BaseModel):
    """Результат тестовой отправки — понятный админу успех/ошибка транспорта.

    `smtp_host`/`smtp_port` — только хост и порт (без логина/пароля, они не
    покидают `.env`), заполняются лишь при `EMAIL_BACKEND=smtp`.
    """

    success: bool
    message: str
    smtp_host: str | None = None
    smtp_port: int | None = None
