"""Notification wire shapes (Pydantic v2) — plan §5/§6.4, M5. ORM rows
serialize via from_attributes, same convention as every other schema module
here — the TS client is generated from the OpenAPI these produce (react.md:
never hand-written interface mirrors)."""
from __future__ import annotations

import datetime
import uuid

from pydantic import BaseModel, ConfigDict


class NotificationOut(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: uuid.UUID
    recipient_id: uuid.UUID
    # M6: the actor renders through rep() FOR THE RECIPIENT at read time
    # (services.projection.project_notification_actor): a visible actor is
    # unchanged; one who collapses becomes their boundary lead's id; one
    # beyond the recipient's world becomes actor_id None + actor_label
    # (the side label). Raw truth stays in the row.
    actor_id: uuid.UUID | None = None
    actor_label: str | None = None
    kind: str  # mention | thread_reply | action_to
    category: str | None = None
    context_label: str | None = None
    body: str
    link: str
    read: bool
    resolved: bool
    source_type: str  # comment | ball
    source_id: uuid.UUID
    ball_id: uuid.UUID
    created_at: datetime.datetime


class NotificationsOut(BaseModel):
    """GET /api/notifications — the session player's notifications, newest
    first (capped at services.notifications.LIST_LIMIT), plus the two
    independent counts the bell needs (review-patterns-ref §3.6):
    unread_count (per-row `read`) and unresolved_count (mirrors the source
    ball via propagate_resolved) move separately and both drive the badge.
    Both counts are TRUE TOTALS, never capped by the listing's page size.
    """

    items: list[NotificationOut]
    unread_count: int
    unresolved_count: int
