"""saved_views — per-user named views over a filtered table (M8).

The R1 "easier to navigate" centerpiece (09-build-plan §M8 / §UX
program): a user names the current filter/sort state of a filtered
table (/entries, /projects, CC batches) and can pin recurring ones to
the nav. Retires the hardcoded quick-chips.

Storage decision (ADR #009): a dedicated table, NOT users.preferences
jsonb. The unique (user, page, lower(name)) name guard, the pinned /
sort_order columns, and race-free single-row inserts/renames/deletes are
all first-class here; a jsonb blob would require read-modify-write on
every mutation. Views are per-USER (real user — a personal nav pref,
never impersonated) and are NOT tenant-scoped nor part of the tenant
audit trail (they hold no tenant data, only a saved filter string).

Revision ID: 0005
Revises: 0004
Create Date: 2026-07-08
"""

from collections.abc import Sequence

import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

from alembic import op

revision: str = "0005"
down_revision: str | None = "0004"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
    op.create_table(
        "saved_views",
        sa.Column(
            "id",
            postgresql.UUID(as_uuid=True),
            primary_key=True,
            server_default=sa.text("gen_random_uuid()"),
        ),
        sa.Column(
            "user_id",
            postgresql.UUID(as_uuid=True),
            sa.ForeignKey("users.id", ondelete="CASCADE"),
            nullable=False,
        ),
        sa.Column("page", sa.Text(), nullable=False),
        sa.Column("name", sa.Text(), nullable=False),
        sa.Column("search", sa.Text(), nullable=False, server_default=sa.text("''")),
        sa.Column(
            "pinned", sa.Boolean(), nullable=False, server_default=sa.text("false")
        ),
        sa.Column(
            "sort_order", sa.Integer(), nullable=False, server_default=sa.text("0")
        ),
        sa.Column(
            "created_at",
            sa.DateTime(timezone=True),
            nullable=False,
            server_default=sa.text("now()"),
        ),
        sa.Column(
            "updated_at",
            sa.DateTime(timezone=True),
            nullable=False,
            server_default=sa.text("now()"),
        ),
        sa.CheckConstraint(
            "page IN ('entries', 'projects', 'cc')", name="ck_saved_views_page"
        ),
    )
    # Case-insensitive name uniqueness per (user, page) — the DB half of
    # the friendly "already exists" guard.
    op.create_index(
        "uq_saved_views_user_page_lname",
        "saved_views",
        ["user_id", "page", sa.text("lower(name)")],
        unique=True,
    )
    op.create_index("ix_saved_views_user_page", "saved_views", ["user_id", "page"])


def downgrade() -> None:
    op.drop_table("saved_views")
