"""initial schema

Revision ID: 4fe03c4eb364
Revises:
Create Date: 2026-08-08 10:26:36.614711
"""

from collections.abc import Sequence

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

revision: str = "4fe03c4eb364"
down_revision: str | None = None
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table(
        "admin_user",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("email", sa.String(length=320), nullable=False),
        sa.Column("name", sa.Text(), nullable=True),
        sa.Column(
            "role", sa.Enum("super_admin", "co_host", "viewer", name="admin_role"), nullable=False
        ),
        sa.Column("is_active", sa.Boolean(), nullable=False),
        sa.Column("last_login_at", sa.DateTime(timezone=True), 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.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("email"),
    )
    op.create_table(
        "message_template",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("channel", sa.Enum("email", "whatsapp", "sms", name="channel"), nullable=False),
        sa.Column(
            "purpose",
            sa.Enum(
                "invite",
                "reminder_15",
                "reminder_7",
                "reminder_2",
                "thank_you",
                "cancel_confirm",
                "custom",
                name="template_purpose",
            ),
            nullable=False,
        ),
        sa.Column("locale", sa.Enum("bn", "en", name="locale"), nullable=False),
        sa.Column("subject", sa.Text(), nullable=True),
        sa.Column("body", sa.Text(), nullable=False),
        sa.Column("body_text", sa.Text(), nullable=True),
        sa.Column("provider_template_id", sa.String(length=120), nullable=True),
        sa.Column("is_active", sa.Boolean(), nullable=False),
        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.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint(
            "channel", "purpose", "locale", name="uq_template_channel_purpose_locale"
        ),
    )
    op.create_table(
        "rate_limit",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("bucket", sa.String(length=40), nullable=False),
        sa.Column("ip_hash", sa.String(length=64), nullable=False),
        sa.Column("window_start", sa.DateTime(timezone=True), nullable=False),
        sa.Column("count", sa.Integer(), nullable=False),
        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.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("bucket", "ip_hash", "window_start", name="uq_rate_limit_window"),
    )
    op.create_index("ix_rate_limit_cleanup", "rate_limit", ["window_start"], unique=False)
    op.create_table(
        "wedding",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("bride_name", sa.Text(), nullable=False),
        sa.Column("groom_name", sa.Text(), nullable=False),
        sa.Column("slug", sa.String(length=120), nullable=False),
        sa.Column("default_locale", sa.Enum("bn", "en", name="locale"), nullable=False),
        sa.Column("timezone", sa.String(length=64), nullable=False),
        sa.Column("host_contact_phone", sa.String(length=20), nullable=True),
        sa.Column(
            "created_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("slug"),
    )
    op.create_table(
        "audit_log",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("admin_user_id", sa.Uuid(), nullable=True),
        sa.Column("actor_email", sa.String(length=320), nullable=True),
        sa.Column("action", sa.String(length=80), nullable=False),
        sa.Column("entity_type", sa.String(length=60), nullable=True),
        sa.Column("entity_id", sa.Uuid(), nullable=True),
        sa.Column("before", postgresql.JSONB(astext_type=sa.Text()), nullable=True),
        sa.Column("after", postgresql.JSONB(astext_type=sa.Text()), nullable=True),
        sa.Column("ip", sa.String(length=64), nullable=True),
        sa.Column(
            "created_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.ForeignKeyConstraint(["admin_user_id"], ["admin_user.id"], ondelete="SET NULL"),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index(op.f("ix_audit_log_action"), "audit_log", ["action"], unique=False)
    op.create_index(
        op.f("ix_audit_log_admin_user_id"), "audit_log", ["admin_user_id"], unique=False
    )
    op.create_table(
        "event",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("wedding_id", sa.Uuid(), nullable=False),
        sa.Column(
            "type", sa.Enum("mehedi", "marriage", "walima", name="event_type"), nullable=False
        ),
        sa.Column("slug", sa.String(length=60), nullable=False),
        sa.Column("title_bn", sa.Text(), nullable=False),
        sa.Column("title_en", sa.Text(), nullable=False),
        sa.Column("starts_at", sa.DateTime(timezone=True), nullable=False),
        sa.Column("ends_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("venue_name", sa.Text(), nullable=False),
        sa.Column("venue_address", sa.Text(), nullable=False),
        sa.Column("map_url", sa.Text(), nullable=True),
        sa.Column("dress_code", sa.Text(), nullable=True),
        sa.Column("notes", sa.Text(), nullable=True),
        sa.Column("theme_key", sa.String(length=40), nullable=False),
        sa.Column("cover_image_url", sa.Text(), nullable=True),
        sa.Column("music_url", sa.Text(), nullable=True),
        sa.Column("rsvp_deadline", sa.DateTime(timezone=True), nullable=True),
        sa.Column("capacity", sa.Integer(), nullable=True),
        sa.Column("is_published", sa.Boolean(), nullable=False),
        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(["wedding_id"], ["wedding.id"], ondelete="CASCADE"),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("slug"),
        sa.UniqueConstraint("wedding_id", "type", name="uq_event_wedding_type"),
    )
    op.create_index(op.f("ix_event_wedding_id"), "event", ["wedding_id"], unique=False)
    op.create_table(
        "guest",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("wedding_id", sa.Uuid(), nullable=False),
        sa.Column("full_name", sa.Text(), nullable=False),
        sa.Column("email", sa.String(length=320), nullable=True),
        sa.Column("phone_e164", sa.String(length=20), nullable=True),
        sa.Column("whatsapp_phone_e164", sa.String(length=20), nullable=True),
        sa.Column("side", sa.Enum("bride", "groom", "common", name="guest_side"), nullable=False),
        sa.Column("group_tag", sa.ARRAY(sa.Text()), server_default="{}", nullable=False),
        sa.Column("preferred_locale", sa.Enum("bn", "en", name="locale"), nullable=False),
        sa.Column(
            "preferred_channel",
            sa.Enum("whatsapp", "sms", "email", "auto", name="preferred_channel"),
            nullable=False,
        ),
        sa.Column(
            "source",
            sa.Enum("manual", "csv_import", "self_registered", name="guest_source"),
            nullable=False,
        ),
        sa.Column("do_not_contact", sa.Boolean(), nullable=False),
        sa.Column("email_invalid", sa.Boolean(), nullable=False),
        sa.Column("is_deleted", sa.Boolean(), nullable=False),
        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.CheckConstraint(
            "phone_e164 IS NOT NULL OR email IS NOT NULL", name="ck_guest_has_contact"
        ),
        sa.ForeignKeyConstraint(["wedding_id"], ["wedding.id"], ondelete="CASCADE"),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index(op.f("ix_guest_is_deleted"), "guest", ["is_deleted"], unique=False)
    op.create_index(op.f("ix_guest_wedding_id"), "guest", ["wedding_id"], unique=False)
    op.create_index(
        "uq_guest_wedding_email",
        "guest",
        ["wedding_id", sa.literal_column("lower(email)")],
        unique=True,
        postgresql_where=sa.text("email IS NOT NULL AND is_deleted = false"),
    )
    op.create_index(
        "uq_guest_wedding_phone",
        "guest",
        ["wedding_id", "phone_e164"],
        unique=True,
        postgresql_where=sa.text("phone_e164 IS NOT NULL AND is_deleted = false"),
    )
    op.create_table(
        "invitation",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("event_id", sa.Uuid(), nullable=False),
        sa.Column("guest_id", sa.Uuid(), nullable=True),
        sa.Column("token", sa.String(length=32), nullable=False),
        sa.Column("short_code", sa.String(length=6), nullable=False),
        sa.Column(
            "status",
            sa.Enum(
                "pending",
                "opened",
                "accepted",
                "declined",
                "cancelled",
                "expired",
                name="invitation_status",
            ),
            nullable=False,
        ),
        sa.Column("max_guests", sa.Integer(), nullable=False),
        sa.Column("opened_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("responded_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("cancelled_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("open_count", sa.Integer(), nullable=False),
        sa.Column(
            "invited_via",
            sa.Enum("whatsapp", "sms", "email", "print_qr", "link", name="invited_via"),
            nullable=False,
        ),
        sa.Column("invited_at", sa.DateTime(timezone=True), 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(["guest_id"], ["guest.id"], ondelete="CASCADE"),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("event_id", "guest_id", name="uq_invitation_event_guest"),
        sa.UniqueConstraint("short_code"),
        sa.UniqueConstraint("token"),
    )
    op.create_index(op.f("ix_invitation_event_id"), "invitation", ["event_id"], unique=False)
    op.create_index(
        "ix_invitation_event_status", "invitation", ["event_id", "status"], unique=False
    )
    op.create_index(op.f("ix_invitation_guest_id"), "invitation", ["guest_id"], unique=False)
    op.create_table(
        "reminder_schedule",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("event_id", sa.Uuid(), nullable=False),
        sa.Column("offset_days", sa.Integer(), nullable=False),
        sa.Column("send_at_local_time", sa.Time(), nullable=False),
        sa.Column("channels", sa.ARRAY(sa.Text()), server_default="{email}", nullable=False),
        sa.Column(
            "audience",
            sa.Enum("accepted", "pending", "all", name="reminder_audience"),
            nullable=False,
        ),
        sa.Column("is_enabled", sa.Boolean(), nullable=False),
        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.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("event_id", "offset_days", name="uq_schedule_event_offset"),
    )
    op.create_index(
        op.f("ix_reminder_schedule_event_id"), "reminder_schedule", ["event_id"], unique=False
    )
    op.create_table(
        "message_job",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("invitation_id", sa.Uuid(), nullable=False),
        sa.Column("template_id", sa.Uuid(), nullable=True),
        sa.Column("schedule_id", sa.Uuid(), nullable=True),
        sa.Column("channel", sa.Enum("email", "whatsapp", "sms", name="channel"), nullable=False),
        sa.Column("scheduled_for", sa.DateTime(timezone=True), nullable=False),
        sa.Column(
            "status",
            sa.Enum(
                "queued",
                "sending",
                "sent",
                "delivered",
                "read",
                "failed",
                "skipped",
                name="message_job_status",
            ),
            nullable=False,
        ),
        sa.Column("attempts", sa.Integer(), nullable=False),
        sa.Column("provider_message_id", sa.String(length=200), nullable=True),
        sa.Column("error_code", sa.String(length=80), nullable=True),
        sa.Column("error_message", sa.Text(), nullable=True),
        sa.Column("skip_reason", sa.String(length=200), nullable=True),
        sa.Column("idempotency_key", sa.String(length=200), nullable=False),
        sa.Column("sent_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("delivered_at", sa.DateTime(timezone=True), 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(["invitation_id"], ["invitation.id"], ondelete="CASCADE"),
        sa.ForeignKeyConstraint(["schedule_id"], ["reminder_schedule.id"], ondelete="SET NULL"),
        sa.ForeignKeyConstraint(["template_id"], ["message_template.id"], ondelete="SET NULL"),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("idempotency_key", name="uq_message_job_idempotency"),
    )
    op.create_index(
        "ix_message_job_claim", "message_job", ["status", "scheduled_for"], unique=False
    )
    op.create_index(
        op.f("ix_message_job_invitation_id"), "message_job", ["invitation_id"], unique=False
    )
    op.create_index("ix_message_job_provider", "message_job", ["provider_message_id"], unique=False)
    op.create_table(
        "rsvp",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("invitation_id", sa.Uuid(), nullable=False),
        sa.Column(
            "response",
            sa.Enum("accepted", "declined", "cancelled", name="rsvp_response"),
            nullable=False,
        ),
        sa.Column("name", sa.Text(), nullable=False),
        sa.Column("email", sa.String(length=320), nullable=True),
        sa.Column("phone_e164", sa.String(length=20), nullable=True),
        sa.Column("party_size", sa.Integer(), nullable=False),
        sa.Column("adults", sa.Integer(), nullable=True),
        sa.Column("children", sa.Integer(), nullable=True),
        sa.Column("message_to_couple", sa.String(length=500), nullable=True),
        sa.Column("dietary_notes", sa.Text(), nullable=True),
        sa.Column("cancel_reason", sa.Text(), nullable=True),
        sa.Column("responded_at", sa.DateTime(timezone=True), nullable=False),
        sa.Column("ip_hash", sa.String(length=64), nullable=True),
        sa.Column("user_agent", sa.Text(), 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(["invitation_id"], ["invitation.id"], ondelete="CASCADE"),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("invitation_id"),
    )
    op.create_table(
        "rsvp_history",
        sa.Column("id", sa.Uuid(), nullable=False),
        sa.Column("invitation_id", sa.Uuid(), nullable=False),
        sa.Column(
            "from_status",
            sa.Enum(
                "pending",
                "opened",
                "accepted",
                "declined",
                "cancelled",
                "expired",
                name="invitation_status",
            ),
            nullable=True,
        ),
        sa.Column(
            "to_status",
            sa.Enum(
                "pending",
                "opened",
                "accepted",
                "declined",
                "cancelled",
                "expired",
                name="invitation_status",
            ),
            nullable=False,
        ),
        sa.Column("party_size", sa.Integer(), nullable=True),
        sa.Column(
            "actor", sa.Enum("guest", "admin", "system", name="history_actor"), nullable=False
        ),
        sa.Column("actor_admin_id", sa.Uuid(), nullable=True),
        sa.Column("source", sa.String(length=40), nullable=True),
        sa.Column(
            "created_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.ForeignKeyConstraint(["actor_admin_id"], ["admin_user.id"], ondelete="SET NULL"),
        sa.ForeignKeyConstraint(["invitation_id"], ["invitation.id"], ondelete="CASCADE"),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index(
        op.f("ix_rsvp_history_invitation_id"), "rsvp_history", ["invitation_id"], unique=False
    )
    # ### end Alembic commands ###


def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_index(op.f("ix_rsvp_history_invitation_id"), table_name="rsvp_history")
    op.drop_table("rsvp_history")
    op.drop_table("rsvp")
    op.drop_index("ix_message_job_provider", table_name="message_job")
    op.drop_index(op.f("ix_message_job_invitation_id"), table_name="message_job")
    op.drop_index("ix_message_job_claim", table_name="message_job")
    op.drop_table("message_job")
    op.drop_index(op.f("ix_reminder_schedule_event_id"), table_name="reminder_schedule")
    op.drop_table("reminder_schedule")
    op.drop_index(op.f("ix_invitation_guest_id"), table_name="invitation")
    op.drop_index("ix_invitation_event_status", table_name="invitation")
    op.drop_index(op.f("ix_invitation_event_id"), table_name="invitation")
    op.drop_table("invitation")
    op.drop_index(
        "uq_guest_wedding_phone",
        table_name="guest",
        postgresql_where=sa.text("phone_e164 IS NOT NULL AND is_deleted = false"),
    )
    op.drop_index(
        "uq_guest_wedding_email",
        table_name="guest",
        postgresql_where=sa.text("email IS NOT NULL AND is_deleted = false"),
    )
    op.drop_index(op.f("ix_guest_wedding_id"), table_name="guest")
    op.drop_index(op.f("ix_guest_is_deleted"), table_name="guest")
    op.drop_table("guest")
    op.drop_index(op.f("ix_event_wedding_id"), table_name="event")
    op.drop_table("event")
    op.drop_index(op.f("ix_audit_log_admin_user_id"), table_name="audit_log")
    op.drop_index(op.f("ix_audit_log_action"), table_name="audit_log")
    op.drop_table("audit_log")
    op.drop_table("wedding")
    op.drop_index("ix_rate_limit_cleanup", table_name="rate_limit")
    op.drop_table("rate_limit")
    op.drop_table("message_template")
    op.drop_table("admin_user")
    # ### end Alembic commands ###
