"""Add app_config table (structured settings to replace key-value system_settings).

app_config stores settings grouped in JSONB columns:
  - render: thumbnail_renderer, engine, samples, stl_quality, etc.
  - storage: upload_dir, minio_url, max_upload_size_mb, etc.
  - notifications: (reserved for Phase G)
  - worker: concurrency, max_concurrent_renders, stall_timeout_minutes
  - billing: (reserved for Phase L)

system_settings table is preserved for backward compatibility during migration.
Existing values are migrated into app_config.

Revision ID: 034
Revises: 033
Create Date: 2026-03-06
"""
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects.postgresql import JSONB, UUID

revision = '034'
down_revision = '033'
branch_labels = None
depends_on = None


def upgrade():
    op.create_table(
        'app_config',
        sa.Column('id', UUID(as_uuid=True), primary_key=True,
                  server_default=sa.text('gen_random_uuid()')),
        sa.Column('version', sa.Integer(), nullable=False, server_default='1'),
        sa.Column('render', JSONB(), nullable=False, server_default='{}'),
        sa.Column('storage', JSONB(), nullable=False, server_default='{}'),
        sa.Column('notifications', JSONB(), nullable=False, server_default='{}'),
        sa.Column('worker', JSONB(), nullable=False, server_default='{}'),
        sa.Column('billing', JSONB(), nullable=False, server_default='{}'),
        sa.Column('updated_at', sa.DateTime(), nullable=False,
                  server_default=sa.text('NOW()')),
    )

    # Migrate existing system_settings into app_config render + worker columns
    op.execute("""
        INSERT INTO app_config (render, worker, updated_at)
        SELECT
            jsonb_build_object(
                'thumbnail_renderer', COALESCE(MAX(CASE WHEN key = 'thumbnail_renderer' THEN value END), 'blender'),
                'blender_engine',     COALESCE(MAX(CASE WHEN key = 'blender_engine' THEN value END), 'cycles'),
                'blender_cycles_samples', COALESCE(MAX(CASE WHEN key = 'blender_cycles_samples' THEN value END), '256')::int,
                'blender_eevee_samples',  COALESCE(MAX(CASE WHEN key = 'blender_eevee_samples' THEN value END), '64')::int,
                'thumbnail_format',   COALESCE(MAX(CASE WHEN key = 'thumbnail_format' THEN value END), 'jpg'),
                'stl_quality',        COALESCE(MAX(CASE WHEN key = 'stl_quality' THEN value END), 'low'),
                'blender_smooth_angle', COALESCE(MAX(CASE WHEN key = 'blender_smooth_angle' THEN value END), '30')::int,
                'cycles_device',      COALESCE(MAX(CASE WHEN key = 'cycles_device' THEN value END), 'auto'),
                'render_backend',     COALESCE(MAX(CASE WHEN key = 'render_backend' THEN value END), 'celery'),
                'product_thumbnail_priority', COALESCE(MAX(CASE WHEN key = 'product_thumbnail_priority' THEN value END), '["latest_render","cad_thumbnail"]')
            ),
            jsonb_build_object(
                'concurrency',              COALESCE(MAX(CASE WHEN key = 'celery_worker_concurrency' THEN value END), '8')::int,
                'max_concurrent_renders',   COALESCE(MAX(CASE WHEN key = 'blender_max_concurrent_renders' THEN value END), '3')::int,
                'render_stall_timeout_minutes', COALESCE(MAX(CASE WHEN key = 'render_stall_timeout_minutes' THEN value END), '120')::int
            ),
            NOW()
        FROM system_settings
        WHERE key IN (
            'thumbnail_renderer', 'blender_engine', 'blender_cycles_samples',
            'blender_eevee_samples', 'thumbnail_format', 'stl_quality',
            'blender_smooth_angle', 'cycles_device', 'render_backend',
            'product_thumbnail_priority', 'celery_worker_concurrency',
            'blender_max_concurrent_renders', 'render_stall_timeout_minutes'
        )
        HAVING COUNT(*) > 0
    """)

    # Insert default row if no system_settings existed
    op.execute("""
        INSERT INTO app_config (render, worker, updated_at)
        SELECT
            '{"thumbnail_renderer": "blender", "blender_engine": "cycles",
              "blender_cycles_samples": 256, "blender_eevee_samples": 64,
              "thumbnail_format": "jpg", "stl_quality": "low",
              "blender_smooth_angle": 30, "cycles_device": "auto",
              "render_backend": "celery",
              "product_thumbnail_priority": ["latest_render", "cad_thumbnail"]}'::jsonb,
            '{"concurrency": 8, "max_concurrent_renders": 3, "render_stall_timeout_minutes": 120}'::jsonb,
            NOW()
        WHERE NOT EXISTS (SELECT 1 FROM app_config)
    """)


def downgrade():
    op.drop_table('app_config')
