"""Add cloud_items, cloud_item_topics, and cloud_folder_states tables.

Revision ID: 0006
Revises: 0005
Create Date: 2026-06-18

Changes:
  1. cloud_connections: add display_name_override (nullable Text) for user-defined
     display names distinct from the auto-generated default.  Does NOT add a
     (user_id, provider) unique constraint — multiple same-provider accounts must
     remain permitted (D-02, D-03).

  2. cloud_items: durable per-item metadata indexed by connection/provider item ID.
     No MinIO object_key field — provider bytes are never mirrored here.
     analysis_status, semantic_index_status, semantic_index_data reserved for
     Phase 14 analysis and semantic search.

  3. cloud_item_topics: association between cloud items and existing owner topics.
     No local Document row required.

  4. cloud_folder_states: per-connection/parent-ref folder freshness row.
     Root folder represented with parent_ref = '' (empty string, not NULL) to
     allow a unique constraint on (connection_id, parent_ref).

Design notes:
  - Ownership boundary: every cloud row has user_id + connection_id.
  - Uniqueness: (connection_id, provider_item_id) for items; (connection_id, parent_ref)
    for folder states.  Same provider_item_id can coexist across connections.
  - CASCADE: deleting a user or connection cascades all child rows automatically.
  - D-18 compliance: no quota_used / byte_size field that flows to quotas.used_bytes.
"""
from __future__ import annotations

import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import UUID as PG_UUID, JSONB
from alembic import op

# revision identifiers, used by Alembic.
revision = "0006"
down_revision = "0005"
branch_labels = None
depends_on = None


