"""P8 I-1 — registered pose guide: prompt_service render branch + attach no-op +
ref_contract 방어 + zoomed-frame lock 정렬.

(원본 test_p8_inc3b_pose_sketch.py 는 stash 미포함[untracked 유실]이라 재작성.)
"""
from __future__ import annotations

import pytest

from app.services.prompt_service import (
    REF_ROLE_VALUES,
    make_labeled_ref_payload,
    resolve_ref_roles,
)


def _payload(labeled_refs, ref_roles, ref_role_metadata):
    return make_labeled_ref_payload(
        labeled_refs=labeled_refs,
        ref_roles=ref_roles,
        ref_role_metadata=ref_role_metadata,
        attached_meta=[("x", "y")] * len(labeled_refs),
    )


def test_immobilized_pose_guide_in_enum():
    assert "immobilized_pose_guide" in REF_ROLE_VALUES


def test_render_branch_emits_pose_support_and_real_human():
    """pose/contact/support SOT + 실인간 단언 + insert visible-portion + 마네킹 복사금지."""
    res = resolve_ref_roles(_payload(
        labeled_refs=[("POSE / SUPPORT GUIDE ...", b"\x89PNG")],
        ref_roles=["immobilized_pose_guide"],
        ref_role_metadata=[{"group_id": "g", "character_short_id": "C04",
                            "visible_focus": "tight insert on the resting hand"}],
    ))
    blob = "\n".join(res.ref_instructions)
    # pose/contact/support SOT
    assert "support surface" in blob and "hand contacts" in blob
    # I-3 real-human 단언 + 마네킹 부정
    assert "real, photorealistic human" in blob
    assert "NOT look like a wooden mannequin" in blob
    # insert visible-portion 주입 + 전신강요 금지
    assert "tight insert on the resting hand" in blob
    assert "do NOT force the whole body" in blob
    # 마네킹/관절선 복사금지
    assert "do NOT draw any mannequin" in blob


def test_render_branch_lifeless_only_when_dead():
    """★ Fix2 (Codex BLOCKING2): '(now lifeless)' 단정은 subject_state==dead 일 때만.
    unconscious/severely_injured(살아있음)는 미부착, 단 실인간/마네킹부정 단언은 유지."""
    def _render(state):
        res = resolve_ref_roles(_payload(
            labeled_refs=[("POSE GUIDE", b"x")],
            ref_roles=["immobilized_pose_guide"],
            ref_role_metadata=[{"group_id": "g", "visible_focus": "",
                                "subject_state": state}],
        ))
        return "\n".join(res.ref_instructions)

    dead = _render("dead")
    unconscious = _render("unconscious")
    assert "now lifeless" in dead
    assert "now lifeless" not in unconscious
    # 상태 무관하게 실인간 + 마네킹 부정은 유지
    for blob in (dead, unconscious):
        assert "real, photorealistic human" in blob
        assert "NOT look like a wooden mannequin" in blob


def test_pose_guide_does_not_satisfy_required_character():
    """★ Codex 가드: registered guide ref 는 required_refs 를 충족하지 않는다.
    required character C04 는 별도 character ref 가 충족해야 한다."""
    from app.core.ref_contract_validator import RefContractError, validate_attached_refs

    rpc = {"asset_requirements": {"required_refs": [
        {"kind": "character", "id": "C04", "policy": "required"}]}}
    # pose guide 만 붙음 → required character 미충족 → raise
    with pytest.raises(RefContractError):
        validate_attached_refs(
            rpc,
            [("POSE / SUPPORT GUIDE ...", b"x")],
            [("immobilized_pose_guide", "12:13")],
            prompt="the immobilized body",
            is_close_framing=False,
            reference_phrase_kinds=[],
        )
    # character ref 가 함께 있으면 통과 (pose guide 는 중립)
    validate_attached_refs(
        rpc,
        [("character C04 appearance reference", b"x"), ("POSE / SUPPORT GUIDE ...", b"y")],
        [("character", "C04"), ("immobilized_pose_guide", "12:13")],
        prompt="the immobilized body",
        is_close_framing=False,
        reference_phrase_kinds=[],
    )


def test_attach_flag_off_is_noop(monkeypatch):
    """flag OFF → attach helper no-op (byte-identical, list 불변)."""
    from app.core.steps import visual_continuity_anchor_step as vca
    from app.core.config import settings
    monkeypatch.setattr(settings, "immobilized_registered_pose_guide_enabled", False)

    labeled_refs, ref_roles, ref_role_metadata, attached_meta = [("c", b"x")], ["character_ref"], [{}], [("character", "C04")]
    before = (list(labeled_refs), list(ref_roles), list(ref_role_metadata), list(attached_meta))
    out = vca.attach_registered_pose_guide_ref(
        labeled_refs, ref_roles, ref_role_metadata, attached_meta,
        scene_index=12, shot_index=8,
        anchor_ctx={"members_by_shot": {(12, 8): ["g"]},
                    "anchors_by_group": {"g": {"shared_state_contract": "x"}},
                    "members_by_group": {"g": [{"scene_index": 12, "shot_index": 8,
                                                "role": "environment"}]}},
        background_chain_bg_map={"12_8": {"image_bytes": b"P" * 2048, "bg_id": "L04B05"}},
        cache_dir=__import__("pathlib").Path("/tmp/nonexistent_rpg"),
    )
    assert out is False
    assert (labeled_refs, ref_roles, ref_role_metadata, attached_meta) == before


def test_zoomed_lock_aligns_with_pose_guide_when_present():
    """pose guide 가 같은 payload 에 있으면 zoomed-frame lock 이 'pose guide=SOT,
    image=환경/연속성'으로 정렬(image wins 단정 대신)."""
    res = resolve_ref_roles(_payload(
        labeled_refs=[("prev zoomed", b"a"), ("POSE GUIDE", b"b")],
        ref_roles=["previous_shot_same_frame_zoomed", "immobilized_pose_guide"],
        ref_role_metadata=[{"immobilized_subjects": [{"character": "C04"}]},
                           {"group_id": "g", "visible_focus": ""}],
    ))
    blob = "\n".join(res.ref_instructions)
    assert "from the attached pose guide" in blob
    # 가이드 정렬 분기에서는 "image 1 wins" 단정을 쓰지 않는다
    assert "image 1 wins" not in blob


def test_zoomed_lock_without_pose_guide_uses_image_wins():
    """pose guide 없으면 기존 Inc3-A 동작(image wins) 유지."""
    res = resolve_ref_roles(_payload(
        labeled_refs=[("prev zoomed", b"a")],
        ref_roles=["previous_shot_same_frame_zoomed"],
        ref_role_metadata=[{"immobilized_subjects": [{"character": "C04"}]}],
    ))
    blob = "\n".join(res.ref_instructions)
    assert "image 1 wins" in blob
