"""Pydantic-схемы протокола WS-чата конференции (`api/chat.py`).

Входящий протокол — дискриминированное объединение по полю `type`: первым
сообщением клиент обязан прислать `auth` (LiveKit access-токен, не
query-параметр — не палим токен в логах nginx), далее — произвольное число
`message`. Исходящий протокол — `history` (один раз, сразу после успешной
аутентификации), `message` (broadcast через Redis pub/sub) и `error`.
"""

from datetime import UTC, datetime
from typing import Annotated, Any, Literal

from pydantic import BaseModel, Field, TypeAdapter, field_serializer, field_validator

# Ограничение длины текста сообщения.
MAX_MESSAGE_LENGTH = 2000


def _to_iso_z(value: datetime) -> str:
    """Отформатировать aware-datetime как UTC ISO-строку с суффиксом `Z`."""
    return value.astimezone(UTC).isoformat().replace("+00:00", "Z")


class ChatAuthIn(BaseModel):
    """Первое сообщение клиента — аутентификация LiveKit access-токеном."""

    type: Literal["auth"]
    token: str


class ChatMessageIn(BaseModel):
    """Сообщение клиента с текстом чата — text обрезается по пробелам и не должен быть пустым."""

    type: Literal["message"]
    text: str = Field(min_length=1, max_length=MAX_MESSAGE_LENGTH)

    @field_validator("text", mode="before")
    @classmethod
    def _strip(cls, value: Any) -> Any:
        return value.strip() if isinstance(value, str) else value


# Дискриминированное объединение входящих сообщений клиента по полю `type`.
ChatClientEnvelope = Annotated[ChatAuthIn | ChatMessageIn, Field(discriminator="type")]
chat_client_envelope_adapter: TypeAdapter[ChatAuthIn | ChatMessageIn] = TypeAdapter(
    ChatClientEnvelope
)


class ChatMessageOut(BaseModel):
    """Одно сообщение чата в исходящем протоколе (history/broadcast)."""

    id: int
    author_id: str | None
    author_name: str
    is_guest: bool
    text: str
    created_at: datetime

    @field_serializer("created_at")
    def _serialize_created_at(self, value: datetime) -> str:
        return _to_iso_z(value)


class ChatHistoryOut(BaseModel):
    """История последних сообщений открытой сессии — отправляется один раз после auth."""

    type: Literal["history"] = "history"
    messages: list[ChatMessageOut]


class ChatMessageEventOut(BaseModel):
    """Одно новое сообщение чата — broadcast через Redis pub/sub (в т.ч. отправителю)."""

    type: Literal["message"] = "message"
    message: ChatMessageOut


class ChatErrorOut(BaseModel):
    """Сообщение об ошибке протокола (например, невалидный текст) без разрыва соединения."""

    type: Literal["error"] = "error"
    code: str