def upgrade() -> None:
    # 1. Extend cloud_connections with optional user-defined display name override
    op.add_column(
        "cloud_connections",
        sa.Column("display_name_override", sa.Text, nullable=True),
    )

    # 2. cloud_items table
    op.create_table(
        "cloud_items",
        sa.Column(
            "id",
            PG_UUID(as_uuid=True),
            primary_key=True,
            server_default=sa.text("gen_random_uuid()"),
        ),
        sa.Column(
            "user_id",
            PG_UUID(as_uuid=True),
            sa.ForeignKey("users.id", ondelete="CASCADE"),
            nullable=False,
        ),
        sa.Column(
            "connection_id",
            PG_UUID(as_uuid=True),
            sa.ForeignKey("cloud_connections.id", ondelete="CASCADE"),
            nullable=False,
        ),
        # Opaque provider-assigned identifier (e.g. Drive file ID, OneDrive driveItem id)
        sa.Column("provider_item_id", sa.Text, nullable=False),
        # Opaque reference to the parent folder (empty string = root)
        sa.Column("parent_ref", sa.Text, nullable=True),
        # Provider-reported path snapshot — informational only, not used for lookup
        sa.Column("path_snapshot", sa.Text, nullable=True),
        sa.Column("name", sa.Text, nullable=False),
        # "file" | "folder"
        sa.Column("kind", sa.String(8), nullable=False),
        sa.Column("content_type", sa.Text, nullable=True),
        # Provider-reported size — never flows to quotas.used_bytes (D-18)
        sa.Column("provider_size", sa.BigInteger, nullable=True),
        sa.Column("etag", sa.Text, nullable=True),
        sa.Column("version", sa.Text, nullable=True),
        sa.Column("modified_at", sa.TIMESTAMP(timezone=True), nullable=True),
        sa.Column(
            "last_seen_at",
            sa.TIMESTAMP(timezone=True),
            nullable=False,
            server_default=sa.text("now()"),
        ),
        sa.Column("deleted_at", sa.TIMESTAMP(timezone=True), nullable=True),
        # Phase 14: byte analysis fields (reserved)
        sa.Column("extracted_text", sa.Text, nullable=True),
        # "pending" | "processing" | "done" | "error" | "skipped"
        sa.Column("analysis_status", sa.String(16), nullable=False, server_default="pending"),
        # "none" | "pending" | "indexed" | "error"
        sa.Column("semantic_index_status", sa.String(16), nullable=False, server_default="none"),
        # Provider-independent semantic search artifacts (reserved for Phase 15)
        sa.Column("semantic_index_data", JSONB, nullable=True),
        sa.Column(
            "created_at",
            sa.TIMESTAMP(timezone=True),
            nullable=False,
            server_default=sa.text("now()"),
        ),
        sa.Column(
            "updated_at",
            sa.TIMESTAMP(timezone=True),
            nullable=False,
            server_default=sa.text("now()"),
        ),
        # Uniqueness: same provider item ID can exist in different connections only
        sa.UniqueConstraint(
            "connection_id", "provider_item_id",
            name="uq_cloud_items_connection_provider_item",
        ),
    )

    # Indexes for ownership lookups
    op.create_index("ix_cloud_items_user_id", "cloud_items", ["user_id"])
    op.create_index("ix_cloud_items_connection_id", "cloud_items", ["connection_id"])
    op.create_index(
        "ix_cloud_items_connection_parent",
        "cloud_items",
        ["connection_id", "parent_ref"],
    )

    # 3. cloud_item_topics association table
    op.create_table(
        "cloud_item_topics",
        sa.Column(
            "cloud_item_id",
            PG_UUID(as_uuid=True),
            sa.ForeignKey("cloud_items.id", ondelete="CASCADE"),
            primary_key=True,
        ),
        sa.Column(
            "topic_id",
            PG_UUID(as_uuid=True),
            sa.ForeignKey("topics.id", ondelete="CASCADE"),
            primary_key=True,
        ),
    )

    # 4. cloud_folder_states table
    # parent_ref uses empty string '' for root (allows unique constraint)
    op.create_table(
        "cloud_folder_states",
        sa.Column(
            "id",
            PG_UUID(as_uuid=True),
            primary_key=True,
            server_default=sa.text("gen_random_uuid()"),
        ),
        sa.Column(
            "user_id",
            PG_UUID(as_uuid=True),
            sa.ForeignKey("users.id", ondelete="CASCADE"),
            nullable=False,
        ),
        sa.Column(
            "connection_id",
            PG_UUID(as_uuid=True),
            sa.ForeignKey("cloud_connections.id", ondelete="CASCADE"),
            nullable=False,
        ),
        # '' for root folder, provider ref string for non-root
        sa.Column("parent_ref", sa.Text, nullable=False, server_default=""),
        # "refreshing" | "fresh" | "warning"
        sa.Column("refresh_state", sa.String(16), nullable=False, server_default="fresh"),
        sa.Column("last_refreshed_at", sa.TIMESTAMP(timezone=True), nullable=True),
        # Stable error code (e.g. "token_expired", "rate_limited") — never raw exception
        sa.Column("error_code", sa.String(64), nullable=True),
        # Safe human-readable message — never raw provider error text
        sa.Column("error_message", sa.Text, nullable=True),
        sa.Column(
            "created_at",
            sa.TIMESTAMP(timezone=True),
            nullable=False,
            server_default=sa.text("now()"),
        ),
        sa.Column(
            "updated_at",
            sa.TIMESTAMP(timezone=True),
            nullable=False,
            server_default=sa.text("now()"),
        ),
        sa.UniqueConstraint(
            "connection_id", "parent_ref",
            name="uq_cloud_folder_states_connection_parent",
        ),
    )

    op.create_index("ix_cloud_folder_states_connection", "cloud_folder_states", ["connection_id"])


def downgrade() -> None:
    op.drop_index("ix_cloud_folder_states_connection", table_name="cloud_folder_states")
    op.drop_table("cloud_folder_states")
    op.drop_table("cloud_item_topics")
    op.drop_index("ix_cloud_items_connection_parent", table_name="cloud_items")
    op.drop_index("ix_cloud_items_connection_id", table_name="cloud_items")
    op.drop_index("ix_cloud_items_user_id", table_name="cloud_items")
    op.drop_table("cloud_items")
    op.drop_column("cloud_connections", "display_name_override")
