"""Projection schema for the selector-scoped scene-VERSION read model (REC-231).

Distinct from ``schemas/board.py`` (the manifest-scoped board read model): this is the
scene-version diagnostic surface — active pointer + unpointed-candidate diagnostics —
projected by ``workspace.readmodel.get_scene_versions``. Pure projection; never written.
"""

from __future__ import annotations

from pydantic import BaseModel, ConfigDict, Field

from recoil.api.schemas._base import _Versioned


class SceneVersionEntryModel(BaseModel):
    """One version's diagnostic projection from a manifest ``versions[]`` entry.

    The REQUIRED keys mirror exactly what ``load_manifest`` validates on read
    (``version``/``state``/``downstream``/``artifact``) so a model and a validated
    manifest never disagree. The diagnostic fields default so a minimal/legacy entry
    projects without error (Phase 3 populates ``kind``/``shot_count``/
    ``structure_fingerprint`` on real writes). ``extra="ignore"`` lets the manifest
    carry its own keys (``source``/``parent_version``/``created_at``) without breaking
    ``SceneVersionEntryModel(**entry)``.
    """

    model_config = ConfigDict(populate_by_name=True, frozen=True, extra="ignore")

    version: int
    state: str
    downstream: str
    artifact: str
    kind: str = "reshoot"
    shot_count: int = 0
    structure_fingerprint: str | None = None


class SceneVersionsModel(_Versioned):
    """Top-level envelope for the scene-version read model.

    ``versions[]`` is STRUCTURED (not bare ints) so the dailies surface can read each
    version's shot-count/state delta WITHOUT re-loading every body. ``newer_unpointed_
    versions`` counts versions whose number exceeds the active pointer — the newest is
    NEVER reported active (the pointer is the only authority).
    """

    active_version: int
    newer_unpointed_versions: int = 0
    versions: list[SceneVersionEntryModel] = Field(default_factory=list)
