"""initial_schema

Revision ID: e56e408b2208
Revises: 
Create Date: 2026-07-06 23:57:31.500925
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa


revision: str = 'e56e408b2208'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('api_keys',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('tenant_id', sa.Uuid(), nullable=False),
    sa.Column('user_id', sa.Uuid(), nullable=False),
    sa.Column('name', sa.String(length=200), nullable=False),
    sa.Column('key_prefix', sa.String(length=10), nullable=False),
    sa.Column('key_hash', sa.String(length=128), nullable=False),
    sa.Column('scopes', sa.JSON(), nullable=False),
    sa.Column('last_used_at', sa.DateTime(timezone=True), nullable=True),
    sa.Column('expires_at', sa.DateTime(timezone=True), nullable=True),
    sa.Column('is_active', sa.Boolean(), nullable=False),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_ak_tenant_active', 'api_keys', ['tenant_id', 'is_active'], unique=False)
    op.create_table('api_usage_logs',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('api_key_id', sa.Uuid(), nullable=True),
    sa.Column('tenant_id', sa.Uuid(), nullable=False),
    sa.Column('endpoint', sa.String(length=300), nullable=False),
    sa.Column('method', sa.String(length=10), nullable=False),
    sa.Column('status_code', sa.Integer(), nullable=True),
    sa.Column('response_ms', sa.Integer(), nullable=True),
    sa.Column('ip_address', sa.String(length=45), nullable=True),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_aul_tenant', 'api_usage_logs', ['tenant_id'], unique=False)
    op.create_index('ix_aul_tenant_created', 'api_usage_logs', ['tenant_id', 'created_at'], unique=False)
    op.create_table('export_jobs',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('user_id', sa.Uuid(), nullable=False),
    sa.Column('tenant_id', sa.Uuid(), nullable=False),
    sa.Column('export_format', sa.String(length=10), nullable=False),
    sa.Column('citation_style', sa.String(length=50), nullable=True),
    sa.Column('filter_params', sa.JSON(), nullable=False),
    sa.Column('status', sa.String(length=20), nullable=False),
    sa.Column('file_url', sa.String(length=1000), nullable=True),
    sa.Column('file_size_bytes', sa.Integer(), nullable=True),
    sa.Column('error_message', sa.Text(), nullable=True),
    sa.Column('total_items', sa.Integer(), nullable=True),
    sa.Column('completed_at', sa.DateTime(timezone=True), nullable=True),
    sa.Column('expires_at', sa.DateTime(timezone=True), nullable=True),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_ej_user_status', 'export_jobs', ['user_id', 'status'], unique=False)
    op.create_table('global_journals',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('name', sa.String(length=500), nullable=False),
    sa.Column('issn', sa.String(length=20), nullable=True),
    sa.Column('eissn', sa.String(length=20), nullable=True),
    sa.Column('tier', sa.String(length=5), nullable=False),
    sa.Column('impact_factor', sa.Float(), nullable=True),
    sa.Column('publisher', sa.String(length=300), nullable=True),
    sa.Column('article_count', sa.Integer(), nullable=False),
    sa.Column('is_active', sa.Boolean(), nullable=False),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_gj_tier', 'global_journals', ['tier'], unique=False)
    op.create_table('global_literature',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('pmid', sa.Integer(), nullable=False),
    sa.Column('title', sa.Text(), nullable=False),
    sa.Column('abstract', sa.Text(), nullable=True),
    sa.Column('authors', sa.JSON(), nullable=False),
    sa.Column('doi', sa.String(length=500), nullable=True),
    sa.Column('journal', sa.String(length=500), nullable=True),
    sa.Column('journal_issn', sa.String(length=20), nullable=True),
    sa.Column('volume', sa.String(length=50), nullable=True),
    sa.Column('issue', sa.String(length=50), nullable=True),
    sa.Column('pages', sa.String(length=100), nullable=True),
    sa.Column('pub_date', sa.Date(), nullable=True),
    sa.Column('pub_year', sa.Integer(), nullable=True),
    sa.Column('pub_types', sa.JSON(), nullable=False),
    sa.Column('mesh_headings', sa.JSON(), nullable=False),
    sa.Column('language', sa.String(length=10), nullable=False),
    sa.Column('raw_xml_hash', sa.String(length=64), nullable=True),
    sa.Column('pmc_id', sa.String(length=20), nullable=True),
    sa.Column('is_oa', sa.Boolean(), nullable=False),
    sa.Column('keywords', sa.JSON(), nullable=False),
    sa.Column('pubmed_revised', sa.DateTime(timezone=True), nullable=True),
    sa.Column('grants', sa.JSON(), nullable=False),
    sa.Column('cited_by_count', sa.Integer(), nullable=False),
    sa.Column('source', sa.String(length=30), nullable=False),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('pmid')
    )
    op.create_index('ix_gl_created', 'global_literature', ['created_at'], unique=False)
    op.create_index('ix_gl_journal_issn', 'global_literature', ['journal_issn'], unique=False)
    op.create_index('ix_gl_pmc_id', 'global_literature', ['pmc_id'], unique=False)
    op.create_index('ix_gl_pub_date', 'global_literature', ['pub_date'], unique=False)
    op.create_index('ix_gl_pub_year', 'global_literature', ['pub_year'], unique=False)
    op.create_table('global_tags',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('mesh_ui', sa.String(length=20), nullable=True),
    sa.Column('name_zh', sa.String(length=200), nullable=False),
    sa.Column('name_en', sa.String(length=200), nullable=True),
    sa.Column('path', sa.String(length=500), nullable=False),
    sa.Column('level', sa.Integer(), nullable=False),
    sa.Column('parent_id', sa.Uuid(), nullable=True),
    sa.Column('tree_number', sa.String(length=100), nullable=True),
    sa.Column('tag_category', sa.String(length=30), nullable=False),
    sa.Column('sort_order', sa.Integer(), nullable=False),
    sa.Column('icon', sa.String(length=50), nullable=True),
    sa.Column('is_selectable', sa.Boolean(), nullable=False),
    sa.Column('article_count', sa.Integer(), nullable=False),
    sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['parent_id'], ['global_tags.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_gt_category', 'global_tags', ['tag_category'], unique=False)
    op.create_index('ix_gt_mesh_ui', 'global_tags', ['mesh_ui'], unique=False)
    op.create_index('ix_gt_path', 'global_tags', ['path'], unique=False)
    op.create_table('payment_orders',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('tenant_id', sa.Uuid(), nullable=False),
    sa.Column('user_id', sa.Uuid(), nullable=False),
    sa.Column('plan', sa.String(length=30), nullable=False),
    sa.Column('amount', sa.Integer(), nullable=False),
    sa.Column('status', sa.String(length=20), nullable=False),
    sa.Column('out_trade_no', sa.String(length=64), nullable=False),
    sa.Column('prepay_id', sa.String(length=128), nullable=True),
    sa.Column('code_url', sa.String(length=512), nullable=True),
    sa.Column('pay_time', sa.DateTime(timezone=True), nullable=True),
    sa.Column('transaction_id', sa.String(length=64), nullable=True),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('out_trade_no')
    )
    op.create_index(op.f('ix_payment_orders_tenant_id'), 'payment_orders', ['tenant_id'], unique=False)
    op.create_table('pipeline_runs',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('run_type', sa.String(length=20), nullable=False),
    sa.Column('status', sa.String(length=20), nullable=False),
    sa.Column('files_processed', sa.Integer(), nullable=False),
    sa.Column('articles_total', sa.Integer(), nullable=False),
    sa.Column('articles_new', sa.Integer(), nullable=False),
    sa.Column('articles_updated', sa.Integer(), nullable=False),
    sa.Column('articles_deleted', sa.Integer(), nullable=False),
    sa.Column('articles_filtered', sa.Integer(), nullable=False),
    sa.Column('users_matched', sa.Integer(), nullable=False),
    sa.Column('feeds_generated', sa.Integer(), nullable=False),
    sa.Column('error_log', sa.Text(), nullable=True),
    sa.Column('started_at', sa.DateTime(timezone=True), nullable=True),
    sa.Column('completed_at', sa.DateTime(timezone=True), nullable=True),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('subscription_events',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('tenant_id', sa.Uuid(), nullable=False),
    sa.Column('event_type', sa.String(length=30), nullable=False),
    sa.Column('from_plan', sa.String(length=30), nullable=True),
    sa.Column('to_plan', sa.String(length=30), nullable=True),
    sa.Column('stripe_event_id', sa.String(length=100), nullable=True),
    sa.Column('metadata', sa.JSON(), nullable=False),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_se_tenant', 'subscription_events', ['tenant_id'], unique=False)
    op.create_table('system_notifications',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('title', sa.String(length=300), nullable=False),
    sa.Column('content', sa.Text(), nullable=True),
    sa.Column('target_type', sa.String(length=20), nullable=False),
    sa.Column('target_id', sa.Uuid(), nullable=True),
    sa.Column('is_read', sa.Boolean(), nullable=False),
    sa.Column('read_at', sa.DateTime(timezone=True), nullable=True),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('tenant_subscriptions',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('tenant_id', sa.Uuid(), nullable=False),
    sa.Column('stripe_customer_id', sa.String(length=100), nullable=True),
    sa.Column('stripe_subscription_id', sa.String(length=100), nullable=True),
    sa.Column('plan_id', sa.String(length=50), nullable=True),
    sa.Column('status', sa.String(length=30), nullable=False),
    sa.Column('current_period_start', sa.DateTime(timezone=True), nullable=False),
    sa.Column('current_period_end', sa.DateTime(timezone=True), nullable=False),
    sa.Column('cancel_at_period_end', sa.Boolean(), nullable=False),
    sa.Column('trial_end', sa.DateTime(timezone=True), nullable=True),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('tenant_id')
    )
    op.create_index('ix_ts_stripe_sub', 'tenant_subscriptions', ['stripe_subscription_id'], unique=False)
    op.create_table('tenants',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('name', sa.String(length=200), nullable=False),
    sa.Column('slug', sa.String(length=100), nullable=False),
    sa.Column('hospital_name', sa.String(length=200), nullable=True),
    sa.Column('department_name', sa.String(length=200), nullable=True),
    sa.Column('plan_type', sa.String(length=30), nullable=False),
    sa.Column('is_personal', sa.Boolean(), nullable=False),
    sa.Column('status', sa.String(length=20), nullable=False),
    sa.Column('logo_url', sa.String(length=500), nullable=True),
    sa.Column('primary_color', sa.String(length=7), nullable=False),
    sa.Column('settings', sa.JSON(), nullable=False),
    sa.Column('max_members', sa.Integer(), nullable=False),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('slug')
    )
    op.create_table('users',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('email', sa.String(length=320), nullable=False),
    sa.Column('hashed_password', sa.String(length=128), nullable=True),
    sa.Column('display_name', sa.String(length=150), nullable=False),
    sa.Column('title', sa.String(length=100), nullable=True),
    sa.Column('avatar_url', sa.String(length=500), nullable=True),
    sa.Column('phone', sa.String(length=30), nullable=True),
    sa.Column('is_active', sa.Boolean(), nullable=False),
    sa.Column('is_superuser', sa.Boolean(), nullable=False),
    sa.Column('sso_provider', sa.String(length=20), nullable=True),
    sa.Column('sso_subject_id', sa.String(length=255), nullable=True),
    sa.Column('last_login_at', sa.DateTime(timezone=True), nullable=True),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('email')
    )
    op.create_table('approval_workflows',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('tenant_id', sa.Uuid(), nullable=False),
    sa.Column('literature_id', sa.Uuid(), nullable=False),
    sa.Column('submitted_by', sa.Uuid(), nullable=False),
    sa.Column('status', sa.String(length=20), nullable=False),
    sa.Column('current_step', sa.Integer(), nullable=False),
    sa.Column('total_steps', sa.Integer(), nullable=False),
    sa.Column('comment', sa.Text(), nullable=True),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['literature_id'], ['global_literature.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['submitted_by'], ['users.id'], ),
    sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('drug_approvals',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('drug_name', sa.String(length=500), nullable=False),
    sa.Column('generic_name', sa.String(length=500), nullable=False),
    sa.Column('target', sa.String(length=200), nullable=True),
    sa.Column('indication', sa.Text(), nullable=False),
    sa.Column('cancer_type_tag_id', sa.Uuid(), nullable=True),
    sa.Column('approval_agency', sa.String(length=10), nullable=False),
    sa.Column('approval_type', sa.String(length=30), nullable=True),
    sa.Column('approval_date', sa.Date(), nullable=False),
    sa.Column('pivotal_trial_nct', sa.String(length=20), nullable=True),
    sa.Column('pivotal_trial_pmid', sa.Integer(), nullable=True),
    sa.Column('source_url', sa.String(length=1000), nullable=True),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['cancer_type_tag_id'], ['global_tags.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('global_literature_tags',
    sa.Column('literature_id', sa.Uuid(), nullable=False),
    sa.Column('tag_id', sa.Uuid(), nullable=False),
    sa.Column('is_major', sa.Boolean(), nullable=False),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['literature_id'], ['global_literature.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['tag_id'], ['global_tags.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('literature_id', 'tag_id')
    )
    op.create_index('ix_glt_tag', 'global_literature_tags', ['tag_id'], unique=False)
    op.create_table('guideline_versions',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('guideline_source', sa.String(length=10), nullable=False),
    sa.Column('cancer_type_tag_id', sa.Uuid(), nullable=False),
    sa.Column('version', sa.String(length=30), nullable=False),
    sa.Column('publish_date', sa.Date(), nullable=False),
    sa.Column('change_summary_zh', sa.Text(), nullable=True),
    sa.Column('change_summary_en', sa.Text(), nullable=True),
    sa.Column('pdf_url', sa.String(length=1000), nullable=True),
    sa.Column('source_url', sa.String(length=1000), nullable=True),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['cancer_type_tag_id'], ['global_tags.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('invitations',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('tenant_id', sa.Uuid(), nullable=False),
    sa.Column('inviter_id', sa.Uuid(), nullable=False),
    sa.Column('email', sa.String(length=320), nullable=False),
    sa.Column('role', sa.String(length=20), nullable=False),
    sa.Column('token', sa.String(length=200), nullable=False),
    sa.Column('status', sa.String(length=20), nullable=False),
    sa.Column('expires_at', sa.DateTime(timezone=True), nullable=False),
    sa.Column('accepted_at', sa.DateTime(timezone=True), nullable=True),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['inviter_id'], ['users.id'], ),
    sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('token')
    )
    op.create_table('journal_club_queue',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('tenant_id', sa.Uuid(), nullable=False),
    sa.Column('literature_id', sa.Uuid(), nullable=False),
    sa.Column('suggested_by', sa.Uuid(), nullable=False),
    sa.Column('title', sa.String(length=500), nullable=True),
    sa.Column('scheduled_date', sa.Date(), nullable=True),
    sa.Column('presenter_id', sa.Uuid(), nullable=True),
    sa.Column('status', sa.String(length=20), nullable=False),
    sa.Column('discussion_notes', sa.Text(), nullable=True),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['literature_id'], ['global_literature.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['presenter_id'], ['users.id'], ),
    sa.ForeignKeyConstraint(['suggested_by'], ['users.id'], ),
    sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('login_logs',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('user_id', sa.Uuid(), nullable=False),
    sa.Column('tenant_id', sa.Uuid(), nullable=True),
    sa.Column('ip_address', sa.String(length=45), nullable=True),
    sa.Column('user_agent', sa.String(length=500), nullable=True),
    sa.Column('success', sa.Boolean(), nullable=False),
    sa.Column('fail_reason', sa.String(length=200), nullable=True),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ondelete='SET NULL'),
    sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_ll_user_created', 'login_logs', ['user_id', 'created_at'], unique=False)
    op.create_table('teams',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('tenant_id', sa.Uuid(), nullable=False),
    sa.Column('name', sa.String(length=200), nullable=False),
    sa.Column('description', sa.Text(), nullable=True),
    sa.Column('created_by', sa.Uuid(), nullable=False),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['created_by'], ['users.id'], ),
    sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('user_feed',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('user_id', sa.Uuid(), nullable=False),
    sa.Column('literature_id', sa.Uuid(), nullable=False),
    sa.Column('matched_tags', sa.JSON(), nullable=False),
    sa.Column('priority', sa.String(length=10), nullable=False),
    sa.Column('is_read', sa.Boolean(), nullable=False),
    sa.Column('read_at', sa.DateTime(timezone=True), nullable=True),
    sa.Column('is_dismissed', sa.Boolean(), nullable=False),
    sa.Column('pushed_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['literature_id'], ['global_literature.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_uf_user_lit', 'user_feed', ['user_id', 'literature_id'], unique=False)
    op.create_index('ix_uf_user_priority', 'user_feed', ['user_id', 'is_dismissed', 'priority'], unique=False)
    op.create_index('ix_uf_user_push', 'user_feed', ['user_id', 'pushed_at'], unique=False)
    op.create_table('user_feedback',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('user_id', sa.Uuid(), nullable=False),
    sa.Column('literature_id', sa.Uuid(), nullable=True),
    sa.Column('feedback_type', sa.String(length=30), nullable=False),
    sa.Column('detail', sa.Text(), nullable=True),
    sa.Column('is_resolved', sa.Boolean(), nullable=False),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['literature_id'], ['global_literature.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('user_folders',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('user_id', sa.Uuid(), nullable=False),
    sa.Column('tenant_id', sa.Uuid(), nullable=False),
    sa.Column('name', sa.String(length=200), nullable=False),
    sa.Column('description', sa.Text(), nullable=True),
    sa.Column('color', sa.String(length=7), nullable=False),
    sa.Column('is_shared', sa.Boolean(), nullable=False),
    sa.Column('sort_order', sa.Integer(), nullable=False),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('user_notes',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('user_id', sa.Uuid(), nullable=False),
    sa.Column('literature_id', sa.Uuid(), nullable=False),
    sa.Column('content', sa.Text(), nullable=False),
    sa.Column('quote_text', sa.Text(), nullable=True),
    sa.Column('is_private', sa.Boolean(), nullable=False),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['literature_id'], ['global_literature.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_un_literature', 'user_notes', ['literature_id'], unique=False)
    op.create_index('ix_un_user', 'user_notes', ['user_id', 'created_at'], unique=False)
    op.create_table('user_pdf_highlights',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('user_id', sa.Uuid(), nullable=False),
    sa.Column('literature_id', sa.Uuid(), nullable=False),
    sa.Column('highlight_type', sa.String(length=20), nullable=False),
    sa.Column('page_number', sa.Integer(), nullable=False),
    sa.Column('position', sa.JSON(), nullable=False),
    sa.Column('text', sa.Text(), nullable=True),
    sa.Column('note', sa.Text(), nullable=True),
    sa.Column('color', sa.String(length=7), nullable=False),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['literature_id'], ['global_literature.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_hl_user_lit', 'user_pdf_highlights', ['user_id', 'literature_id'], unique=False)
    op.create_table('user_subscriptions',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('user_id', sa.Uuid(), nullable=False),
    sa.Column('tenant_id', sa.Uuid(), nullable=False),
    sa.Column('tag_id', sa.Uuid(), nullable=False),
    sa.Column('match_mode', sa.String(length=10), nullable=False),
    sa.Column('is_active', sa.Boolean(), nullable=False),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['tag_id'], ['global_tags.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_us_tag_active', 'user_subscriptions', ['tag_id', 'is_active'], unique=False)
    op.create_index('ix_us_user_active', 'user_subscriptions', ['user_id', 'is_active'], unique=False)
    op.create_table('user_tenants',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('user_id', sa.Uuid(), nullable=False),
    sa.Column('tenant_id', sa.Uuid(), nullable=False),
    sa.Column('role', sa.String(length=20), nullable=False),
    sa.Column('is_default', sa.Boolean(), nullable=False),
    sa.Column('joined_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_ut_user', 'user_tenants', ['user_id'], unique=False)
    op.create_index('ix_ut_user_tenant', 'user_tenants', ['user_id', 'tenant_id'], unique=False)
    op.create_table('approval_steps',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('workflow_id', sa.Uuid(), nullable=False),
    sa.Column('step_order', sa.Integer(), nullable=False),
    sa.Column('approver_id', sa.Uuid(), nullable=False),
    sa.Column('status', sa.String(length=20), nullable=False),
    sa.Column('comment', sa.Text(), nullable=True),
    sa.Column('decided_at', sa.DateTime(timezone=True), nullable=True),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['approver_id'], ['users.id'], ),
    sa.ForeignKeyConstraint(['workflow_id'], ['approval_workflows.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('guideline_evidence',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('guideline_version_id', sa.Uuid(), nullable=False),
    sa.Column('literature_id', sa.Uuid(), nullable=False),
    sa.Column('recommendation', sa.Text(), nullable=True),
    sa.Column('recommendation_level', sa.String(length=10), nullable=True),
    sa.Column('treatment_line', sa.String(length=20), nullable=True),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['guideline_version_id'], ['guideline_versions.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['literature_id'], ['global_literature.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('shared_folders',
    sa.Column('folder_id', sa.Uuid(), nullable=False),
    sa.Column('target_type', sa.String(length=20), nullable=False),
    sa.Column('target_id', sa.Uuid(), nullable=False),
    sa.Column('permission', sa.String(length=20), nullable=False),
    sa.Column('created_by', sa.Uuid(), nullable=False),
    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['created_by'], ['users.id'], ),
    sa.ForeignKeyConstraint(['folder_id'], ['user_folders.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('folder_id', 'target_type', 'target_id')
    )
    op.create_table('team_members',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('team_id', sa.Uuid(), nullable=False),
    sa.Column('user_id', sa.Uuid(), nullable=False),
    sa.Column('role', sa.String(length=20), nullable=False),
    sa.Column('joined_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.ForeignKeyConstraint(['team_id'], ['teams.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_tm_team', 'team_members', ['team_id'], unique=False)
    op.create_index('ix_tm_user_team', 'team_members', ['user_id', 'team_id'], unique=False)
    op.create_table('user_literature',
    sa.Column('id', sa.Uuid(), nullable=False),
    sa.Column('user_id', sa.Uuid(), nullable=False),
    sa.Column('literature_id', sa.Uuid(), nullable=False),
    sa.Column('folder_id', sa.Uuid(), nullable=True),
    sa.Column('reading_status', sa.String(length=20), nullable=False),
    sa.Column('rating', sa.Integer(), nullable=True),
    sa.Column('personal_tags', sa.JSON(), nullable=False),
    sa.Column('saved_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=False),
    sa.Column('read_started_at', sa.DateTime(timezone=True), nullable=True),
    sa.Column('read_finished_at', sa.DateTime(timezone=True), nullable=True),
    sa.CheckConstraint('rating >= 1 AND rating <= 5', name='ck_rating'),
    sa.ForeignKeyConstraint(['folder_id'], ['user_folders.id'], ondelete='SET NULL'),
    sa.ForeignKeyConstraint(['literature_id'], ['global_literature.id'], ondelete='CASCADE'),
    sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index('ix_ul_user_saved', 'user_literature', ['user_id', 'saved_at'], unique=False)
    # ### end Alembic commands ###


def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_index('ix_ul_user_saved', table_name='user_literature')
    op.drop_table('user_literature')
    op.drop_index('ix_tm_user_team', table_name='team_members')
    op.drop_index('ix_tm_team', table_name='team_members')
    op.drop_table('team_members')
    op.drop_table('shared_folders')
    op.drop_table('guideline_evidence')
    op.drop_table('approval_steps')
    op.drop_index('ix_ut_user_tenant', table_name='user_tenants')
    op.drop_index('ix_ut_user', table_name='user_tenants')
    op.drop_table('user_tenants')
    op.drop_index('ix_us_user_active', table_name='user_subscriptions')
    op.drop_index('ix_us_tag_active', table_name='user_subscriptions')
    op.drop_table('user_subscriptions')
    op.drop_index('ix_hl_user_lit', table_name='user_pdf_highlights')
    op.drop_table('user_pdf_highlights')
    op.drop_index('ix_un_user', table_name='user_notes')
    op.drop_index('ix_un_literature', table_name='user_notes')
    op.drop_table('user_notes')
    op.drop_table('user_folders')
    op.drop_table('user_feedback')
    op.drop_index('ix_uf_user_push', table_name='user_feed')
    op.drop_index('ix_uf_user_priority', table_name='user_feed')
    op.drop_index('ix_uf_user_lit', table_name='user_feed')
    op.drop_table('user_feed')
    op.drop_table('teams')
    op.drop_index('ix_ll_user_created', table_name='login_logs')
    op.drop_table('login_logs')
    op.drop_table('journal_club_queue')
    op.drop_table('invitations')
    op.drop_table('guideline_versions')
    op.drop_index('ix_glt_tag', table_name='global_literature_tags')
    op.drop_table('global_literature_tags')
    op.drop_table('drug_approvals')
    op.drop_table('approval_workflows')
    op.drop_table('users')
    op.drop_table('tenants')
    op.drop_index('ix_ts_stripe_sub', table_name='tenant_subscriptions')
    op.drop_table('tenant_subscriptions')
    op.drop_table('system_notifications')
    op.drop_index('ix_se_tenant', table_name='subscription_events')
    op.drop_table('subscription_events')
    op.drop_table('pipeline_runs')
    op.drop_index(op.f('ix_payment_orders_tenant_id'), table_name='payment_orders')
    op.drop_table('payment_orders')
    op.drop_index('ix_gt_path', table_name='global_tags')
    op.drop_index('ix_gt_mesh_ui', table_name='global_tags')
    op.drop_index('ix_gt_category', table_name='global_tags')
    op.drop_table('global_tags')
    op.drop_index('ix_gl_pub_year', table_name='global_literature')
    op.drop_index('ix_gl_pub_date', table_name='global_literature')
    op.drop_index('ix_gl_pmc_id', table_name='global_literature')
    op.drop_index('ix_gl_journal_issn', table_name='global_literature')
    op.drop_index('ix_gl_created', table_name='global_literature')
    op.drop_table('global_literature')
    op.drop_index('ix_gj_tier', table_name='global_journals')
    op.drop_table('global_journals')
    op.drop_index('ix_ej_user_status', table_name='export_jobs')
    op.drop_table('export_jobs')
    op.drop_index('ix_aul_tenant_created', table_name='api_usage_logs')
    op.drop_index('ix_aul_tenant', table_name='api_usage_logs')
    op.drop_table('api_usage_logs')
    op.drop_index('ix_ak_tenant_active', table_name='api_keys')
    op.drop_table('api_keys')
    # ### end Alembic commands ###
