"""Area #1 — producer → card → consumer e2e integration.

shot_staging v12 emits subject_reference_policy[] → render_prompt_card normalizes +
injects id_policy.subject_reference_policy → visible_entities_validator + scene_detail
prompt consume via per-subject IdUsageRule matrix.

시나리오 의존성 0 — placeholder C01/C02/O01 만 사용.
"""
import pytest
from app.core.errors import AppError


def test_e2e_producer_to_card_inject():
    """Producer emit → card payload 안 sub-field 존재."""
    from app.core.steps.render_prompt_card import build_id_policy
    from app.core.subject_reference_policy import (
        normalize_subject_reference_policy_items,
        serialize_subject_reference_policy_map,
    )
    raw_items = [{
        "subject_id": "C01",
        "policy_type": "identity_reference",
        "policy": "base_id_required",
        "reason": "test",
    }]
    policy_map = normalize_subject_reference_policy_items(
        raw_items, visible_subject_ids={"C01"}, where="e2e_test"
    )
    serialized = serialize_subject_reference_policy_map(policy_map)
    ip = build_id_policy(
        visible_entities=["C01"],
        outlook_pairs=[{"character_id": "C01", "outlook_id": "O01"}],
        perception_mode="direct",
        key_bg_elements=[],
        subject_reference_policies=serialized,
    )
    assert "subject_reference_policy" in ip
    assert len(ip["subject_reference_policy"]) == 1
    assert ip["subject_reference_policy"][0]["policy"] == "base_id_required"


def test_e2e_v12_staging_missing_field_caller_appends_error_marker():
    """v12 staging dict + subject_reference_policy field 누락 → caller AppError ".field_missing" (Gate 4 second-line defense).

    W2 atomic 에서 v12 schema 에 field required 추가했으므로 producer LLM emit
    단계에서 schema validation 으로 1차 차단. caller (build_render_prompt_card)
    의 second-line defense — schema validation 우회 path (legacy / mocked dict)
    에서도 field 누락 시 fail-fast.
    """
    from app.core.steps.render_prompt_card import build_render_prompt_card
    # Per-shot staging dict — subject_reference_policy field 자체 누락 (contract violation).
    staging_missing_field = {
        "camera_direction": "medium shot",
        "framing_scale": "close",
        "lighting_mood": "warm",
        "key_bg_elements": [],
        # subject_reference_policy 누락 — v12 contract violation
    }
    with pytest.raises(AppError) as exc:
        build_render_prompt_card(
            scene_index=1,
            shot_index=1,
            seg={"index": 1, "text": "test scene"},
            shot_info={"shot_index": 1, "camera_direction": "medium shot", "primary_subject": "test"},
            visible_entities=["C01"],
            outlook_pairs=[{"character_id": "C01", "outlook_id": "O01"}],
            perception_mode="direct",
            staging=staging_missing_field,
            bg_id="cb_test",
            bg_owned=[],
            bg_camera_meta={
                "camera_position": "south",
                "camera_height": "eye-level",
                "lens_hint": "35mm",
                "framing_notes": "wide",
            },
            bg_guide=None,
            is_close_framing=True,
            background_mode_on=False,
            fixed_elements=[],
            previous_shot_refs=[],
            forward_zoom_targets=[],
        )
    assert "field_missing" in exc.value.code, (
        f"caller-side fail-fast 누락 — Gate 4 위반 위험: {exc.value.code}"
    )


def test_e2e_legacy_staging_not_applicable_path_graceful():
    """staging=None marker → helper graceful empty (items=None pass)."""
    from app.core.subject_reference_policy import normalize_subject_reference_policy_items
    # caller 가 명시적으로 items=None pass (legacy / staging_not_applicable path)
    result = normalize_subject_reference_policy_items(
        None, visible_subject_ids=None, where="legacy_path"
    )
    assert result == {}


def test_e2e_per_subject_no_leak():
    """한 subject 의 policy 가 다른 subject 의 default 를 덮어쓰지 않음."""
    from app.core.subject_reference_policy import (
        normalize_subject_reference_policy_items,
        get_subject_reference_policy_or_default,
    )
    items = [{
        "subject_id": "C01",
        "policy_type": "identity_reference",
        "policy": "generic_descriptor_allowed",
        "reason": "C01 only",
    }]
    pmap = normalize_subject_reference_policy_items(
        items, visible_subject_ids={"C01", "C02"}, where="test"
    )
    # C01: emit 된 policy
    p1 = get_subject_reference_policy_or_default(pmap, "C01", where="test")
    assert p1.policy == "generic_descriptor_allowed"
    # C02: default (omitted, exceptions-first)
    p2 = get_subject_reference_policy_or_default(pmap, "C02", where="test")
    assert p2.policy == "id_and_outlook_required"
