"""FINDING C W1 (Category B) — base_id_required + state_variant/O00 character
required_ref emit 수정.

W0 triage: `build_asset_requirements` 의 `for pair in outlook_pairs` 루프에서
`if cid in _state_variant: continue` 와 `if oid == "O00": continue` 가
`base_id_required` policy 분기보다 앞 → base_id_required 이면서 state_variant
이거나 O00 인 캐릭터는 `{kind:character}` required_ref 가 emit 0 → resolver
attach 0 → validator step 6 `character` fail.

fix: policy 를 exclusion 이전에 resolve. `_state_variant`/`O00` exclusion 은
`character_outlook` 위조 방지용 → `id_and_outlook_required` 분기에만 적용.
base_id_required 는 state_variant/O00 무관 `{kind:character}` emit.

대상 실패 still: b512587c S11.10 / 68eb2cb9 S11.14 (둘 다 C08 base_id_required
+ dead state-variant). Gates G1-G8.
"""
from __future__ import annotations

import pytest

from app.core.ref_contract_validator import RefContractError, validate_attached_refs
from app.core.steps.render_prompt_card import build_asset_requirements
from app.core.subject_reference_policy import SubjectReferencePolicy
from app.services.scene_reference_service import SceneReferenceService


def _policy(subject_id: str, policy: str) -> SubjectReferencePolicy:
    return SubjectReferencePolicy(
        subject_id=subject_id,
        policy_type="identity_reference",
        policy=policy,
        reason="(test fixture)",
    )


# ── G1: base_id_required + state_variant → emits {kind:character} ──────
def test_g1_base_id_required_state_variant_emits_character() -> None:
    """base_id_required 이면서 state_variant 인 캐릭터 → required_refs 에
    {kind:character} emit (현재 버그: state_variant continue 가 skip)."""
    a = build_asset_requirements(
        visible_entities=["C08"],
        outlook_pairs=[{"character_id": "C08", "outlook_id": "O04"}],
        bg_id=None, is_close_framing=True, background_mode_on=True,
        used_outlook_pairs={("C08", "O04")},
        state_variant_chars={"C08"},
        render_contracts=[],
        policy_map={"C08": _policy("C08", "base_id_required")},
    )
    refs = a["required_refs"]
    assert {"kind": "character", "id": "C08", "policy": "required"} in refs
    assert not any(r["kind"] == "character_outlook" for r in refs)


# ── G2: base_id_required + O00 → emits {kind:character} ────────────────
def test_g2_base_id_required_o00_emits_character() -> None:
    """base_id_required 이면서 outlook O00 인 캐릭터 → {kind:character} emit
    (현재 버그: O00 continue 가 skip). production-shape narrow-RPC filter
    (used_outlook_pairs) 하에서 검증 — Codex plan review 권고."""
    a = build_asset_requirements(
        visible_entities=["C08"],
        outlook_pairs=[{"character_id": "C08", "outlook_id": "O00"}],
        bg_id=None, is_close_framing=False, background_mode_on=False,
        used_outlook_pairs={("C08", "O00")},
        render_contracts=[],
        policy_map={"C08": _policy("C08", "base_id_required")},
    )
    refs = a["required_refs"]
    assert {"kind": "character", "id": "C08", "policy": "required"} in refs


# ── G3: regression — id_and_outlook + state_variant → still excluded ──
def test_g3_id_and_outlook_state_variant_still_excluded() -> None:
    """회귀 가드: id_and_outlook_required 이면서 state_variant 인 캐릭터 →
    character_outlook 도 character 도 emit 안 함 (resolver 가 character_state
    ref attach — character_outlook 위조 금지, validator P2)."""
    a = build_asset_requirements(
        visible_entities=["C01"],
        outlook_pairs=[{"character_id": "C01", "outlook_id": "O02"}],
        bg_id=None, is_close_framing=False, background_mode_on=False,
        state_variant_chars={"C01"},
        render_contracts=[],
        policy_map={"C01": _policy("C01", "id_and_outlook_required")},
    )
    refs = a["required_refs"]
    assert not any(r["kind"] in ("character", "character_outlook") for r in refs)


# ── G4: regression — id_and_outlook + O00 → still excluded ────────────
def test_g4_id_and_outlook_o00_still_excluded() -> None:
    """회귀 가드: id_and_outlook_required 이면서 O00 → character_outlook 미emit
    (resolver O00 분기가 base ref attach)."""
    a = build_asset_requirements(
        visible_entities=["C01"],
        outlook_pairs=[{"character_id": "C01", "outlook_id": "O00"}],
        bg_id=None, is_close_framing=False, background_mode_on=False,
        render_contracts=[],
        policy_map={"C01": _policy("C01", "id_and_outlook_required")},
    )
    refs = a["required_refs"]
    assert not any(r["kind"] == "character_outlook" for r in refs)


