"""invitation card designs, guest invitation type, customisable greeting messages

Revision ID: 9a4c17be2d05
Revises: 63070b9fd010
Create Date: 2026-08-13 01:45:00.000000

Entirely additive. Every existing row is valid the moment this completes: `invitation_type`
carries a server default so no backfill is needed, and `invitation_messages` defaults to an
empty object whose missing keys resolve to built-in defaults in the application.

Hand-written rather than autogenerated because the partial unique index (one published
design per event) is not something Alembic infers.
"""

from collections.abc import Sequence

import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql

revision: str = "9a4c17be2d05"
down_revision: str | None = "63070b9fd010"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


# Created explicitly so the same objects can be dropped on downgrade. `create_type=False` on
# the column definitions below stops SQLAlchemy emitting a second CREATE TYPE for each.
invitation_type = postgresql.ENUM("single", "family", name="invitation_type")
card_renderer = postgresql.ENUM("component", "image", "layered", name="card_renderer")
card_design_status = postgresql.ENUM("draft", "published", name="card_design_status")


def upgrade() -> None:
    bind = op.get_bind()
    invitation_type.create(bind, checkfirst=True)
    card_renderer.create(bind, checkfirst=True)
    card_design_status.create(bind, checkfirst=True)

    op.add_column(
        "guest",
        sa.Column(
            "invitation_type",
            postgresql.ENUM("single", "family", name="invitation_type", create_type=False),
            server_default="single",
            nullable=False,
        ),
    )

    op.add_column(
        "wedding",
        sa.Column(
            "invitation_messages",
            postgresql.JSONB(astext_type=sa.Text()),
            server_default="{}",
            nullable=False,
        ),
    )

    op.create_table(
        "event_card_design",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("event_id", sa.Uuid(), nullable=False),
        sa.Column(
            "renderer",
            postgresql.ENUM(
                "component", "image", "layered", name="card_renderer", create_type=False
            ),
            nullable=False,
        ),
        sa.Column(
            "status",
            postgresql.ENUM("draft", "published", name="card_design_status", create_type=False),
            nullable=False,
        ),
        sa.Column("version", sa.Integer(), nullable=False),
        sa.Column(
            "config",
            postgresql.JSONB(astext_type=sa.Text()),
            server_default="{}",
            nullable=False,
        ),
        sa.Column(
            "assets",
            postgresql.JSONB(astext_type=sa.Text()),
            server_default="{}",
            nullable=False,
        ),
        sa.Column("published_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("created_by_admin_id", sa.Uuid(), nullable=True),
        sa.Column(
            "created_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.Column(
            "updated_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.ForeignKeyConstraint(["event_id"], ["event.id"], ondelete="CASCADE"),
        sa.ForeignKeyConstraint(["created_by_admin_id"], ["admin_user.id"], ondelete="SET NULL"),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("event_id", "version", name="uq_card_design_event_version"),
    )
    op.create_index(
        "ix_event_card_design_event_id", "event_card_design", ["event_id"], unique=False
    )
    # The invariant that matters: two concurrent publishes cannot both win. Enforcing it in
    # the endpoint instead would leave the guest page choosing arbitrarily between them.
    op.create_index(
        "uq_card_design_one_published",
        "event_card_design",
        ["event_id"],
        unique=True,
        postgresql_where=sa.text("status = 'published'"),
    )


def downgrade() -> None:
    op.drop_index("uq_card_design_one_published", table_name="event_card_design")
    op.drop_index("ix_event_card_design_event_id", table_name="event_card_design")
    op.drop_table("event_card_design")
    op.drop_column("wedding", "invitation_messages")
    op.drop_column("guest", "invitation_type")

    bind = op.get_bind()
    card_design_status.drop(bind, checkfirst=True)
    card_renderer.drop(bind, checkfirst=True)
    invitation_type.drop(bind, checkfirst=True)
