"""lane 스케일 쌍이 **스텝 지문에 접히는가** (B-2b, Codex #39 BLOCK).

## 무엇이 문제였나

PR 첫 판은 「참조 라벨과 프롬프트 절이 `compute_input_fingerprint` 에
해시되니 지문은 따라 움직인다」고 적었다. **그 앞을 못 봤다.**

완료된 스텝은 `StepRunner._evaluate_resume_decision` 이 **config hash 가
같고 파일이 있으면 SKIP** 한다. 그러면 샷별 지문까지 내려가지도 않는다.
그래서 outer hash 에 새 바이트가 없으면 옛 에피소드에서 이 수정이
**한 번도 안 돈다** — 옛 lane 이미지가 그대로 재사용된다.

이 저장소에서 **다섯 번째** 만나는 같은 부류다(`camera_frame_stem` ·
`broll_variation_stem` · `naturalism_pack` · `cine_stage_direction`).

## 재는 자리

「스템 이름을 payload 에 넣었나」가 아니라 **지문 값이 실제로 움직이나**를
본다. 이름만 세면 다른 경로로 그 값이 안 접혀도 통과한다.
"""
from __future__ import annotations

import app.modules.pipeline.still_recipe as sr

FRAMING_SCALE_STEM = "framing_scale_clause"
LANE_SKETCH_STEM = "sketch_label_lane"


def _hash(monkeypatch, *, camera_frame_on=True, lane_on=True,
          doctored=None) -> str:
    from app.core.config import settings
    from app.core.steps.image_steps import SceneImagePipelineStep

    step = SceneImagePipelineStep.__new__(SceneImagePipelineStep)
    step.project_config = {}
    for k, v in (("still_recipe_mode", "v1"),
                 ("still_recipe_camera_frame_enabled", camera_frame_on),
                 ("outdoor_lane_pipe_enabled", lane_on),
                 ("outdoor_lane_plan_enabled", lane_on)):
        monkeypatch.setattr(settings, k, v)
    real = _hash.real

    def _spy(selector, stem):
        return "CHANGED" if doctored and stem == doctored else real(
            selector, stem)

    monkeypatch.setattr(sr, "recipe_stem_content_hash", _spy)
    return step._config_hash_base()


# ★원본을 **한 번만** 잡는다 — 헬퍼 안에서 매번 잡으면 두 번째부터
#  「이미 씌운 대역」을 원본으로 잡아 변조가 묻힌다.
_hash.real = sr.recipe_stem_content_hash


def test_both_lane_stems_move_the_hash(monkeypatch):
    """★★두 스템 **어느 쪽을 고쳐도** 지문이 움직여야 한다.

    절만 바뀌고 라벨이 그대로면 같은 요청에서 권위가 둘이 된다. 그래서
    둘 다 계약이고 둘 다 지문이다.
    """
    base = _hash(monkeypatch)
    for stem in (FRAMING_SCALE_STEM, LANE_SKETCH_STEM):
        got = _hash(monkeypatch, doctored=stem)
        assert got != base, (
            f"{stem} 문안을 고쳤는데 지문이 그대로다 — 완료 스텝이 통째로 "
            "skip 되어 옛 lane 그림이 남는다")


def test_lane_off_keeps_the_old_hash(monkeypatch):
    """lane pipe 가 꺼져 있으면 이 절이 안 나간다 — 스탬프도 없다.

    안 쓰는 스템을 접으면 무관한 재생성이 난다(2026-08-27 에 그 반대편으로
    한 번 넘어갔다가 물렸다).
    """
    off = _hash(monkeypatch, lane_on=False)
    assert off == _hash(monkeypatch, lane_on=False, doctored=FRAMING_SCALE_STEM)
    assert off == _hash(monkeypatch, lane_on=False, doctored=LANE_SKETCH_STEM)


def test_camera_frame_off_keeps_the_old_hash(monkeypatch):
    """camera_frame 이 꺼져 있으면 조립이 이 절을 안 만든다 — 스탬프도 없다."""
    off = _hash(monkeypatch, camera_frame_on=False)
    assert off == _hash(monkeypatch, camera_frame_on=False,
                        doctored=FRAMING_SCALE_STEM)
    assert off == _hash(monkeypatch, camera_frame_on=False,
                        doctored=LANE_SKETCH_STEM)


def test_turning_the_pair_on_changes_the_hash(monkeypatch):
    """양성 확인 — 켜고 끄는 것 자체가 지문을 가른다.

    이게 같으면 위 OFF 시험들은 「아무것도 안 접혀서」 초록일 수 있다.
    """
    assert _hash(monkeypatch, lane_on=True) != _hash(monkeypatch, lane_on=False)
