"""pending_curator_actions — curator-proposed mutations awaiting user approval

Revision ID: 0051
Revises: 0050
Create Date: 2026-05-23

Architecture: the curator runs unattended and can be confidently wrong.
Additive operations (create_*, record_moment, log_work, save_person)
land directly because adds are easily undone. Mutating operations
(update_*, delete_*) are proposed to this table instead — the user
sees them in a "Needs Review" panel and approves or rejects each one.

A proposed action stores:
  - The action type (`update_task`, `delete_note`, etc.) — drives which
    handler gets re-executed on approval.
  - The target id + type for display ("update task 42", "delete note 17").
  - The proposed arguments (`payload`) — what the curator wanted to do.
  - A snapshot of the target's state at proposal time (`current_snapshot`)
    — so the review UI can render a real before/after diff even if other
    work modified the entity since.
  - The status (`pending` / `approved` / `rejected`) and review timestamp.

Approval replays the original tool call via execute_tool with
authority="user" so the request bypasses the curator interceptor and
just runs.
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import JSONB


revision = "0051"
down_revision = "0050"
branch_labels = None
depends_on = None


def upgrade() -> None:
    op.create_table(
        "pending_curator_actions",
        sa.Column("id", sa.Integer, primary_key=True),
        sa.Column(
            "user_id",
            sa.Integer,
            sa.ForeignKey("users.id", ondelete="CASCADE"),
            nullable=False,
        ),
        sa.Column(
            "conv_id",
            sa.Integer,
            sa.ForeignKey("conversations.id", ondelete="SET NULL"),
            nullable=True,
        ),
        sa.Column("action_type", sa.Text, nullable=False),
        sa.Column("target_type", sa.Text, nullable=True),
        sa.Column("target_id", sa.Integer, nullable=True),
        sa.Column("target_label", sa.Text, nullable=True),
        sa.Column("payload", JSONB, nullable=False, server_default=sa.text("'{}'::jsonb")),
        sa.Column(
            "current_snapshot",
            JSONB,
            nullable=False,
            server_default=sa.text("'{}'::jsonb"),
        ),
        sa.Column(
            "status",
            sa.Text,
            nullable=False,
            server_default=sa.text("'pending'"),
        ),
        sa.Column(
            "created_at",
            sa.DateTime(timezone=True),
            nullable=False,
            server_default=sa.text("now()"),
        ),
        sa.Column("reviewed_at", sa.DateTime(timezone=True), nullable=True),
        sa.CheckConstraint(
            "status IN ('pending', 'approved', 'rejected')",
            name="pending_curator_actions_status_check",
        ),
    )
    # Pending-only index — the Needs Review panel fetches "user's pending"
    # constantly, history rows just accumulate.
    op.create_index(
        "ix_pending_curator_actions_user_pending",
        "pending_curator_actions",
        ["user_id", sa.text("created_at DESC")],
        postgresql_where=sa.text("status = 'pending'"),
    )


def downgrade() -> None:
    op.drop_index(
        "ix_pending_curator_actions_user_pending",
        table_name="pending_curator_actions",
    )
    op.drop_table("pending_curator_actions")
