"""Prevent duplicate active file-folder paths.

Revision ID: 6a7b8c9d0e1f
Revises: 5f6a7b8c9d0e
"""

from __future__ import annotations

from collections import defaultdict
import sqlalchemy as sa
from alembic import op
from sqlalchemy import inspect

revision = "6a7b8c9d0e1f"
down_revision = "5f6a7b8c9d0e"
branch_labels = None
depends_on = None

USER_INDEX = "uq_file_folders_active_user_path"
GROUP_INDEX = "uq_file_folders_active_group_path"


def _deduplicate_active_folders() -> None:
    bind = op.get_bind()
    rows = bind.execute(sa.text("""
        SELECT id, tenant_id, owner_type, owner_user_id, owner_group_id, path, created_at
        FROM file_folders
        WHERE deleted_at IS NULL
        ORDER BY created_at ASC, id ASC
    """)).mappings().all()
    grouped: dict[tuple[object, ...], list[str]] = defaultdict(list)
    for row in rows:
        owner_id = row["owner_user_id"] if row["owner_type"] == "user" else row["owner_group_id"]
        grouped[(row["tenant_id"], row["owner_type"], owner_id, row["path"])].append(row["id"])
    duplicate_ids = [folder_id for ids in grouped.values() for folder_id in ids[1:]]
    if duplicate_ids:
        bind.execute(
            sa.text("UPDATE file_folders SET deleted_at = CURRENT_TIMESTAMP WHERE id IN :ids").bindparams(
                sa.bindparam("ids", expanding=True)
            ),
            {"ids": duplicate_ids},
        )


def upgrade() -> None:
    _deduplicate_active_folders()
    existing = {item["name"] for item in inspect(op.get_bind()).get_indexes("file_folders")}
    if USER_INDEX not in existing:
        op.create_index(
            USER_INDEX,
            "file_folders",
            ["tenant_id", "owner_user_id", "path"],
            unique=True,
            sqlite_where=sa.text("owner_type = 'user' AND deleted_at IS NULL"),
            postgresql_where=sa.text("owner_type = 'user' AND deleted_at IS NULL"),
        )
    if GROUP_INDEX not in existing:
        op.create_index(
            GROUP_INDEX,
            "file_folders",
            ["tenant_id", "owner_group_id", "path"],
            unique=True,
            sqlite_where=sa.text("owner_type = 'group' AND deleted_at IS NULL"),
            postgresql_where=sa.text("owner_type = 'group' AND deleted_at IS NULL"),
        )


def downgrade() -> None:
    existing = {item["name"] for item in inspect(op.get_bind()).get_indexes("file_folders")}
    if GROUP_INDEX in existing:
        op.drop_index(GROUP_INDEX, table_name="file_folders")
    if USER_INDEX in existing:
        op.drop_index(USER_INDEX, table_name="file_folders")
