"""rename tenancy scope table to core scopes

Revision ID: 4f2a9c8e7b6d
Revises: 3f4a5b6c7d8e
Create Date: 2026-07-10 00:00:00.000000
"""
from __future__ import annotations

from alembic import op
import sqlalchemy as sa


revision = "4f2a9c8e7b6d"
down_revision = "3f4a5b6c7d8e"
branch_labels = None
depends_on = None

LEGACY_SCOPE_TABLE = "tenancy_tenants"
CORE_SCOPE_TABLE = "core_scopes"
LEGACY_SLUG_INDEX = "ix_tenancy_tenants_slug"
CORE_SLUG_INDEX = "ix_core_scopes_slug"
_KNOWN_SCOPE_TABLES = {LEGACY_SCOPE_TABLE, CORE_SCOPE_TABLE}


def _row_count(bind: sa.Connection, table_name: str) -> int:
    if table_name not in _KNOWN_SCOPE_TABLES:
        raise RuntimeError(f"Unexpected scope table name: {table_name}")
    quoted = bind.dialect.identifier_preparer.quote(table_name)
    return int(bind.execute(sa.text(f"SELECT COUNT(*) FROM {quoted}")).scalar_one())  # nosemgrep: python.sqlalchemy.security.audit.avoid-sqlalchemy-text.avoid-sqlalchemy-text


def _scope_tables(bind: sa.Connection) -> set[str]:
    return set(sa.inspect(bind).get_table_names())


def _drop_table_if_empty(bind: sa.Connection, table_name: str) -> bool:
    if _row_count(bind, table_name) != 0:
        return False
    op.drop_table(table_name)
    return True


def _ensure_slug_index(bind: sa.Connection, table_name: str, index_name: str, old_index_name: str) -> None:
    indexes = {index["name"] for index in sa.inspect(bind).get_indexes(table_name)}
    if old_index_name in indexes:
        op.drop_index(old_index_name, table_name=table_name)
        indexes.remove(old_index_name)
    if index_name not in indexes:
        op.create_index(op.f(index_name), table_name, ["slug"], unique=True)


def _rename_scope_table(old_name: str, new_name: str) -> None:
    bind = op.get_bind()
    tables = _scope_tables(bind)
    if old_name not in tables:
        if new_name in tables:
            _ensure_slug_index(bind, new_name, CORE_SLUG_INDEX if new_name == CORE_SCOPE_TABLE else LEGACY_SLUG_INDEX, LEGACY_SLUG_INDEX if new_name == CORE_SCOPE_TABLE else CORE_SLUG_INDEX)
        return

    if new_name in tables:
        if _drop_table_if_empty(bind, new_name):
            tables.remove(new_name)
        elif _drop_table_if_empty(bind, old_name):
            _ensure_slug_index(bind, new_name, CORE_SLUG_INDEX if new_name == CORE_SCOPE_TABLE else LEGACY_SLUG_INDEX, LEGACY_SLUG_INDEX if new_name == CORE_SCOPE_TABLE else CORE_SLUG_INDEX)
            return
        else:
            raise RuntimeError(f"Cannot reconcile non-empty {old_name} over non-empty {new_name}")

    if old_name in tables and new_name not in tables:
        op.rename_table(old_name, new_name)
        _ensure_slug_index(bind, new_name, CORE_SLUG_INDEX if new_name == CORE_SCOPE_TABLE else LEGACY_SLUG_INDEX, LEGACY_SLUG_INDEX if new_name == CORE_SCOPE_TABLE else CORE_SLUG_INDEX)


def upgrade() -> None:
    _rename_scope_table(LEGACY_SCOPE_TABLE, CORE_SCOPE_TABLE)


def downgrade() -> None:
    _rename_scope_table(CORE_SCOPE_TABLE, LEGACY_SCOPE_TABLE)
