"""notification table (消息通知中心 站内消息)

Revision ID: notification_table
Revises: 1a924c274fce
Create Date: 2026-07-15 12:00:00.000000

"""
from typing import Sequence, Union

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


# revision identifiers, used by Alembic.
revision: str = 'notification_table'
down_revision: Union[str, Sequence[str], None] = '1a924c274fce'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None

# PG 用 JSONB,SQLite 退化为通用 JSON(与 models/notification._JSON 一致)。
_JSON = sa.JSON().with_variant(postgresql.JSONB(), 'postgresql')


def upgrade() -> None:
    op.create_table(
        'notification',
        sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
        sa.Column('user_id', sa.Integer(), nullable=False),
        sa.Column('type', sa.String(length=32), nullable=False),
        sa.Column('coins', sa.Integer(), nullable=True),
        sa.Column('cash_cents', sa.Integer(), nullable=True),
        sa.Column('info_rows', _JSON, nullable=False),
        sa.Column('extra', _JSON, nullable=False),
        sa.Column('is_read', sa.Boolean(), nullable=False),
        sa.Column('read_at', sa.DateTime(timezone=True), nullable=True),
        sa.Column('dedup_key', sa.String(length=64), nullable=True),
        sa.Column('sent_at', sa.DateTime(timezone=True), server_default=sa.text('(CURRENT_TIMESTAMP)'), 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(['user_id'], ['user.id'], ),
        sa.PrimaryKeyConstraint('id'),
    )
    with op.batch_alter_table('notification', schema=None) as batch_op:
        batch_op.create_index('ix_notification_type', ['type'], unique=False)
        # 列表分页:按用户取 + sent_at 倒序
        batch_op.create_index('ix_notification_user_sent', ['user_id', 'sent_at'], unique=False)
        # 铃铛角标:count where user_id=? and is_read=false —— 部分索引只覆盖未读行
        batch_op.create_index(
            'ix_notification_user_unread', ['user_id'], unique=False,
            sqlite_where=sa.text('is_read = 0'),
            postgresql_where=sa.text('is_read = false'),
        )
        # 去重/合并:同一 (user, type, dedup_key) 未读期间只允许一条(已读后可再生成)
        batch_op.create_index(
            'uq_notification_user_type_dedup', ['user_id', 'type', 'dedup_key'], unique=True,
            sqlite_where=sa.text('dedup_key IS NOT NULL AND is_read = 0'),
            postgresql_where=sa.text('dedup_key IS NOT NULL AND is_read = false'),
        )


def downgrade() -> None:
    with op.batch_alter_table('notification', schema=None) as batch_op:
        batch_op.drop_index('uq_notification_user_type_dedup')
        batch_op.drop_index('ix_notification_user_unread')
        batch_op.drop_index('ix_notification_user_sent')
        batch_op.drop_index('ix_notification_type')
    op.drop_table('notification')
