"""Stamp Feature Pack contract V2 onto catalog composition JSON.

Does not replace packs. Adds typed requires, provider_bindings, provides, events,
entity field protection, and workflow graphs for approval/support.

Revision ID: 134
Revises: 133
"""
from datetime import datetime, timezone

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

revision = "134"
down_revision = "133"
branch_labels = None
depends_on = None

feature_packs = sa.table(
    "feature_packs",
    sa.column("slug", sa.String),
    sa.column("composition", postgresql.JSONB),
    sa.column("access_policy", postgresql.JSONB),
    sa.column("version", sa.String),
    sa.column("updated_at", sa.DateTime(timezone=True)),
)


def upgrade() -> None:
    from src.domain.feature_packs.normalize import stamp_composition

    conn = op.get_bind()
    rows = conn.execute(
        sa.select(
            feature_packs.c.slug,
            feature_packs.c.composition,
            feature_packs.c.access_policy,
            feature_packs.c.version,
        )
    ).fetchall()
    now = datetime.now(timezone.utc)
    for slug, composition, access_policy, version in rows:
        if not isinstance(composition, dict):
            continue
        stamped = stamp_composition(
            slug,
            composition,
            access_policy=access_policy if isinstance(access_policy, dict) else None,
            implementation_version=version,
        )
        conn.execute(
            sa.update(feature_packs)
            .where(feature_packs.c.slug == slug)
            .values(composition=stamped, updated_at=now)
        )


def downgrade() -> None:
    conn = op.get_bind()
    rows = conn.execute(
        sa.select(feature_packs.c.slug, feature_packs.c.composition)
    ).fetchall()
    now = datetime.now(timezone.utc)
    for slug, composition in rows:
        if not isinstance(composition, dict):
            continue
        blob = dict(composition)
        typed = blob.get("requires")
        packs: list[str] = []
        primitives: list[str] = []
        if isinstance(typed, list):
            for item in typed:
                if not isinstance(item, dict):
                    continue
                if item.get("kind") == "feature_pack" and item.get("id"):
                    packs.append(str(item["id"]))
                elif item.get("kind") == "primitive" and item.get("id"):
                    primitives.append(str(item["id"]))
        connections: list[str] = []
        for binding in blob.get("provider_bindings") or []:
            if not isinstance(binding, dict):
                continue
            if not binding.get("configuration_required"):
                continue
            connections.extend(
                str(p) for p in (binding.get("providers") or []) if p
            )
        blob["requires"] = primitives
        if packs:
            blob["requires_packs"] = packs
        if connections:
            blob["requires_connections"] = connections
        blob.pop("schema_version", None)
        blob.pop("provides", None)
        blob.pop("optional_requires", None)
        blob.pop("provider_bindings", None)
        blob.pop("events", None)
        blob.pop("entity_contracts", None)
        blob.pop("workflows", None)
        blob.pop("security", None)
        blob.pop("compatibility", None)
        conn.execute(
            sa.update(feature_packs)
            .where(feature_packs.c.slug == slug)
            .values(composition=blob, updated_at=now)
        )
