"""Meta wire shapes — the constants module, typed for OpenAPI.

These models SHAPE the payload; the VALUES come from app.constants (the one
authority — nothing here re-types a vocabulary string or a color class).
"""
from __future__ import annotations

from pydantic import BaseModel, Field


class VocabOut(BaseModel):
    """One vocabulary: its ordered values + the exact tailwind pill classes
    and dot classes per value, its default, and whether the stored strings
    are all-lowercase (frontend applies CSS `capitalize`)."""

    values: list[str]
    styles: dict[str, str]
    dots: dict[str, str]
    default: str
    capitalize: bool


class FallbackOut(BaseModel):
    """Defensive classes for an unknown/legacy value (ref §1.7)."""

    style: str
    dot: str


class CouplingOut(BaseModel):
    """The bidirectional status<->review field-coupling map (ref §2.1/2.2).
    Intentionally not exhaustive — absent keys mean 'no rule'."""

    status_to_review: dict[str, str]
    review_to_status: dict[str, str]


class ServeSheetField(BaseModel):
    field: str
    label: str
    kind: str  # text | markdown | url | tags | category | players
    required: bool = False
    remember_last_used: bool = False


class CourtConfigOut(BaseModel):
    key: str
    name: str
    ball_types: list[str]
    serve_sheet: list[ServeSheetField]
    columns: list[str]
    status_vocab: str
    review_vocab: str | None = None
    ball_chip_driver: str  # "coupling" | "action_to"
    aging_days: int
    defaults: dict[str, str] = Field(default_factory=dict)
    # Court automation map. status_to_review/review_to_status are live from
    # M1; the touch/movement automations are declared data until M2.
    automations: dict

class MetaOut(BaseModel):
    """GET /api/meta — vocab + colors + court configs from one authority."""

    vocab: dict[str, VocabOut]
    fallback: FallbackOut
    resolved_badge_class: str
    coupling: CouplingOut
    court_configs: dict[str, CourtConfigOut]
