"""Cross-module enum SOT alignment lock-in — Patch C / Area A + Area C.

producer (shot_staging.ORIENTATION_REQUIRED_CLASSES) 와 consumer
(detail_steps._DIRECTIVE_TEMPLATES.keys()) 가 동일 enum literal 집합을
참조하는지 검증. drift 발생 시 fail-fast.

Area A 의 핵심 SOT 는 LLM emit directionality_class enum 이다.
producer 가 검사하는 'orientation 필수' enum literal 과 consumer 가
디렉티브 분기에 쓰는 enum literal 이 한 곳에서만 정의되어야 향후
class 추가 / 삭제 시 두 site 가 동시 갱신된다.

Area C 추가 (2026-05-12):
- _ALLOWED_DIRECTIONALITY_CLASSES (render_prompt_card) ↔ shot_staging
  schema.json 의 5 enum 정확 동일.
- _REPRODUCTION_SURFACE_CLASSES (render_prompt_card) ↔
  ORIENTATION_REQUIRED_CLASSES (shot_staging) 정확 동일.
"""
import json
import pathlib

from app.core.steps.detail_steps import _DIRECTIVE_TEMPLATES
from app.core.steps.render_prompt_card import (
    _ALLOWED_DIRECTIONALITY_CLASSES,
    _REPRODUCTION_SURFACE_CLASSES,
)
from app.modules.pipeline.shot_staging import ORIENTATION_REQUIRED_CLASSES


def test_orientation_required_classes_aligned_with_directive_templates():
    """producer 의 required classes set == consumer 의 directive template keys."""
    producer = frozenset(ORIENTATION_REQUIRED_CLASSES)
    consumer = frozenset(_DIRECTIVE_TEMPLATES.keys())
    assert producer == consumer, (
        "Patch C / Area A enum SOT drift: "
        f"producer={sorted(producer)!r}, consumer={sorted(consumer)!r}. "
        "shot_staging.ORIENTATION_REQUIRED_CLASSES 와 "
        "detail_steps._DIRECTIVE_TEMPLATES.keys() 가 동일 enum literal "
        "집합 (content_surface, reflective_surface) 을 참조해야 합니다."
    )


# Area C migration 2026-05-12 — 추가 두 lock-in.

_SHOT_STAGING_SCHEMA_PATH = (
    pathlib.Path(__file__).parent.parent.parent.parent
    / "prompts" / "_base" / "shot_staging" / "9.202605121441" / "schema.json"
)


def _find_directionality_enum(node) -> list[str] | None:
    """Recursively walk JSON schema, return first node with name
    `directionality_class` (object with `enum` key + description starting
    with 'Semantic classification').

    Resilient to schema reorganization — does not assume fixed path.
    """
    if isinstance(node, dict):
        # direct check — current schema embeds enum under properties.shots
        # .items.properties.key_bg_elements.items.properties.directionality_class
        if (
            "enum" in node
            and isinstance(node.get("description"), str)
            and node["description"].startswith("Semantic classification")
        ):
            return list(node["enum"])
        for v in node.values():
            found = _find_directionality_enum(v)
            if found is not None:
                return found
    elif isinstance(node, list):
        for v in node:
            found = _find_directionality_enum(v)
            if found is not None:
                return found
    return None


def test_allowed_directionality_classes_aligned_with_schema():
    """render_prompt_card._ALLOWED_DIRECTIONALITY_CLASSES 가
    shot_staging schema 의 directionality_class enum 정확 동일.

    drift 차단 — schema 추가/삭제 시 코드 site 동시 갱신 강제.
    """
    assert _SHOT_STAGING_SCHEMA_PATH.exists(), (
        f"shot_staging schema not found at {_SHOT_STAGING_SCHEMA_PATH}"
    )
    schema = json.loads(_SHOT_STAGING_SCHEMA_PATH.read_text(encoding="utf-8"))
    schema_enum = _find_directionality_enum(schema)
    assert schema_enum is not None, (
        f"directionality_class enum not found in {_SHOT_STAGING_SCHEMA_PATH}"
    )
    schema_set = frozenset(schema_enum)
    code_set = frozenset(_ALLOWED_DIRECTIONALITY_CLASSES)
    assert schema_set == code_set, (
        "Area C enum SOT drift: "
        f"schema={sorted(schema_set)!r}, "
        f"render_prompt_card._ALLOWED_DIRECTIONALITY_CLASSES={sorted(code_set)!r}. "
        f"shot_staging/9.202605121441/schema.json 의 directionality_class enum 과 "
        f"코드 site 가 동시 갱신되어야 합니다."
    )


def test_reproduction_surface_classes_aligned_with_orientation_required():
    """render_prompt_card._REPRODUCTION_SURFACE_CLASSES ==
    shot_staging.ORIENTATION_REQUIRED_CLASSES (set 동일).

    Area C SOT lock-in — reproduction surface 판정 set 과 orientation
    필수 set 이 동일해야 reproduction_surface_rule.applies bool derive
    가 staging contract 와 align.
    """
    code_set = frozenset(_REPRODUCTION_SURFACE_CLASSES)
    staging_set = frozenset(ORIENTATION_REQUIRED_CLASSES)
    assert code_set == staging_set, (
        "Area C reproduction surface set drift: "
        f"_REPRODUCTION_SURFACE_CLASSES={sorted(code_set)!r}, "
        f"ORIENTATION_REQUIRED_CLASSES={sorted(staging_set)!r}. "
        f"두 set 이 동일해야 reproduction_surface_rule.applies 가 "
        f"staging contract (orientation 필수 클래스) 와 align."
    )