# ── G5: regression — base_id_required 평범 (no state_variant) ──────────
def test_g5_base_id_required_plain_still_emits_character() -> None:
    """회귀 가드: state_variant 아닌 base_id_required → {kind:character} emit
    (W2 기존 동작 보존)."""
    a = build_asset_requirements(
        visible_entities=["C07"],
        outlook_pairs=[{"character_id": "C07", "outlook_id": "O09"}],
        bg_id=None, is_close_framing=False, background_mode_on=False,
        render_contracts=[],
        policy_map={"C07": _policy("C07", "base_id_required")},
    )
    refs = a["required_refs"]
    assert {"kind": "character", "id": "C07", "policy": "required"} in refs


# ── G6: regression — generic_descriptor_allowed + state_variant ───────
def test_g6_generic_descriptor_state_variant_emits_neither() -> None:
    """회귀 가드: generic_descriptor_allowed 이면 state_variant 든 아니든
    character / character_outlook 둘 다 emit 안 함."""
    a = build_asset_requirements(
        visible_entities=["C03"],
        outlook_pairs=[{"character_id": "C03", "outlook_id": "O05"}],
        bg_id=None, is_close_framing=False, background_mode_on=False,
        state_variant_chars={"C03"},
        render_contracts=[],
        policy_map={"C03": _policy("C03", "generic_descriptor_allowed")},
    )
    refs = a["required_refs"]
    assert not any(r["kind"] in ("character", "character_outlook") for r in refs)


# ── G7: dedup — base_id_required + state_variant, 복수 outlook_pair ────
def test_g7_base_id_required_state_variant_dedup() -> None:
    """base_id_required + state_variant 캐릭터가 outlook_pairs 에 복수 등장 →
    {kind:character} 는 1회만 emit (dedup 보존)."""
    a = build_asset_requirements(
        visible_entities=["C08"],
        outlook_pairs=[
            {"character_id": "C08", "outlook_id": "O04"},
            {"character_id": "C08", "outlook_id": "O07"},
        ],
        bg_id=None, is_close_framing=False, background_mode_on=False,
        used_outlook_pairs={("C08", "O04"), ("C08", "O07")},
        state_variant_chars={"C08"},
        render_contracts=[],
        policy_map={"C08": _policy("C08", "base_id_required")},
    )
    char_refs = [r for r in a["required_refs"] if r["kind"] == "character" and r["id"] == "C08"]
    assert len(char_refs) == 1


# ── G8: repro — case 2/3 end-to-end (build → resolve → validate) ──────
def test_g8_finding_c_case2_3_repro_no_violation() -> None:
    """실패 still b512587c/68eb2cb9 재현: C08 base_id_required + state_variant,
    bare C08 t2i_prompt, reference_phrase_kinds=['character'] → fix 후
    REF_CONTRACT_VIOLATION 0."""
    policy_map = {"C08": _policy("C08", "base_id_required")}
    ar = build_asset_requirements(
        visible_entities=["C08"],
        outlook_pairs=[{"character_id": "C08", "outlook_id": "O04"}],
        bg_id=None, is_close_framing=True, background_mode_on=True,
        used_outlook_pairs={("C08", "O04")},
        state_variant_chars={"C08"},
        render_contracts=[],
        policy_map=policy_map,
    )
    assert {"kind": "character", "id": "C08", "policy": "required"} in ar["required_refs"]

    svc = SceneReferenceService.__new__(SceneReferenceService)
    prompt = "Photorealistic cinematic still. C08, an East Asian woman, lying motionless."
    payload = svc.resolve_refs_for_prompt(
        t2i_prompt=prompt,
        visible_entities=[
            {"id": "uuid-c08", "short_id": "C08", "entity_type": "character", "name": "Mother"},
        ],
        scene_ref_image_map={"uuid-c08": b"c08_face"},
        entity_lookup={},
        required_refs=ar["required_refs"],
    )
    assert ("character", "C08") in payload.attached_meta

    validate_attached_refs(
        {"asset_requirements": ar},
        labeled_refs=list(payload.labeled_refs),
        attached_meta=list(payload.attached_meta),
        prompt=prompt, is_close_framing=True,
        reference_phrase_kinds=["character"],
    )
