"""P0 (2026-07-01) — 씬 스틸 실제 첨부 image asset UUID 영속화.

resolve_refs_for_prompt 가 attach 시점에 scene_ref_asset_id_map 으로 asset_id +
pipeline_role 를 ref_role_metadata 에 stamp 하는지(라벨 파싱 없이 구조 key SOT),
그리고 map 부재 시 byte-identical(추가 key 없음) 인지 검증. coordinator 의
collect_actual_attached_refs 수집/dedup/unresolved 분리도 검증.
"""
from __future__ import annotations

from app.services.scene_reference_service import SceneReferenceService
from app.services.scene_generation_coordinator import collect_actual_attached_refs
from app.services.prompt_service import make_labeled_ref_payload


def _svc() -> SceneReferenceService:
    return SceneReferenceService.__new__(SceneReferenceService)


def test_base_ref_stamps_asset_id_when_map_provided() -> None:
    """required character base ref → metadata 에 asset_id(map) + pipeline_role."""
    svc = _svc()
    ve = [{"id": "uuid-c07", "short_id": "C07", "entity_type": "character", "name": "Hero"}]
    payload = svc.resolve_refs_for_prompt(
        t2i_prompt="A wide shot. C07 stands by the window.",
        visible_entities=ve,
        scene_ref_image_map={"uuid-c07": b"c07_face"},
        entity_lookup={},
        required_refs=[{"kind": "character", "id": "C07", "policy": "required"}],
        scene_ref_asset_id_map={"uuid-c07": "ASSET-C07"},
    )
    meta = [m for m in payload.ref_role_metadata if m.get("sid") == "C07"]
    assert meta, payload.ref_role_metadata
    assert meta[0]["asset_id"] == "ASSET-C07"
    assert meta[0]["pipeline_role"] == "reference_face"


def test_no_map_is_byte_identical_metadata() -> None:
    """scene_ref_asset_id_map=None(기존 caller) → asset_id/pipeline_role 미추가."""
    svc = _svc()
    ve = [{"id": "uuid-c07", "short_id": "C07", "entity_type": "character", "name": "Hero"}]
    payload = svc.resolve_refs_for_prompt(
        t2i_prompt="A wide shot. C07 stands by the window.",
        visible_entities=ve,
        scene_ref_image_map={"uuid-c07": b"c07_face"},
        entity_lookup={},
        required_refs=[{"kind": "character", "id": "C07", "policy": "required"}],
    )
    meta = [m for m in payload.ref_role_metadata if m.get("sid") == "C07"]
    assert meta
    assert "asset_id" not in meta[0]
    assert "pipeline_role" not in meta[0]


def test_composite_stamps_from_composite_key() -> None:
    """C##O## composite attach → asset_id 는 composite key 로 회수."""
    svc = _svc()
    ve = [
        {"id": "uuid-c01", "short_id": "C01", "entity_type": "character", "name": "A"},
        {"id": "uuid-o02", "short_id": "O02", "entity_type": "outlook", "name": "outfit"},
    ]
    ckey = "composite:uuid-c01:uuid-o02"
    payload = svc.resolve_refs_for_prompt(
        t2i_prompt="C01O02 walks in.",
        visible_entities=ve,
        scene_ref_image_map={ckey: b"composite_bytes"},
        entity_lookup={},
        scene_ref_asset_id_map={ckey: "ASSET-COMP"},
    )
    meta = [m for m in payload.ref_role_metadata if m.get("outfit_kind") == "composite"]
    assert meta, payload.ref_role_metadata
    assert meta[0]["asset_id"] == "ASSET-COMP"
    assert meta[0]["pipeline_role"] == "reference_composite"


def test_collect_actual_attached_refs_dedup_and_unresolved() -> None:
    """asset_id(UUID) 있는 것만 image_ids(dedup). asset_id 없는 prev_shot 은 unresolved."""
    payload = make_labeled_ref_payload(
        labeled_refs=[("bg", b""), ("face", b""), ("face-dup", b""), ("prev", b"")],
        ref_roles=["background_chain_ref", "character_ref", "character_ref", "previous_shot_continuity"],
        ref_role_metadata=[
            {"bg_id": "L04B06", "asset_id": "uuid-bg", "pipeline_role": "background_render"},
            {"sid": "C01", "asset_id": "FACE", "pipeline_role": "reference_face"},
            {"sid": "C01", "asset_id": "FACE", "pipeline_role": "reference_face"},
            {"loc": "L1"},
        ],
        attached_meta=[("background", "L04B06"), ("character", "C01"), ("character", "C01"), ("background_prev_shot", "L1")],
    )
    c = collect_actual_attached_refs(payload)
    assert c["image_ids"] == ["uuid-bg", "FACE"]  # dedup, bg는 resolve된 UUID
    assert len(c["unresolved"]) == 1
    assert c["unresolved"][0]["role"] == "previous_shot_continuity"


def test_collect_preserves_structural_fields_in_unresolved() -> None:
    """★ P0: registered guide unresolved 에 구조필드(pipeline_role/group_id/bg_key/
    guide_hash/subject_state) 보존 → persistence 가 라벨없이 post-hoc resolve."""
    payload = make_labeled_ref_payload(
        labeled_refs=[("POSE GUIDE", b"")],
        ref_roles=["immobilized_pose_guide"],
        ref_role_metadata=[{
            "group_id": "g1", "bg_key": "L04B06", "guide_hash": "h9",
            "pipeline_role": "registered_pose_guide", "subject_state": "dead",
            "visible_focus": "torso",
        }],
        attached_meta=[("immobilized_pose_guide", "12:7")],
    )
    c = collect_actual_attached_refs(payload)
    assert c["image_ids"] == []
    u = c["unresolved"][0]
    assert u["pipeline_role"] == "registered_pose_guide"
    assert u["group_id"] == "g1"
    assert u["bg_key"] == "L04B06"
    assert u["guide_hash"] == "h9"
    assert u["subject_state"] == "dead"


def test_collect_bg_id_only_is_unresolved_not_uuid() -> None:
    """★ Codex BLOCKING: bg meta 가 bg_id(L04B06)만 있고 asset_id 미해결이면
    image_ids 에 들어가지 않고 unresolved 로 간다(구조키를 UUID 로 오인 금지)."""
    payload = make_labeled_ref_payload(
        labeled_refs=[("bg-unresolved", b"")],
        ref_roles=["background_chain_ref"],
        ref_role_metadata=[{"bg_id": "L04B06", "pipeline_role": "background_render"}],
        attached_meta=[("background", "L04B06")],
    )
    c = collect_actual_attached_refs(payload)
    assert c["image_ids"] == []
    assert len(c["unresolved"]) == 1
    assert c["unresolved"][0]["role"] == "background_render"
