"""create template_trees and platform_steps global content tables

Revision ID: 3a40fe11b427
Revises: 2c6aabd89bc6
Create Date: 2026-04-09 00:00:00.000000

These tables hold platform-owned content that is readable by all
authenticated users. No account_id. No RLS. Ever.
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import UUID, JSONB

revision: str = '3a40fe11b427'
down_revision: Union[str, None] = '2c6aabd89bc6'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    # ── Create template_trees ─────────────────────────────────────────────────
    op.create_table(
        'template_trees',
        sa.Column('id', UUID(), primary_key=True),
        sa.Column('name', sa.String(255), nullable=False),
        sa.Column('description', sa.Text(), nullable=True),
        sa.Column('category', sa.String(100), nullable=True),
        sa.Column('tree_type', sa.String(20), nullable=False),
        sa.Column('tree_structure', JSONB(), nullable=False),
        sa.Column('tags', JSONB(), nullable=False, server_default='[]'),
        sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true'),
        sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
        sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
        sa.Column('source_tree_id', UUID(), sa.ForeignKey('trees.id', ondelete='SET NULL'), nullable=True),
    )
    op.create_index('ix_template_trees_tree_type', 'template_trees', ['tree_type'])

    # ── Create platform_steps ────────────────────────────────────────────────
    op.create_table(
        'platform_steps',
        sa.Column('id', UUID(), primary_key=True),
        sa.Column('title', sa.String(255), nullable=False),
        sa.Column('step_type', sa.String(50), nullable=False),
        sa.Column('content', JSONB(), nullable=False),
        sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true'),
        sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
        sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
        sa.Column('source_step_id', UUID(), sa.ForeignKey('step_library.id', ondelete='SET NULL'), nullable=True),
    )
    op.create_index('ix_platform_steps_step_type', 'platform_steps', ['step_type'])

    # ── Copy is_default=TRUE trees → template_trees ─────────────────────────
    # Note: trees.tags is a relationship via tree_tags join table — no direct column.
    # Aggregate tag names via a correlated subquery.
    op.execute("""
        INSERT INTO template_trees
            (id, name, description, category, tree_type, tree_structure,
             tags, is_active, created_at, updated_at, source_tree_id)
        SELECT
            gen_random_uuid(), t.name, t.description, t.category, t.tree_type,
            t.tree_structure,
            COALESCE(
                (SELECT jsonb_agg(tt.name ORDER BY tt.name)
                 FROM tree_tag_assignments ta
                 JOIN tree_tags tt ON tt.id = ta.tag_id
                 WHERE ta.tree_id = t.id),
                '[]'::jsonb
            ),
            t.is_active,
            COALESCE(t.created_at, NOW()), COALESCE(t.updated_at, NOW()), t.id
        FROM trees t
        WHERE t.is_default = TRUE
    """)

    # ── Copy visibility='public' steps → platform_steps ─────────────────────
    op.execute("""
        INSERT INTO platform_steps
            (id, title, step_type, content, is_active, created_at, updated_at, source_step_id)
        SELECT
            gen_random_uuid(), title, step_type, content, is_active,
            COALESCE(created_at, NOW()), COALESCE(updated_at, NOW()), id
        FROM step_library
        WHERE visibility = 'public'
    """)

    # ── Create platform sentinel account ─────────────────────────────────────
    op.execute("""
        INSERT INTO accounts (id, name, display_code, created_at, updated_at)
        VALUES (
            '00000000-0000-0000-0000-000000000001',
            'ResolutionFlow Platform',
            'PLATFORM',
            NOW(),
            NOW()
        )
        ON CONFLICT (id) DO NOTHING
    """)

    # ── Assign is_default trees to platform account ──────────────────────────
    op.execute("""
        UPDATE trees
        SET account_id = '00000000-0000-0000-0000-000000000001'
        WHERE is_default = TRUE
          AND account_id IS NULL
    """)

    # ── Assign remaining trees to their author's account ─────────────────────
    # Handles trees with no team_id that aren't is_default (e.g. inactive test
    # trees, trees created before the team system existed).
    op.execute("""
        UPDATE trees
        SET account_id = u.account_id
        FROM users u
        WHERE trees.author_id = u.id
          AND trees.account_id IS NULL
          AND u.account_id IS NOT NULL
    """)

    # ── Final fallback: any still-NULL trees go to platform account ───────────
    # Covers trees whose author has no account (seeded content, system rows).
    op.execute("""
        UPDATE trees
        SET account_id = '00000000-0000-0000-0000-000000000001'
        WHERE account_id IS NULL
    """)

    # ── Assign global categories/tags/steps to platform account ─────────────
    op.execute("""
        UPDATE tree_categories
        SET account_id = '00000000-0000-0000-0000-000000000001'
        WHERE account_id IS NULL
    """)

    op.execute("""
        UPDATE tree_tags
        SET account_id = '00000000-0000-0000-0000-000000000001'
        WHERE account_id IS NULL
    """)

    op.execute("""
        UPDATE step_categories
        SET account_id = '00000000-0000-0000-0000-000000000001'
        WHERE account_id IS NULL
    """)

    op.execute("""
        UPDATE step_library
        SET account_id = '00000000-0000-0000-0000-000000000001'
        WHERE account_id IS NULL
    """)

    # ── Verify zero NULLs in all 5 tables ───────────────────────────────────
    for table in ('trees', 'tree_categories', 'tree_tags', 'step_categories', 'step_library'):
        result = op.get_bind().execute(
            sa.text(f"SELECT COUNT(*) FROM {table} WHERE account_id IS NULL")
        )
        count = result.scalar()
        if count > 0:
            raise RuntimeError(
                f"ROLLBACK: {count} NULL account_id rows remain in {table} "
                "after platform account assignment. Investigate before re-running."
            )


def downgrade() -> None:
    platform_id = '00000000-0000-0000-0000-000000000001'
    for table in ('trees', 'tree_categories', 'tree_tags', 'step_categories', 'step_library'):
        op.execute(f"UPDATE {table} SET account_id = NULL WHERE account_id = '{platform_id}'")

    op.execute(f"DELETE FROM accounts WHERE id = '{platform_id}'")
    op.drop_index('ix_platform_steps_step_type', table_name='platform_steps')
    op.drop_index('ix_template_trees_tree_type', table_name='template_trees')
    op.drop_table('platform_steps')
    op.drop_table('template_trees')
