"""W21B-wave-4: FloorPlanPromptStep scene_primary fallback regression.

근본 버그: shot.location_id 가 비어 있는 프로젝트에서 floor_plan_prompt_step 이
``scene_primary`` (scene_director.primary_location) 를 _select_scenes_for_group 에
넘기지 않아 모든 shot 이 skip → user_prompt 의 Scene segments 가 비어, 모델이 씬
텍스트 0 으로 평면도를 설계했다 (모든 FP 영향). background_master_plan_step 은 같은
fallback 을 이미 넘긴다 — 비대칭 누락.

이 test 는 deterministic data-flow (구조 필드 primary_location exact join) 만 검증:
shot.location_id=None + scene_director.primary_location==group member loc_id 일 때
user_prompt 에 scene text 가 들어오는지, scene_director 가 없으면(=fallback 미작동)
빈 scene 으로 회귀하는지. LLM/image/VLM API 0 (run_floor_plan_prompt mock), DB write 0.
"""
from __future__ import annotations

from unittest.mock import patch

from app.core.steps.floor_plan_prompt_step import FloorPlanPromptStep


_SCENE_PROBE = "PROBE_SCENE_TEXT_거실_방문_커튼"


def _checkpoints(*, with_scene_director: bool):
    """shot.location_id=None 인 1-fp / 1-bg / 1-scene fixture.

    with_scene_director=True 면 scene_director.primary_location='L05' 제공
    (fallback 작동), False 면 scene_director cp 자체를 None (fallback 미작동).
    """
    cps = {
        "background_master_plan": {
            "data": {
                "plans": {
                    "g1": {
                        "status": "ok",
                        "plan": {
                            "floor_plans": [
                                {"fp_id": "fp_x", "depends_on_fp": []}
                            ],
                            "backgrounds": [
                                {
                                    "bg_id": "L05B01",
                                    "loc_id": "L05",
                                    "depends_on_fp": ["fp_x"],
                                    "applies_to_shots": ["S5_Shot1"],
                                }
                            ],
                        },
                    }
                },
                "bg_catalog_hash": "h",
                "shot_binding_hash": "h",
            }
        },
        "scene_save": {
            "data": {
                "segments": [
                    {"scene_index": 5, "heading": "옥탑방 안", "text": _SCENE_PROBE}
                ]
            }
        },
        # shot.location_id=None — 이 프로젝트의 실제 형태. fallback 없으면 매칭 실패.
        "shot_validator": {
            "data": {
                "scenes": [
                    {
                        "scene_index": 5,
                        "shots": [
                            {"shot_index": 1, "location_id": None, "description": "d"}
                        ],
                    }
                ]
            }
        },
        "shot_selection": {
            "data": {"scenes": [{"scene_index": 5, "selected_shot_indices": [1]}]}
        },
        "visual_world_rules": {"data": {}},
        "world_guide": {"data": {}},
    }
    if with_scene_director:
        cps["scene_director"] = {
            "data": {"scenes": [{"scene_index": 5, "primary_location": "L05"}]}
        }
    else:
        cps["scene_director"] = None
    return cps


def _run_capture_user_prompt(*, with_scene_director: bool) -> str:
    cps = _checkpoints(with_scene_director=with_scene_director)
    step = FloorPlanPromptStep.__new__(FloorPlanPromptStep)
    step.project_id = "p"
    step.episode_id = "e"
    step.project_config = {}
    step._load_prev_checkpoint = lambda sid: cps.get(sid)  # type: ignore
    step.build_opik_metadata = lambda: {}  # type: ignore

    captured = {}

    def _fake_run(*, user_prompt, expected_fp_id, **kw):
        captured["user_prompt"] = user_prompt
        return {
            "fp_id": expected_fp_id,
            "t2i_prompt": "x" * 60,
            "key_elements": [],
            "numbered_elements": [],
            "camera_recommendations": [],
        }

    with patch(
        "app.core.steps.floor_plan_prompt_step.run_floor_plan_prompt",
        side_effect=_fake_run,
    ):
        step._execute(mode="resume")
    return captured["user_prompt"]


def test_scene_primary_fallback_injects_scene_text_when_location_id_none():
    """scene_director.primary_location 이 group loc_id 와 맞으면 scene text 가
    user_prompt 에 들어온다 (shot.location_id=None 이어도)."""
    up = _run_capture_user_prompt(with_scene_director=True)
    assert _SCENE_PROBE in up


def test_without_scene_director_scene_segments_empty_regression():
    """scene_director fallback 이 없으면(누락) location_id=None 매칭 실패로
    Scene segments 가 비어 scene text 가 user_prompt 에 들어오지 않는다 —
    fix 이전의 버그 동작을 회귀로 고정."""
    up = _run_capture_user_prompt(with_scene_director=False)
    assert _SCENE_PROBE not in up
