"""D5 S8 회귀 canary — production manifest 기반 fail-fast 검증.

S8_Shot4 production 사고 (2026-05-09): C01 zero-gate + D1~D4 fix 적용 후 단건
재호출 시 HTTP 200 통과 + 시각 결함 (출입문이 아닌 사람 가리킴 + bg 누락 +
character outlook 미적용). 진단 결과 = `validate_attached_refs` 의 substring
매칭으로 character description 안 'plain white background' 같은 우연 단어로
false-positive 통과. **identity-level contract 부재**.

본 fixture 는 D5 spec 의 raison d'être — 같은 production manifest + attached
state 가 다시 들어와도 identity check 로 차단됨을 영구 보장.

spec: docs/superpowers/specs/2026-05-09-attached-reference-identity-contract-design.md §AC-1
plan: docs/superpowers/plans/2026-05-09-attached-reference-identity-contract-implementation.md Task 5
"""
from __future__ import annotations

import pytest


@pytest.fixture
def s8_manifest_fixture():
    """S8_Shot4 production manifest 의 RPC subset (canonical list shape)."""
    return {
        "render_prompt_card": {
            "asset_requirements": {
                "required_refs": [
                    {"kind": "character_outlook", "id": "C01O02", "policy": "required"},
                    {"kind": "background", "id": "bg_store_sales_floor_dusk_busy_exit_visible", "policy": "required"},
                ],
                "forbidden_refs": [],
                "readiness_policy": "block_if_missing",
                "constraints": [
                    "do not imply or describe a reference image that is not listed in required_refs (no phantom references)",
                ],
            }
        }
    }


def test_S8_canary_validator_blocks_when_only_C01_base_attached(s8_manifest_fixture):
    """S8 production 결함 시뮬레이션 — composite 부재 + bg 미첨부 + character description
    안 'plain white background' inline → identity 검증으로 차단.

    P2 적용 — base ("character", "C01") 가 outlook ("character_outlook", "C01O02")
    요구를 만족시키지 않음. 첫 번째 raise 는 character_outlook (검사 순서상).
    """
    from app.core.ref_contract_validator import validate_attached_refs, RefContractError

    rpc = s8_manifest_fixture["render_prompt_card"]
    # production 에서 실제 발생한 attached: C01 base 1개만 (composite 부재 fallback)
    labeled_refs = [(
        "Image 1 (character reference): 수리영 — Set in modern mid-2020s, 대한민국. "
        "Passport-style ID photo, head and upper chest visible, plain white background. "
        "Korean early 20s female, long black hair, soft facial structure, dark eyes.",
        b"c01_base_bytes",
    )]
    attached_meta = [("character", "C01")]  # P2: 별 kind (composite 부재 fallback)

    with pytest.raises(RefContractError) as exc_info:
        validate_attached_refs(
            rpc, labeled_refs, attached_meta,
            prompt="dummy",
            is_close_framing=False,
            chain_bg_lookup=lambda _: None,
            reference_phrase_kinds=[],
        )

    assert "character_outlook" in str(exc_info.value)
    assert "'C01O02'" in str(exc_info.value)


def test_S8_canary_pre_d5_substring_logic_would_have_passed():
    """D5 이전 substring 검증의 false-positive evidence 영구 기록 (회귀 방지).

    이 assertion 들은 변경하지 말 것 — D5 의 raison d'être 가 무엇이었는지를
    code 안에 영원히 남기는 의도. 같은 substring 패턴이 다시 nonzero match 를
    만든다면 D5 이전 logic 으로 회귀했음을 즉시 알 수 있다.
    """
    label = (
        "Image 1 (character reference): 수리영 — Set in modern mid-2020s, "
        "대한민국. Passport-style ID photo, head and upper chest visible, "
        "plain white background. Korean early 20s female, long black hair, "
        "soft facial structure, dark eyes."
    )
    # D5 이전 substring 검사 — 이 두 토큰이 자연어 description 안에 우연히 들어가서
    # validator 가 "character ref present" + "background ref present" 로 통과시켰음.
    assert "character" in label.lower(), \
        "사후 evidence: pre-D5 character 검사 false-positive (label 안 'character reference' 자체 inline)"
    assert "background" in label.lower(), \
        "사후 evidence: pre-D5 background 검사 false-positive (label 안 'plain white background' 자연어 inline)"


def test_S8_canary_composite_outlook_attached_unblocks(s8_manifest_fixture):
    """사후 fix evidence — D5 후 composite outlook 이 정상 첨부되면 통과.

    fix 는 "차단" 만이 아니라 "올바른 attached 일 때 통과" 도 보장한다는 invariant.
    사용자 binding G2 (character_outlook strict) 가 정상 첨부 시에는 trigger 안 됨.
    """
    from app.core.ref_contract_validator import validate_attached_refs

    rpc = s8_manifest_fixture["render_prompt_card"]
    # composite + chain_bg 둘 다 정상 첨부된 경우 (fix 후 정상 path)
    labeled_refs = [
        ("Image 1 (character reference): 수리영 in outfit O02", b"c01_outlook_bytes"),
        ("Image 2 (background): supermarket aisle dusk", b"bg_bytes"),
    ]
    attached_meta = [
        ("character_outlook", "C01O02"),
        ("background", "bg_store_sales_floor_dusk_busy_exit_visible"),
    ]
    # raise 없으면 통과
    validate_attached_refs(
        rpc, labeled_refs, attached_meta,
        prompt="Two figures share the supermarket aisle.",
        is_close_framing=False,
        chain_bg_lookup=lambda _: None,
        reference_phrase_kinds=[],
    )


def test_S8_canary_prev_shot_lineage_substitute_unblocks(s8_manifest_fixture):
    """fix 후 prev_shot lineage 일치 시 background 요구 통과 (chain_bg 부재 환경).

    field-deployed projects 에서 chain_bg PNG 가 없는 경우 prev_shot 으로 lineage
    substitute. chain_bg_lookup 이 bg_id → loc_id 역참조 후 attached
    ("background_prev_shot", loc) 가 일치하면 통과.
    """
    from app.core.ref_contract_validator import validate_attached_refs

    rpc = s8_manifest_fixture["render_prompt_card"]
    labeled_refs = [
        ("Image 1 (character reference): 수리영 in outfit O02", b"c01_outlook_bytes"),
        ("Image 2: previous shot at supermarket entrance — wide framing", b"prev_bytes"),
    ]
    attached_meta = [
        ("character_outlook", "C01O02"),
        ("background_prev_shot", "L09"),
    ]
    chain_bg_lookup = lambda bg_id: (
        "L09" if bg_id == "bg_store_sales_floor_dusk_busy_exit_visible" else None
    )
    validate_attached_refs(
        rpc, labeled_refs, attached_meta,
        prompt="Two figures share the aisle.",
        is_close_framing=False,
        chain_bg_lookup=chain_bg_lookup,
        reference_phrase_kinds=[],
    )
