"""add hierarchical expense memory governance fields

Revision ID: 20260716_0007
Revises: 20260716_0006
Create Date: 2026-07-16 12:05:00
"""

from collections.abc import Sequence

import sqlalchemy as sa

from alembic import op

revision: str = "20260716_0007"
down_revision: str | None = "20260716_0006"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
    op.add_column(
        "memory_entries",
        sa.Column(
            "origin_type",
            sa.String(length=24),
            server_default="learned",
            nullable=False,
        ),
    )
    op.add_column(
        "memory_entries",
        sa.Column("managed_by", sa.String(length=255), nullable=True),
    )
    op.add_column(
        "memory_entries",
        sa.Column("managed_at", sa.DateTime(timezone=True), nullable=True),
    )
    op.add_column(
        "memory_entries",
        sa.Column("management_reason", sa.String(length=255), nullable=True),
    )
    op.add_column(
        "memory_entries",
        sa.Column("policy_version", sa.String(length=64), nullable=True),
    )

    op.drop_constraint(
        "ck_memory_entries_scope_type",
        "memory_entries",
        type_="check",
    )
    op.create_check_constraint(
        "ck_memory_entries_scope_type",
        "memory_entries",
        "scope_type IN ('user', 'department', 'enterprise')",
    )
    op.create_check_constraint(
        "ck_memory_entries_origin_type",
        "memory_entries",
        "origin_type IN ('learned', 'admin_managed')",
    )
    op.create_check_constraint(
        "ck_memory_entries_scope_origin",
        "memory_entries",
        "(scope_type = 'user' AND origin_type = 'learned') OR "
        "(scope_type IN ('department', 'enterprise') "
        "AND origin_type = 'admin_managed')",
    )
    op.create_check_constraint(
        "ck_memory_entries_enterprise_scope",
        "memory_entries",
        "scope_type != 'enterprise' OR scope_id = tenant_id",
    )
    op.create_check_constraint(
        "ck_memory_entries_management_audit",
        "memory_entries",
        "(origin_type = 'learned' AND managed_by IS NULL "
        "AND managed_at IS NULL AND management_reason IS NULL) OR "
        "(origin_type = 'admin_managed' AND managed_by IS NOT NULL "
        "AND length(trim(managed_by)) > 0 AND managed_at IS NOT NULL "
        "AND management_reason IS NOT NULL "
        "AND length(trim(management_reason)) > 0 "
        "AND policy_version IS NOT NULL AND length(trim(policy_version)) > 0)",
    )


def downgrade() -> None:
    op.drop_constraint(
        "ck_memory_entries_management_audit",
        "memory_entries",
        type_="check",
    )
    op.drop_constraint(
        "ck_memory_entries_enterprise_scope",
        "memory_entries",
        type_="check",
    )
    op.drop_constraint(
        "ck_memory_entries_scope_origin",
        "memory_entries",
        type_="check",
    )
    op.drop_constraint(
        "ck_memory_entries_origin_type",
        "memory_entries",
        type_="check",
    )
    op.drop_constraint(
        "ck_memory_entries_scope_type",
        "memory_entries",
        type_="check",
    )
    # 0006 只认识个人记忆。组织记忆无法无损映射回旧结构，因此降级时
    # 先解除组织版本间的自引用并删除组织作用域记录，个人记忆完整保留。
    op.execute(
        "UPDATE memory_entries SET superseded_by_id = NULL "
        "WHERE scope_type IN ('department', 'enterprise')"
    )
    op.execute(
        "DELETE FROM memory_entries "
        "WHERE scope_type IN ('department', 'enterprise')"
    )
    op.create_check_constraint(
        "ck_memory_entries_scope_type",
        "memory_entries",
        "scope_type = 'user'",
    )

    op.drop_column("memory_entries", "policy_version")
    op.drop_column("memory_entries", "management_reason")
    op.drop_column("memory_entries", "managed_at")
    op.drop_column("memory_entries", "managed_by")
    op.drop_column("memory_entries", "origin_type")
