"""add tenant_isolation policy on restaurant table

Migration 0023 / 0030 created `tenant_isolation` policies on every table with
a `restaurant_id` column, but skipped the `restaurant` table itself (where
`id` IS the tenant discriminator — there is no `restaurant_id` column to key
off). At the same time, Neon's "Auth (Neon RLS)" feature auto-enabled
`ROW LEVEL SECURITY` on every public table when it was switched on in the
Neon Console. Result: `restaurant` has RLS-on with zero policies, which
Postgres treats as default-deny for any non-BYPASSRLS role.

Every PATCH /api/v1/restaurants/me and DELETE /api/v1/restaurants/{id} that
ran after `get_tenant_session` (which drops to the `authenticated` role)
became the first writes to mutate this row under RLS — they matched zero
rows and SQLAlchemy raised `StaleDataError: UPDATE statement on table
'restaurant' expected to update 1 row(s); 0 were matched`, producing a
generic 500. Read paths (`/api/v1/restaurants/me` GET) keep working because
`get_current_restaurant` resolves them via the owner-pool session (BYPASSRLS).

This policy is the standard self-referential shape: a request can read /
write the `restaurant` row whose `id` matches the JWT's
`activeRestaurantId` claim. The claim is already validated upstream by
`get_current_restaurant` (team membership + (org, team) consistency +
`active_restaurant_id` cross-check), so this DB-side fence is
defense-in-depth, not the only check.

Revision ID: 0032_restaurant_rls_policy
Revises: 0031_table_combination_merged_position
Create Date: 2026-05-26 22:30:00.000000
"""

from collections.abc import Sequence

from alembic import op

revision: str = "0032_restaurant_rls_policy"
down_revision: str | None = "0031_table_combination_merged_position"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
    # Idempotent — every step is drop-if-exists / IF NOT EXISTS so the
    # migration can re-run safely on branches that already saw a partial
    # rollout (e.g. someone fixed prod by hand before this landed).
    op.execute('ALTER TABLE "restaurant" ENABLE ROW LEVEL SECURITY')
    op.execute('ALTER TABLE "restaurant" FORCE ROW LEVEL SECURITY')
    op.execute('DROP POLICY IF EXISTS tenant_isolation ON "restaurant"')
    op.execute(
        'CREATE POLICY tenant_isolation ON "restaurant" '
        "FOR ALL "
        "USING (id = (auth.session() ->> 'activeRestaurantId')) "
        "WITH CHECK (id = (auth.session() ->> 'activeRestaurantId'))"
    )


def downgrade() -> None:
    op.execute('DROP POLICY IF EXISTS tenant_isolation ON "restaurant"')
