"""API token permission scopes (apitokens.scopes)

Adds a nullable scopes column to apitokens: a JSON array of permission-name
strings. NULL means the token is unscoped and acts with the full authority of
its owner (the original behavior). A scoped token grants ONLY the listed
permissions, intersected with what the owner actually holds, and suspends the
admin-role bypass so a scoped token minted by an admin is genuinely limited.

Idempotent guard so it is safe on a partially-migrated box; real downgrade.

Revision ID: 7d22_apitokens_scopes
Revises: 7d21_apitokens
Create Date: 2026-07-12

"""
from alembic import op
import sqlalchemy as sa


revision = '7d22_apitokens_scopes'
down_revision = '7d21_apitokens'
branch_labels = None
depends_on = None


def upgrade():
    bind = op.get_bind()
    insp = sa.inspect(bind)

    if 'apitokens' not in insp.get_table_names():
        return
    columns = {c['name'] for c in insp.get_columns('apitokens')}
    if 'scopes' in columns:
        return

    op.add_column('apitokens', sa.Column('scopes', sa.Text(), nullable=True))


def downgrade():
    bind = op.get_bind()
    insp = sa.inspect(bind)

    if 'apitokens' not in insp.get_table_names():
        return
    columns = {c['name'] for c in insp.get_columns('apitokens')}
    if 'scopes' not in columns:
        return

    op.drop_column('apitokens', 'scopes')
