"""Multi-provider authentication: LDAP, SAML, OIDC + TOTP MFA

Revision ID: 009
Revises: 008
Create Date: 2026-05-11 17:30:00.000000

Adds:
- users.auth_provider (enum: local, ldap, saml, oidc)
- users.external_id (string, links to IdP subject/objectGUID)
- users.external_groups (JSON, raw groups from IdP for re-mapping)
- users.totp_secret (encrypted base32, nullable)
- users.totp_enabled (bool)
- users.last_provider_sync (datetime)
- Audit event values: LOGIN_LDAP_SUCCESS, LOGIN_SSO_SUCCESS,
  AUTH_PROVIDER_FAILED, JIT_USER_CREATED, EXTERNAL_ROLE_MAPPED,
  MFA_ENABLED, MFA_DISABLED, MFA_VERIFIED, MFA_FAILED
"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


revision = '009'
down_revision = '008'
branch_labels = None
depends_on = None


def upgrade() -> None:
    # 1. Auth provider enum
    auth_provider_enum = sa.Enum('local', 'ldap', 'saml', 'oidc', name='authprovider')
    auth_provider_enum.create(op.get_bind(), checkfirst=True)

    op.add_column(
        'users',
        sa.Column(
            'auth_provider',
            auth_provider_enum,
            server_default='local',
            nullable=False,
        ),
    )
    op.create_index('ix_users_auth_provider', 'users', ['auth_provider'])

    op.add_column('users', sa.Column('external_id', sa.String(255), nullable=True))
    op.create_index('ix_users_external_id', 'users', ['external_id'])

    op.add_column('users', sa.Column('external_groups', sa.Text(), nullable=True))
    op.add_column('users', sa.Column('last_provider_sync', sa.DateTime(), nullable=True))

    # TOTP
    op.add_column('users', sa.Column('totp_secret', sa.String(255), nullable=True))
    op.add_column(
        'users',
        sa.Column('totp_enabled', sa.Boolean(), server_default='false', nullable=False),
    )

    # 2. New audit event types
    # Postgres requires ALTER TYPE per value.
    new_events = [
        'LOGIN_LDAP_SUCCESS',
        'LOGIN_SSO_SUCCESS',
        'AUTH_PROVIDER_FAILED',
        'JIT_USER_CREATED',
        'EXTERNAL_ROLE_MAPPED',
        'MFA_ENABLED',
        'MFA_DISABLED',
        'MFA_VERIFIED',
        'MFA_FAILED',
        'USER_AUTO_LINKED',
    ]
    for ev in new_events:
        op.execute(f"ALTER TYPE auditeventtype ADD VALUE IF NOT EXISTS '{ev}'")

    # 3. password_hash now nullable (external users have no local password)
    op.alter_column('users', 'password_hash', nullable=True)


def downgrade() -> None:
    op.alter_column('users', 'password_hash', nullable=False)

    op.drop_column('users', 'totp_enabled')
    op.drop_column('users', 'totp_secret')
    op.drop_column('users', 'last_provider_sync')
    op.drop_column('users', 'external_groups')
    op.drop_index('ix_users_external_id', table_name='users')
    op.drop_column('users', 'external_id')
    op.drop_index('ix_users_auth_provider', table_name='users')
    op.drop_column('users', 'auth_provider')

    op.execute('DROP TYPE IF EXISTS authprovider')
    # Note: can't easily DROP enum values in postgres; new audit events remain.
