"""namespace platform-owned tables

Revision ID: 2e3f4a5b6c7d
Revises: 1b2c3d4e5f70
Create Date: 2026-07-09 00:00:00.000000
"""
from __future__ import annotations

from alembic import op
import sqlalchemy as sa

revision = "2e3f4a5b6c7d"
down_revision = "1b2c3d4e5f70"
branch_labels = None
depends_on = None

_TABLE_RENAMES = (
    ("tenants", "tenancy_tenants"),
    ("accounts", "access_accounts"),
    ("users", "access_users"),
    ("groups", "access_groups"),
    ("roles", "access_roles"),
    ("system_role_assignments", "access_system_role_assignments"),
    ("user_group_memberships", "access_user_group_memberships"),
    ("user_role_assignments", "access_user_role_assignments"),
    ("group_role_assignments", "access_group_role_assignments"),
    ("api_keys", "access_api_keys"),
    ("auth_sessions", "access_auth_sessions"),
    ("system_settings", "core_system_settings"),
    ("governance_templates", "admin_governance_templates"),
    ("governance_template_assignments", "admin_governance_template_assignments"),
)
_KNOWN_TABLE_NAMES = {name for pair in _TABLE_RENAMES for name in pair}


def _row_count(bind: sa.Connection, table_name: str) -> int:
    if table_name not in _KNOWN_TABLE_NAMES:
        raise RuntimeError(f"Unexpected 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 _rename_tables(renames: tuple[tuple[str, str], ...]) -> None:
    bind = op.get_bind()
    inspector = sa.inspect(bind)
    tables = set(inspector.get_table_names())

    old_tables_to_drop: set[str] = set()
    new_tables_to_drop: set[str] = set()
    for old_name, new_name in renames:
        if old_name not in tables or new_name not in tables:
            continue
        if _row_count(bind, old_name) == 0:
            old_tables_to_drop.add(old_name)
        elif _row_count(bind, new_name) == 0:
            new_tables_to_drop.add(new_name)
        else:
            raise RuntimeError(f"Cannot rename non-empty {old_name} over non-empty {new_name}")

    for old_name, _new_name in reversed(renames):
        if old_name in old_tables_to_drop:
            op.drop_table(old_name)
            tables.remove(old_name)
    for _old_name, new_name in reversed(renames):
        if new_name in new_tables_to_drop:
            op.drop_table(new_name)
            tables.remove(new_name)

    for old_name, new_name in renames:
        if old_name not in tables:
            continue
        op.rename_table(old_name, new_name)
        tables.remove(old_name)
        tables.add(new_name)


def upgrade() -> None:
    _rename_tables(_TABLE_RENAMES)


def downgrade() -> None:
    _rename_tables(tuple((new_name, old_name) for old_name, new_name in reversed(_TABLE_RENAMES)))
