"""202512051405

Revision ID: 20a742ef1d93
Revises: fc9664b8e4a1
Create Date: 2025-12-05 14:24:33.545865

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision: str = '20a742ef1d93'
down_revision: Union[str, None] = 'fc9664b8e4a1'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def table_exists(table_name: str) -> bool:
    """检查表是否存在"""
    bind = op.get_bind()
    inspector = inspect(bind)
    return table_name in inspector.get_table_names()


def column_exists(table_name: str, column_name: str) -> bool:
    """检查列是否存在"""
    bind = op.get_bind()
    inspector = inspect(bind)
    if not table_exists(table_name):
        return False
    columns = [col['name'] for col in inspector.get_columns(table_name)]
    return column_name in columns


def index_exists(table_name: str, index_name: str) -> bool:
    """检查索引是否存在"""
    bind = op.get_bind()
    inspector = inspect(bind)
    if not table_exists(table_name):
        return False
    indexes = [idx['name'] for idx in inspector.get_indexes(table_name)]
    return index_name in indexes


def constraint_exists(table_name: str, constraint_name: str) -> bool:
    """检查约束是否存在（外键、唯一约束等）"""
    bind = op.get_bind()
    inspector = inspect(bind)
    if not table_exists(table_name):
        return False
    
    # 检查外键约束
    foreign_keys = [fk['name'] for fk in inspector.get_foreign_keys(table_name) if fk['name']]
    if constraint_name in foreign_keys:
        return True
    
    # 检查唯一约束
    unique_constraints = [uc['name'] for uc in inspector.get_unique_constraints(table_name) if uc['name']]
    if constraint_name in unique_constraints:
        return True
    
    # 检查检查约束
    check_constraints = [cc['name'] for cc in inspector.get_check_constraints(table_name) if cc['name']]
    if constraint_name in check_constraints:
        return True
    
    return False


def trigger_exists(trigger_name: str) -> bool:
    """检查触发器是否存在（PostgreSQL）"""
    bind = op.get_bind()
    result = bind.execute(sa.text(
        "SELECT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = :trigger_name)"
    ), {"trigger_name": trigger_name})
    return result.scalar()


def sequence_exists(sequence_name: str) -> bool:
    """检查序列是否存在（PostgreSQL）"""
    bind = op.get_bind()
    result = bind.execute(sa.text(
        "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relkind = 'S' AND relname = :sequence_name)"
    ), {"sequence_name": sequence_name})
    return result.scalar()


def enum_exists(enum_name: str) -> bool:
    """检查枚举类型是否存在（PostgreSQL）"""
    bind = op.get_bind()
    result = bind.execute(sa.text(
        "SELECT EXISTS (SELECT 1 FROM pg_type WHERE typname = :enum_name)"
    ), {"enum_name": enum_name})
    return result.scalar()


def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    with op.batch_alter_table('data_config', schema=None) as batch_op:
        batch_op.alter_column('llm',
               existing_type=sa.VARCHAR(length=255),
               comment='LLM模型配置ID',
               existing_comment='LS',
               existing_nullable=True)

    with op.batch_alter_table('end_users', schema=None) as batch_op:
        batch_op.alter_column('id',
               existing_type=sa.UUID(),
               server_default=None,
               existing_nullable=False)

    with op.batch_alter_table('knowledges', schema=None) as batch_op:
        batch_op.alter_column('permission_id',
               existing_type=sa.VARCHAR(),
               comment='permission ID:Private|Share|Memory',
               existing_comment='permission ID:Private|Share',
               existing_nullable=True)

    with op.batch_alter_table('memory_increments', schema=None) as batch_op:
        batch_op.alter_column('id',
               existing_type=sa.UUID(),
               server_default=None,
               existing_nullable=False)
        batch_op.alter_column('created_at',
               existing_type=postgresql.TIMESTAMP(precision=0),
               server_default=None,
               existing_nullable=True)
        batch_op.alter_column('updated_at',
               existing_type=postgresql.TIMESTAMP(precision=0),
               server_default=None,
               existing_nullable=True)

    # 为 model_configs 添加 tenant_id 列和相关约束
    if table_exists('model_configs'):
        # 添加列（分步骤处理 NOT NULL 约束）
        if not column_exists('model_configs', 'tenant_id'):
            # 步骤1: 先添加可空列
            with op.batch_alter_table('model_configs', schema=None) as batch_op:
                batch_op.add_column(sa.Column('tenant_id', sa.UUID(), nullable=True, comment='租户ID'))
            
            # 步骤2: 为现有数据填充默认值
            # 注意：这里需要根据实际业务逻辑填充，这里假设获取第一个租户ID
            bind = op.get_bind()
            result = bind.execute(sa.text("SELECT id FROM tenants LIMIT 1"))
            first_tenant = result.scalar()
            
            if first_tenant:
                # 更新现有记录
                bind.execute(
                    sa.text("UPDATE model_configs SET tenant_id = :tenant_id WHERE tenant_id IS NULL"),
                    {"tenant_id": first_tenant}
                )
            
            # 步骤3: 将列改为 NOT NULL
            with op.batch_alter_table('model_configs', schema=None) as batch_op:
                batch_op.alter_column('tenant_id', nullable=False)
        
        # 创建索引
        if not index_exists('model_configs', 'ix_model_configs_tenant_id'):
            op.create_index('ix_model_configs_tenant_id', 'model_configs', ['tenant_id'], unique=False)
        
        # 创建外键
        if not constraint_exists('model_configs', 'model_configs_tenant_id_fkey'):
            op.create_foreign_key('model_configs_tenant_id_fkey', 'model_configs', 'tenants', ['tenant_id'], ['id'])

    # ### end Alembic commands ###


def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    # 回滚 model_configs 的修改
    if table_exists('model_configs'):
        # 删除外键
        if constraint_exists('model_configs', 'model_configs_tenant_id_fkey'):
            op.drop_constraint('model_configs_tenant_id_fkey', 'model_configs', type_='foreignkey')
        
        # 删除索引
        if index_exists('model_configs', 'ix_model_configs_tenant_id'):
            op.drop_index('ix_model_configs_tenant_id', table_name='model_configs')
        
        # 删除列
        if column_exists('model_configs', 'tenant_id'):
            with op.batch_alter_table('model_configs', schema=None) as batch_op:
                batch_op.drop_column('tenant_id')

    with op.batch_alter_table('memory_increments', schema=None) as batch_op:
        batch_op.alter_column('updated_at',
               existing_type=postgresql.TIMESTAMP(precision=0),
               server_default=sa.text('CURRENT_TIMESTAMP'),
               existing_nullable=True)
        batch_op.alter_column('created_at',
               existing_type=postgresql.TIMESTAMP(precision=0),
               server_default=sa.text('CURRENT_TIMESTAMP'),
               existing_nullable=True)
        batch_op.alter_column('id',
               existing_type=sa.UUID(),
               server_default=sa.text('gen_random_uuid()'),
               existing_nullable=False)

    with op.batch_alter_table('knowledges', schema=None) as batch_op:
        batch_op.alter_column('permission_id',
               existing_type=sa.VARCHAR(),
               comment='permission ID:Private|Share',
               existing_comment='permission ID:Private|Share|Memory',
               existing_nullable=True)

    with op.batch_alter_table('end_users', schema=None) as batch_op:
        batch_op.alter_column('id',
               existing_type=sa.UUID(),
               server_default=sa.text('gen_random_uuid()'),
               existing_nullable=False)

    with op.batch_alter_table('data_config', schema=None) as batch_op:
        batch_op.alter_column('llm',
               existing_type=sa.VARCHAR(length=255),
               comment='LS',
               existing_comment='LLM模型配置ID',
               existing_nullable=True)

    # ### end Alembic commands ###
