"""Added leaderboard_versions table and corresponding constraints

Revision ID: cc80e886e153
Revises: 040f2dfde5a5
Create Date: 2023-11-08 16:16:39.265150

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = "cc80e886e153"
down_revision = "040f2dfde5a5"
branch_labels = None
depends_on = None


def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table(
        "leaderboard_versions",
        sa.Column("leaderboard_id", sa.UUID(), nullable=False),
        sa.Column("version_number", sa.DECIMAL(), nullable=False),
        sa.Column("published", sa.Boolean(), nullable=False),
        sa.Column(
            "created_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("TIMEZONE('utc', statement_timestamp())"),
            nullable=False,
        ),
        sa.Column(
            "updated_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("TIMEZONE('utc', statement_timestamp())"),
            nullable=False,
        ),
        sa.ForeignKeyConstraint(
            ["leaderboard_id"],
            ["leaderboards.id"],
            name=op.f("fk_leaderboard_versions_leaderboard_id_leaderboards"),
            ondelete="CASCADE",
        ),
        sa.PrimaryKeyConstraint(
            "leaderboard_id", "version_number", name=op.f("pk_leaderboard_versions")
        ),
        sa.UniqueConstraint(
            "leaderboard_id",
            "version_number",
            name=op.f("uq_leaderboard_versions_leaderboard_id"),
        ),
    )
    op.create_index(
        op.f("ix_leaderboard_versions_created_at"),
        "leaderboard_versions",
        ["created_at"],
        unique=False,
    )
    op.add_column(
        "leaderboard_scores",
        sa.Column("leaderboard_version_number", sa.DECIMAL(), nullable=True),
    )
    op.drop_constraint(
        "uq_leaderboard_scores_leaderboard_id", "leaderboard_scores", type_="unique"
    )
    op.create_unique_constraint(
        op.f("uq_leaderboard_scores_leaderboard_id"),
        "leaderboard_scores",
        ["leaderboard_id", "address", "leaderboard_version_number"],
    )
    op.drop_constraint(
        "fk_leaderboard_scores_leaderboard_id_leaderboards",
        "leaderboard_scores",
        type_="foreignkey",
    )
    op.create_foreign_key(
        op.f("fk_leaderboard_scores_leaderboard_id_leaderboard_versions"),
        "leaderboard_scores",
        "leaderboard_versions",
        ["leaderboard_id", "leaderboard_version_number"],
        ["leaderboard_id", "version_number"],
        ondelete="CASCADE",
    )
    # ### end Alembic commands ###

    # Insert version 0 for all existing leaderboards
    op.execute(
        """
        INSERT INTO leaderboard_versions (leaderboard_id, version_number, published)
        SELECT id, 0, true FROM leaderboards
        """
    )
    # Set the leaderboard_version_number for all existing scores to the version 0
    op.execute(
        """
        UPDATE leaderboard_scores SET leaderboard_version_number = 0
        """
    )
    # Alter leaderboard_scores to make leaderboard_version_number non-nullable
    op.alter_column(
        "leaderboard_scores",
        "leaderboard_version_number",
        nullable=False,
    )


def downgrade():
    op.execute(
        """
        WITH latest_version_for_leaderboard AS (
            SELECT leaderboard_id, MAX(version_number) AS latest_version
            FROM leaderboard_versions WHERE published = true
            GROUP BY leaderboard_id
        )
        DELETE FROM leaderboard_scores WHERE
            (leaderboard_id, leaderboard_version_number) NOT IN (
                SELECT
                    leaderboard_id,
                    latest_version AS leaderboard_version_number
                FROM
                    latest_version_for_leaderboard
            )
        """
    )
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_constraint(
        op.f("fk_leaderboard_scores_leaderboard_id_leaderboard_versions"),
        "leaderboard_scores",
        type_="foreignkey",
    )
    op.create_foreign_key(
        "fk_leaderboard_scores_leaderboard_id_leaderboards",
        "leaderboard_scores",
        "leaderboards",
        ["leaderboard_id"],
        ["id"],
        ondelete="CASCADE",
    )
    op.drop_constraint(
        op.f("uq_leaderboard_scores_leaderboard_id"),
        "leaderboard_scores",
        type_="unique",
    )
    op.create_unique_constraint(
        "uq_leaderboard_scores_leaderboard_id",
        "leaderboard_scores",
        ["leaderboard_id", "address"],
    )
    op.drop_column("leaderboard_scores", "leaderboard_version_number")
    op.drop_index(
        op.f("ix_leaderboard_versions_created_at"), table_name="leaderboard_versions"
    )
    op.drop_table("leaderboard_versions")
    # ### end Alembic commands ###
