"""Enable RLS on Phase 2 session and supporting tables.

10 tables use a standard tenant-only policy.
step_library uses a visibility-aware policy — public steps visible to all tenants.

NOTE: session_messages does not exist in this codebase (removed from plan).
script_generations is the correct table name (not script_template_generations).
sessions and ai_sessions are two separate tables, both in scope.

Prerequisites:
- Phase 1 migration must have run (resolutionflow_app role exists, Phase 1 tables have RLS)
- NOT NULL write-path bugs fixed (P2-A commits b641ac6)
- shares.py cross-tenant session fix deployed (P2-B commit ac2b193)

Revision ID: 70a5dd746e83
Revises: c5f48b9890f9
Create Date: 2026-04-10 06:54:49.431817

"""
from typing import Sequence, Union

from alembic import op

# revision identifiers, used by Alembic.
revision: str = '70a5dd746e83'
down_revision: Union[str, None] = 'c5f48b9890f9'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None

_NULL_UUID = "00000000-0000-0000-0000-000000000000"
_CURRENT_ACCOUNT = (
    f"COALESCE(NULLIF(current_setting('app.current_account_id', TRUE), ''), "
    f"'{_NULL_UUID}')::uuid"
)

# Standard tenant-only policy — account_id must match the current tenant.
# When no tenant context is set, COALESCE returns the nil UUID so zero rows
# are visible (fail-closed).
_STANDARD_USING = f"account_id = {_CURRENT_ACCOUNT}"

# Visibility-aware policy for step_library — public steps (visibility='public')
# must be visible to ALL tenants regardless of account_id. This covers the
# visibility='public' arm of build_step_visibility_filter() in app/core/filters.py.
# The created_by arm (private steps visible to their author) is covered
# transitively: private steps share account_id with their creator, so the
# account_id match handles it. This relies on account_id NOT NULL on step_library.
_STEP_LIBRARY_USING = f"account_id = {_CURRENT_ACCOUNT} OR visibility = 'public'"

# Standard tables: strict tenant isolation, no cross-tenant visibility.
_STANDARD_TABLES = [
    "sessions",
    "ai_sessions",
    "session_branches",
    "session_supporting_data",
    "session_resolution_outputs",
    "session_handoffs",
    "script_templates",
    "script_generations",
    "maintenance_schedules",
    "psa_post_log",
]


def upgrade() -> None:
    # ── Standard tenant-isolation tables ────────────────────────────────────
    for table in _STANDARD_TABLES:
        op.execute(f"ALTER TABLE {table} ENABLE ROW LEVEL SECURITY")
        op.execute(f"ALTER TABLE {table} FORCE ROW LEVEL SECURITY")
        op.execute(f"""
            CREATE POLICY tenant_isolation ON {table}
                USING ({_STANDARD_USING})
        """)

    # ── step_library ────────────────────────────────────────────────────────
    # Public steps (visibility='public') must be readable by all tenants so
    # the Solutions Library browsing experience works without tenant context.
    # Private/team steps remain tenant-scoped.
    op.execute("ALTER TABLE step_library ENABLE ROW LEVEL SECURITY")
    op.execute("ALTER TABLE step_library FORCE ROW LEVEL SECURITY")
    op.execute(f"""
        CREATE POLICY tenant_isolation ON step_library
            USING ({_STEP_LIBRARY_USING})
    """)


def downgrade() -> None:
    for table in _STANDARD_TABLES + ["step_library"]:
        op.execute(f"DROP POLICY IF EXISTS tenant_isolation ON {table}")
        op.execute(f"ALTER TABLE {table} DISABLE ROW LEVEL SECURITY")
        op.execute(f"ALTER TABLE {table} NO FORCE ROW LEVEL SECURITY")
