"""add calendars & calendar_entries

Revision ID: 215330c5ec15
Revises: 20251102_223123
Create Date: 2025-11-03 13:07:10.387189
"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = "215330c5ec15"
down_revision = "20251102_223123"
branch_labels = None
depends_on = None


def upgrade():
    # calendars
    op.create_table(
        "calendars",
        sa.Column("id", sa.Integer(), primary_key=True),
        sa.Column("post_id", sa.Integer(), sa.ForeignKey("posts.id", ondelete="CASCADE"), nullable=False),
        sa.Column("name", sa.String(length=255), nullable=False),
        sa.Column("slug", sa.String(length=255), nullable=False),
        sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
        sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
        sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True),
        # no hard UniqueConstraint; we enforce soft-delete-aware uniqueness with a partial index below
    )
    # helpful lookup indexes
    op.create_index("ix_calendars_post_id", "calendars", ["post_id"], unique=False)
    op.create_index("ix_calendars_name", "calendars", ["name"], unique=False)
    op.create_index("ix_calendars_slug", "calendars", ["slug"], unique=False)

    # calendar_entries
    op.create_table(
        "calendar_entries",
        sa.Column("id", sa.Integer(), primary_key=True),
        sa.Column("calendar_id", sa.Integer(), sa.ForeignKey("calendars.id", ondelete="CASCADE"), nullable=False),
        sa.Column("name", sa.String(length=255), nullable=False),
        sa.Column("start_at", sa.DateTime(timezone=True), nullable=False),
        sa.Column("end_at", sa.DateTime(timezone=True), nullable=True),  # <- allow open-ended
        sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
        sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
        sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True),
        sa.CheckConstraint("(end_at IS NULL) OR (end_at >= start_at)", name="ck_calendar_entries_end_after_start"),
    )
    op.create_index("ix_calendar_entries_calendar_id", "calendar_entries", ["calendar_id"], unique=False)
    op.create_index("ix_calendar_entries_start_at", "calendar_entries", ["start_at"], unique=False)
    op.create_index("ix_calendar_entries_name", "calendar_entries", ["name"], unique=False)

    # ---- Soft-delete-aware uniqueness for calendars (Postgres) ----
    # One active calendar per (post_id, lower(slug))
    if op.get_bind().dialect.name == "postgresql":
        # cleanup any active duplicates (defensive; table is new on fresh runs)
        op.execute("""
            WITH ranked AS (
                SELECT
                    id,
                    ROW_NUMBER() OVER (
                        PARTITION BY post_id, lower(slug)
                        ORDER BY updated_at DESC, created_at DESC, id DESC
                    ) AS rn
                FROM calendars
                WHERE deleted_at IS NULL
            )
            UPDATE calendars c
            SET deleted_at = NOW()
            FROM ranked r
            WHERE c.id = r.id AND r.rn > 1;
        """)
        op.execute("""
            CREATE UNIQUE INDEX IF NOT EXISTS ux_calendars_post_slug_active
            ON calendars (post_id, lower(slug))
            WHERE deleted_at IS NULL;
        """)


def downgrade():
    # drop in reverse dependency order
    op.drop_index("ix_calendar_entries_name", table_name="calendar_entries")
    op.drop_index("ix_calendar_entries_start_at", table_name="calendar_entries")
    op.drop_index("ix_calendar_entries_calendar_id", table_name="calendar_entries")
    op.drop_table("calendar_entries")

    if op.get_bind().dialect.name == "postgresql":
        op.execute("DROP INDEX IF EXISTS ux_calendars_post_slug_active;")

    op.drop_index("ix_calendars_slug", table_name="calendars")
    op.drop_index("ix_calendars_name", table_name="calendars")
    op.drop_index("ix_calendars_post_id", table_name="calendars")
    op.drop_table("calendars")
