"""Add database-backed manual run idempotency key

Revision ID: 20260217_0005
Revises: 20260217_0004
Create Date: 2026-02-17 16:20:00.000000
"""

from collections.abc import Sequence

import sqlalchemy as sa

from alembic import op

# revision identifiers, used by Alembic.
revision: str = "20260217_0005"
down_revision: str | Sequence[str] | None = "20260217_0004"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


INDEX_NAME = "uq_crawl_runs_user_manual_idempotency_key"


def upgrade() -> None:
    bind = op.get_bind()
    inspector = sa.inspect(bind)
    columns = {column["name"] for column in inspector.get_columns("crawl_runs")}
    indexes = {index["name"] for index in inspector.get_indexes("crawl_runs")}

    if "idempotency_key" not in columns:
        op.add_column(
            "crawl_runs",
            sa.Column("idempotency_key", sa.String(length=128), nullable=True),
        )
        columns.add("idempotency_key")

    if "idempotency_key" in columns:
        op.execute(
            """
            UPDATE crawl_runs
            SET idempotency_key = NULLIF(BTRIM(error_log #>> '{meta,idempotency_key}'), '')
            WHERE trigger_type = 'manual'
              AND idempotency_key IS NULL
            """
        )
        # Preserve one winning row per (user_id, idempotency_key) before creating uniqueness.
        op.execute(
            """
            WITH ranked AS (
                SELECT
                    id,
                    ROW_NUMBER() OVER (
                        PARTITION BY user_id, idempotency_key
                        ORDER BY start_dt DESC, id DESC
                    ) AS rn
                FROM crawl_runs
                WHERE trigger_type = 'manual'
                  AND idempotency_key IS NOT NULL
            )
            UPDATE crawl_runs AS runs
            SET idempotency_key = NULL
            FROM ranked
            WHERE runs.id = ranked.id
              AND ranked.rn > 1
            """
        )

    if INDEX_NAME not in indexes:
        op.create_index(
            INDEX_NAME,
            "crawl_runs",
            ["user_id", "idempotency_key"],
            unique=True,
            postgresql_where=sa.text("idempotency_key IS NOT NULL AND trigger_type = 'manual'::run_trigger_type"),
        )


def downgrade() -> None:
    bind = op.get_bind()
    inspector = sa.inspect(bind)
    columns = {column["name"] for column in inspector.get_columns("crawl_runs")}
    indexes = {index["name"] for index in inspector.get_indexes("crawl_runs")}

    if INDEX_NAME in indexes:
        op.drop_index(INDEX_NAME, table_name="crawl_runs")
    if "idempotency_key" in columns:
        op.drop_column("crawl_runs", "idempotency_key")
