"""Card design (spec invitation-card, design D1).

One published design per event, any number of drafts beside it. The design is data, not
code: `renderer` picks how it is drawn and `config` carries everything that varies between
customers, which is what lets a second wedding reuse a template with no deploy.

`config` and `assets` are JSONB because their shape depends on `renderer` — the API
validates them through a discriminated union, so the column stays honest without the
schema needing to know about every renderer that will ever exist.
"""

import uuid
from datetime import datetime
from typing import Any

from sqlalchemy import ForeignKey, Index, Integer, UniqueConstraint, text
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.models.base import Base, TimestampTZ, created_at_col, pg_enum, updated_at_col, uuid_pk
from app.models.enums import CardDesignStatus, CardRenderer
from app.models.wedding import Event


class EventCardDesign(Base):
    __tablename__ = "event_card_design"
    __table_args__ = (
        UniqueConstraint("event_id", "version", name="uq_card_design_event_version"),
        # At most one published design per event, enforced by the database rather than by
        # the publish endpoint remembering to unpublish the previous one. Two concurrent
        # publishes would otherwise both succeed and the guest page would pick arbitrarily.
        Index(
            "uq_card_design_one_published",
            "event_id",
            unique=True,
            postgresql_where=text("status = 'published'"),
        ),
    )

    id: Mapped[uuid.UUID] = uuid_pk()
    event_id: Mapped[uuid.UUID] = mapped_column(
        ForeignKey("event.id", ondelete="CASCADE"), nullable=False, index=True
    )

    renderer: Mapped[CardRenderer] = mapped_column(
        pg_enum(CardRenderer, "card_renderer"), nullable=False
    )
    status: Mapped[CardDesignStatus] = mapped_column(
        pg_enum(CardDesignStatus, "card_design_status"),
        default=CardDesignStatus.DRAFT,
        nullable=False,
    )
    #: Monotonic per event. Never reused, so a rolled-back version keeps its identity in the
    #: audit log and in any conversation with the customer about "the one from Tuesday".
    version: Mapped[int] = mapped_column(Integer, nullable=False, default=1)

    #: Renderer-specific. `component`: template key, palette, fonts, motion flags.
    #: `image`: per-locale alt text.
    config: Mapped[dict[str, Any]] = mapped_column(
        JSONB, nullable=False, default=dict, server_default="{}"
    )
    #: Logical asset name -> stored media URL, e.g. {"paper": "/media/cards/…/ab12.webp"}.
    #: Template code refers to names; the URLs are content-addressed and change on re-upload.
    assets: Mapped[dict[str, str]] = mapped_column(
        JSONB, nullable=False, default=dict, server_default="{}"
    )

    published_at: Mapped[datetime | None] = mapped_column(TimestampTZ)
    created_by_admin_id: Mapped[uuid.UUID | None] = mapped_column(
        ForeignKey("admin_user.id", ondelete="SET NULL")
    )

    created_at: Mapped[datetime] = created_at_col()
    updated_at: Mapped[datetime] = updated_at_col()

    event: Mapped[Event] = relationship()
