"""Add is_public column to collections

Gives each RAG collection an egress classification: is its content
non-sensitive ("public") or sensitive ("private")? Defaults to private
(False) — the safe choice. A private collection is excluded under
PUBLIC_ONLY egress scope and forces local LLM/embeddings inference when
used, so its chunks never reach a cloud model.

Revision ID: 0011
Revises: 0010
Create Date: 2026-06-04

Column Added:
=============
- is_public (BOOLEAN): Whether the collection's content may be treated as
  public (cloud inference permitted, allowed under PUBLIC_ONLY scope).
  NOT NULL, server_default 0 (private) so pre-existing collections are
  classified private — the safe default.

Migration Notes:
===============
- Uses SQLite batch mode (table recreation) for ALTER TABLE operations.
- Idempotent: skips the column if it already exists (fresh databases
  created after the model change will already have it).

Downgrade Behavior:
==================
The downgrade() function removes the is_public column. Any per-collection
public/private classification will be LOST on downgrade.
"""

from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect

# revision identifiers, used by Alembic.
revision = "0011"
down_revision = "0010"
branch_labels = None
depends_on = None


def column_exists(table_name: str, column_name: str) -> bool:
    """Check if a column exists in a table."""
    bind = op.get_bind()
    inspector = inspect(bind)

    if not inspector.has_table(table_name):
        return False

    columns = {col["name"] for col in inspector.get_columns(table_name)}
    return column_name in columns


def upgrade():
    """Add is_public column to collections if it doesn't exist."""
    bind = op.get_bind()
    inspector = inspect(bind)

    if not inspector.has_table("collections"):
        # Table doesn't exist yet, will be created by initial migration
        return

    if column_exists("collections", "is_public"):
        # Column already exists (fresh database created after model change)
        return

    # Use batch mode for SQLite compatibility. server_default 0 so existing
    # collections are classified private (the safe default) without a
    # separate backfill UPDATE. nullable=True to match the ORM model column
    # (Collection.is_public, like its sibling is_default, is nullable) so the
    # schema-drift autogenerate check stays clean; the classification code
    # treats NULL as private anyway.
    with op.batch_alter_table("collections") as batch_op:
        batch_op.add_column(
            sa.Column(
                "is_public",
                sa.Boolean(),
                nullable=True,
                server_default=sa.false(),
            )
        )


def downgrade():
    """Remove is_public column from collections."""
    bind = op.get_bind()
    inspector = inspect(bind)

    if not inspector.has_table("collections"):
        return

    if not column_exists("collections", "is_public"):
        return

    with op.batch_alter_table("collections") as batch_op:
        batch_op.drop_column("is_public")
