"""P1-3 + P2: 热路径索引 + 状态 CHECK 约束

新增：
- 复合索引 ix_chapter_reviews_project_chapter / ix_chapter_digests_project_chapter
- 主访问谓词单列索引 ix_projects_owner_id / ix_usage_ledger_owner_id /
  ix_usage_ledger_project_id / ix_rules_project_id
- 部分索引 ix_jobs_status_running (status='running') /
  ix_rules_global (project_id IS NULL)
- CHECK 约束 ck_chapters_status / ck_foreshadow_status（DB 边界兜底状态机）

Revision ID: c2b3d4e5f6a7
Revises: b1a2c3d4e5f6
Create Date: 2026-06-21 00:05:00.000000
"""

from __future__ import annotations

from collections.abc import Sequence

import sqlalchemy as sa
from alembic import op

revision: str = "c2b3d4e5f6a7"
down_revision: str | None = "b1a2c3d4e5f6"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
    # --- 复合索引（验收/评审热路径）---
    op.create_index(
        "ix_chapter_reviews_project_chapter",
        "chapter_reviews",
        ["project_id", "chapter_no", sa.text("created_at DESC")],
        unique=False,
    )
    op.create_index(
        "ix_chapter_digests_project_chapter",
        "chapter_digests",
        ["project_id", sa.text("chapter_no DESC")],
        unique=False,
    )
    # --- 主访问谓词单列索引 ---
    op.create_index("ix_projects_owner_id", "projects", ["owner_id"], unique=False)
    op.create_index("ix_usage_ledger_owner_id", "usage_ledger", ["owner_id"], unique=False)
    op.create_index("ix_usage_ledger_project_id", "usage_ledger", ["project_id"], unique=False)
    op.create_index("ix_rules_project_id", "rules", ["project_id"], unique=False)
    # --- 部分索引 ---
    op.create_index(
        "ix_jobs_status_running",
        "jobs",
        ["status"],
        unique=False,
        postgresql_where=sa.text("status = 'running'"),
    )
    op.create_index(
        "ix_rules_global",
        "rules",
        ["id"],
        unique=False,
        postgresql_where=sa.text("project_id IS NULL"),
    )
    # --- 状态 CHECK 约束 ---
    op.create_check_constraint(
        "ck_chapters_status",
        "chapters",
        "status IN ('draft', 'accepted')",
    )
    op.create_check_constraint(
        "ck_foreshadow_status",
        "foreshadow",
        "status IN ('OPEN', 'PARTIAL', 'CLOSED', 'OVERDUE')",
    )


def downgrade() -> None:
    op.drop_constraint("ck_foreshadow_status", "foreshadow", type_="check")
    op.drop_constraint("ck_chapters_status", "chapters", type_="check")
    op.drop_index("ix_rules_global", table_name="rules")
    op.drop_index("ix_jobs_status_running", table_name="jobs")
    op.drop_index("ix_rules_project_id", table_name="rules")
    op.drop_index("ix_usage_ledger_project_id", table_name="usage_ledger")
    op.drop_index("ix_usage_ledger_owner_id", table_name="usage_ledger")
    op.drop_index("ix_projects_owner_id", table_name="projects")
    op.drop_index("ix_chapter_digests_project_chapter", table_name="chapter_digests")
    op.drop_index("ix_chapter_reviews_project_chapter", table_name="chapter_reviews")
