"""Add dashboarddefaults.displayrole; make businessunitid nullable

One "display" PC image resolves its role (dashboard / lobby / partskiosk) from
its IP. Existing rows are dashboard mappings, so displayrole defaults to
'dashboard'. Lobby / kiosk roles need no business unit, so businessunitid
becomes nullable.

Idempotent guards on column presence / nullability.

Revision ID: 7d28_dashboarddefault_displayrole
Revises: 7d27_roles_color
Create Date: 2026-07-21

"""
from alembic import op
import sqlalchemy as sa


revision = '7d28_dashboarddefault_displayrole'
down_revision = '7d27_roles_color'
branch_labels = None
depends_on = None


def _cols(insp):
    return {c['name']: c for c in insp.get_columns('dashboarddefaults')}


def upgrade():
    bind = op.get_bind()
    insp = sa.inspect(bind)
    if 'dashboarddefaults' not in insp.get_table_names():
        return
    cols = _cols(insp)

    if 'displayrole' not in cols:
        op.add_column('dashboarddefaults', sa.Column(
            'displayrole', sa.String(length=20),
            nullable=False, server_default='dashboard'))

    if 'businessunitid' in cols and not cols['businessunitid']['nullable']:
        op.alter_column('dashboarddefaults', 'businessunitid',
                        existing_type=sa.Integer(), nullable=True)


def downgrade():
    bind = op.get_bind()
    insp = sa.inspect(bind)
    if 'dashboarddefaults' not in insp.get_table_names():
        return
    cols = _cols(insp)

    # Only restore NOT NULL if no rows would violate it.
    if 'businessunitid' in cols and cols['businessunitid']['nullable']:
        nulls = bind.execute(sa.text(
            'SELECT COUNT(*) FROM dashboarddefaults '
            'WHERE businessunitid IS NULL')).scalar()
        if not nulls:
            op.alter_column('dashboarddefaults', 'businessunitid',
                            existing_type=sa.Integer(), nullable=False)

    if 'displayrole' in cols:
        op.drop_column('dashboarddefaults', 'displayrole')
