"""initial schema part 4: collaboration tables

Revision ID: 202602260004
Revises: 202602260003
Create Date: 2026-02-26 20:13:00
"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

revision: str = "202602260004"
down_revision: Union[str, Sequence[str], None] = "202602260003"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    op.create_table(
        "schedule_items",
        sa.Column("id", sa.UUID(), nullable=False),
        sa.Column("owner_id", sa.UUID(), nullable=False),
        sa.Column("title", sa.String(length=255), nullable=False),
        sa.Column("description", sa.Text(), nullable=True),
        sa.Column("start_at", sa.DateTime(timezone=True), nullable=False),
        sa.Column("end_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("timezone", sa.String(length=50), nullable=False),
        sa.Column(
            "metadata",
            postgresql.JSONB(astext_type=sa.Text()),
            server_default="{}",
            nullable=False,
        ),
        sa.Column("recurrence_rule", sa.String(length=255), nullable=True),
        sa.Column("source_type", sa.String(length=20), nullable=False),
        sa.Column("status", sa.String(length=20), nullable=False),
        sa.Column("created_by", sa.UUID(), nullable=True),
        sa.Column(
            "created_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.Column(
            "updated_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index(
        "ix_schedule_items_owner_start",
        "schedule_items",
        ["owner_id", "start_at"],
        unique=False,
    )
    op.create_index(
        "ix_schedule_items_status_start",
        "schedule_items",
        ["status", "start_at"],
        unique=False,
    )
    op.execute(
        "ALTER TABLE schedule_items ADD CONSTRAINT chk_schedule_item_source_type CHECK (source_type IN ('manual', 'imported', 'agent_generated'))"
    )
    op.execute(
        "ALTER TABLE schedule_items ADD CONSTRAINT chk_schedule_item_status CHECK (status IN ('active', 'completed', 'canceled', 'archived'))"
    )
    op.create_foreign_key(
        "fk_schedule_items_owner_id",
        "schedule_items",
        "users",
        ["owner_id"],
        ["id"],
        referent_schema="auth",
        ondelete="CASCADE",
    )
    op.create_foreign_key(
        "fk_schedule_items_created_by",
        "schedule_items",
        "users",
        ["created_by"],
        ["id"],
        referent_schema="auth",
        ondelete="SET NULL",
    )
    _enable_rls("schedule_items")

    op.create_table(
        "schedule_subscriptions",
        sa.Column("id", sa.UUID(), nullable=False),
        sa.Column("item_id", sa.UUID(), nullable=False),
        sa.Column("subscriber_id", sa.UUID(), nullable=False),
        sa.Column(
            "permission", sa.Integer(), nullable=False, server_default=sa.text("1")
        ),
        sa.Column(
            "notify_level",
            sa.String(length=20),
            nullable=False,
            server_default=sa.text("'all'"),
        ),
        sa.Column(
            "status",
            sa.String(length=20),
            nullable=False,
            server_default=sa.text("'active'"),
        ),
        sa.Column("created_by", sa.UUID(), nullable=True),
        sa.Column(
            "created_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.Column(
            "updated_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint(
            "item_id", "subscriber_id", name="uq_schedule_subscriptions_item_subscriber"
        ),
    )
    op.create_index(
        "ix_schedule_subscribers_subscriber_status",
        "schedule_subscriptions",
        ["subscriber_id", "status"],
        unique=False,
    )
    op.create_index(
        "ix_schedule_subscribers_item_status",
        "schedule_subscriptions",
        ["item_id", "status"],
        unique=False,
    )
    op.execute(
        "ALTER TABLE schedule_subscriptions ADD CONSTRAINT chk_schedule_subscription_permission CHECK (permission BETWEEN 0 AND 7)"
    )
    op.execute(
        "ALTER TABLE schedule_subscriptions ADD CONSTRAINT chk_schedule_subscription_notify_level CHECK (notify_level IN ('all', 'mentions', 'none'))"
    )
    op.execute(
        "ALTER TABLE schedule_subscriptions ADD CONSTRAINT chk_schedule_subscription_status CHECK (status IN ('active', 'paused', 'unsubscribed'))"
    )
    op.create_foreign_key(
        "fk_schedule_subscriptions_item_id",
        "schedule_subscriptions",
        "schedule_items",
        ["item_id"],
        ["id"],
        ondelete="CASCADE",
    )
    op.create_foreign_key(
        "fk_schedule_subscriptions_subscriber_id",
        "schedule_subscriptions",
        "users",
        ["subscriber_id"],
        ["id"],
        referent_schema="auth",
        ondelete="CASCADE",
    )
    op.create_foreign_key(
        "fk_schedule_subscriptions_created_by",
        "schedule_subscriptions",
        "users",
        ["created_by"],
        ["id"],
        referent_schema="auth",
        ondelete="SET NULL",
    )
    _enable_rls("schedule_subscriptions")

    op.create_table(
        "todos",
        sa.Column("id", sa.UUID(), nullable=False),
        sa.Column("owner_id", sa.UUID(), nullable=False),
        sa.Column("title", sa.String(length=255), nullable=False),
        sa.Column("description", sa.String(length=1000), nullable=True),
        sa.Column("due_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("priority", sa.Integer(), nullable=False),
        sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
        sa.Column("status", sa.String(length=20), nullable=False),
        sa.Column("created_by", sa.UUID(), nullable=True),
        sa.Column(
            "created_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.Column(
            "updated_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index(
        "ix_todos_owner_status_due",
        "todos",
        ["owner_id", "status", "due_at"],
        unique=False,
    )
    op.create_index(
        "ix_todos_owner_created", "todos", ["owner_id", "created_at"], unique=False
    )
    op.execute(
        "ALTER TABLE todos ADD CONSTRAINT chk_todos_status CHECK (status IN ('pending', 'done', 'canceled'))"
    )
    op.execute(
        "ALTER TABLE todos ADD CONSTRAINT chk_todos_priority CHECK (priority BETWEEN 1 AND 4)"
    )
    op.create_foreign_key(
        "fk_todos_owner_id",
        "todos",
        "users",
        ["owner_id"],
        ["id"],
        referent_schema="auth",
        ondelete="CASCADE",
    )
    op.create_foreign_key(
        "fk_todos_created_by",
        "todos",
        "users",
        ["created_by"],
        ["id"],
        referent_schema="auth",
        ondelete="SET NULL",
    )
    _enable_rls("todos")

    op.create_table(
        "todo_sources",
        sa.Column("id", sa.UUID(), nullable=False),
        sa.Column("todo_id", sa.UUID(), nullable=False),
        sa.Column("schedule_item_id", sa.UUID(), nullable=False),
        sa.Column(
            "created_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.Column(
            "updated_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint(
            "todo_id", "schedule_item_id", name="uq_todo_sources_todo_schedule"
        ),
    )
    op.create_index("ix_todo_sources_todo", "todo_sources", ["todo_id"], unique=False)
    op.create_index(
        "ix_todo_sources_schedule_item",
        "todo_sources",
        ["schedule_item_id"],
        unique=False,
    )
    op.create_foreign_key(
        "fk_todo_sources_todo_id",
        "todo_sources",
        "todos",
        ["todo_id"],
        ["id"],
        ondelete="CASCADE",
    )
    op.create_foreign_key(
        "fk_todo_sources_schedule_item_id",
        "todo_sources",
        "schedule_items",
        ["schedule_item_id"],
        ["id"],
        ondelete="CASCADE",
    )
    _enable_rls("todo_sources")

    op.create_table(
        "inbox_messages",
        sa.Column("id", sa.UUID(), nullable=False),
        sa.Column("recipient_id", sa.UUID(), nullable=False),
        sa.Column("sender_id", sa.UUID(), nullable=True),
        sa.Column("message_type", sa.String(length=20), nullable=False),
        sa.Column("friendship_id", sa.UUID(), nullable=True),
        sa.Column("schedule_item_id", sa.UUID(), nullable=True),
        sa.Column("group_id", sa.UUID(), nullable=True),
        sa.Column("content", sa.Text(), nullable=True),
        sa.Column(
            "is_read", sa.Boolean(), nullable=False, server_default=sa.text("false")
        ),
        sa.Column("status", sa.String(length=20), nullable=False),
        sa.Column("created_by", sa.UUID(), nullable=True),
        sa.Column(
            "created_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.Column(
            "updated_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("now()"),
            nullable=False,
        ),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index(
        "ix_inbox_messages_recipient_status_created",
        "inbox_messages",
        ["recipient_id", "status", "created_at"],
        unique=False,
    )
    op.execute(
        "ALTER TABLE inbox_messages ADD CONSTRAINT chk_inbox_message_type CHECK (message_type IN ('friend_request', 'calendar', 'system', 'group'))"
    )
    op.execute(
        "ALTER TABLE inbox_messages ADD CONSTRAINT chk_inbox_message_status CHECK (status IN ('pending', 'accepted', 'rejected', 'dismissed'))"
    )
    op.execute(
        "ALTER TABLE inbox_messages ADD CONSTRAINT chk_inbox_message_sender CHECK ((message_type = 'system' AND sender_id IS NULL) OR (message_type <> 'system' AND sender_id IS NOT NULL))"
    )
    op.execute(
        "ALTER TABLE inbox_messages ADD CONSTRAINT chk_inbox_message_friendship CHECK ((message_type = 'friend_request' AND friendship_id IS NOT NULL) OR (message_type <> 'friend_request' AND friendship_id IS NULL))"
    )
    op.execute(
        "ALTER TABLE inbox_messages ADD CONSTRAINT chk_inbox_message_schedule_item CHECK ((message_type = 'calendar' AND schedule_item_id IS NOT NULL) OR (message_type <> 'calendar' AND schedule_item_id IS NULL))"
    )
    op.execute(
        "ALTER TABLE inbox_messages ADD CONSTRAINT chk_inbox_message_group CHECK ((message_type = 'group' AND group_id IS NOT NULL) OR (message_type <> 'group' AND group_id IS NULL))"
    )
    op.execute(
        "ALTER TABLE inbox_messages ADD CONSTRAINT chk_inbox_message_system_fields CHECK ((message_type = 'system' AND friendship_id IS NULL AND schedule_item_id IS NULL AND group_id IS NULL) OR (message_type <> 'system' AND (friendship_id IS NOT NULL OR schedule_item_id IS NOT NULL OR group_id IS NOT NULL)))"
    )
    op.create_foreign_key(
        "fk_inbox_messages_recipient_id",
        "inbox_messages",
        "users",
        ["recipient_id"],
        ["id"],
        referent_schema="auth",
        ondelete="CASCADE",
    )
    op.create_foreign_key(
        "fk_inbox_messages_sender_id",
        "inbox_messages",
        "users",
        ["sender_id"],
        ["id"],
        referent_schema="auth",
        ondelete="SET NULL",
    )
    op.create_foreign_key(
        "fk_inbox_messages_friendship_id",
        "inbox_messages",
        "friendships",
        ["friendship_id"],
        ["id"],
        ondelete="CASCADE",
    )
    op.create_foreign_key(
        "fk_inbox_messages_schedule_item_id",
        "inbox_messages",
        "schedule_items",
        ["schedule_item_id"],
        ["id"],
        ondelete="CASCADE",
    )
    op.create_foreign_key(
        "fk_inbox_messages_group_id",
        "inbox_messages",
        "groups",
        ["group_id"],
        ["id"],
        ondelete="CASCADE",
    )
    op.create_foreign_key(
        "fk_inbox_messages_created_by",
        "inbox_messages",
        "users",
        ["created_by"],
        ["id"],
        referent_schema="auth",
        ondelete="SET NULL",
    )
    _enable_rls("inbox_messages")


def downgrade() -> None:
    _drop_rls("inbox_messages")
    op.drop_constraint(
        "fk_inbox_messages_created_by", "inbox_messages", type_="foreignkey"
    )
    op.drop_constraint(
        "fk_inbox_messages_group_id", "inbox_messages", type_="foreignkey"
    )
    op.drop_constraint(
        "fk_inbox_messages_schedule_item_id", "inbox_messages", type_="foreignkey"
    )
    op.drop_constraint(
        "fk_inbox_messages_friendship_id", "inbox_messages", type_="foreignkey"
    )
    op.drop_constraint(
        "fk_inbox_messages_sender_id", "inbox_messages", type_="foreignkey"
    )
    op.drop_constraint(
        "fk_inbox_messages_recipient_id", "inbox_messages", type_="foreignkey"
    )
    op.drop_table("inbox_messages")

    _drop_rls("todo_sources")
    op.drop_constraint(
        "fk_todo_sources_schedule_item_id", "todo_sources", type_="foreignkey"
    )
    op.drop_constraint("fk_todo_sources_todo_id", "todo_sources", type_="foreignkey")
    op.drop_table("todo_sources")

    _drop_rls("todos")
    op.drop_constraint("fk_todos_created_by", "todos", type_="foreignkey")
    op.drop_constraint("fk_todos_owner_id", "todos", type_="foreignkey")
    op.drop_table("todos")

    _drop_rls("schedule_subscriptions")
    op.drop_constraint(
        "fk_schedule_subscriptions_created_by",
        "schedule_subscriptions",
        type_="foreignkey",
    )
    op.drop_constraint(
        "fk_schedule_subscriptions_subscriber_id",
        "schedule_subscriptions",
        type_="foreignkey",
    )
    op.drop_constraint(
        "fk_schedule_subscriptions_item_id",
        "schedule_subscriptions",
        type_="foreignkey",
    )
    op.drop_table("schedule_subscriptions")

    _drop_rls("schedule_items")
    op.drop_constraint(
        "fk_schedule_items_created_by", "schedule_items", type_="foreignkey"
    )
    op.drop_constraint(
        "fk_schedule_items_owner_id", "schedule_items", type_="foreignkey"
    )
    op.drop_table("schedule_items")


def _enable_rls(table_name: str) -> None:
    for role in ["anon", "authenticated"]:
        for action in ["select", "insert", "update", "delete"]:
            op.execute(
                f"DROP POLICY IF EXISTS {role}_{action}_{table_name} ON {table_name}"
            )
    op.execute(f"ALTER TABLE {table_name} ENABLE ROW LEVEL SECURITY")
    for role in ["anon", "authenticated"]:
        op.execute(
            f"CREATE POLICY {role}_select_{table_name} ON {table_name} FOR SELECT TO {role} USING (false)"
        )
        op.execute(
            f"CREATE POLICY {role}_insert_{table_name} ON {table_name} FOR INSERT TO {role} WITH CHECK (false)"
        )
        op.execute(
            f"CREATE POLICY {role}_update_{table_name} ON {table_name} FOR UPDATE TO {role} USING (false) WITH CHECK (false)"
        )
        op.execute(
            f"CREATE POLICY {role}_delete_{table_name} ON {table_name} FOR DELETE TO {role} USING (false)"
        )


def _drop_rls(table_name: str) -> None:
    for role in ["anon", "authenticated"]:
        op.execute(f"DROP POLICY IF EXISTS {role}_delete_{table_name} ON {table_name}")
        op.execute(f"DROP POLICY IF EXISTS {role}_update_{table_name} ON {table_name}")
        op.execute(f"DROP POLICY IF EXISTS {role}_insert_{table_name} ON {table_name}")
        op.execute(f"DROP POLICY IF EXISTS {role}_select_{table_name} ON {table_name}")
    op.execute(f"ALTER TABLE {table_name} DISABLE ROW LEVEL SECURITY")
