"""소품 합치기 규칙 — **그림이 다르면 합치지 않는다** (2026-09-20 사용자 지시).

## 무엇이 났나

사용자가 손으로 고쳐야 했던 총 정본(P107)이 **「권총이면서 장총」**인
묶음 문구였다. 그 뿌리는 추출 팩의 **예시 한 줄**이다:

    - 같은 종류의 일반 물체는 하나로 (예: **총1, 총2 → 총**)

하필 그 예시가 「그림이 전혀 다른 둘을 합쳐라」고 시킨다. 모델은 시킨
대로 했고, 하류는 한 문장 안에서 권총과 장총을 동시에 그리려 했다.

★같은 판단을 **인물 팩은 이미 반대로** 적고 있었다 —
 「핵심: 같은 인물이라도 **이미지로 그렸을 때 다른 사람처럼 보이면**
 무조건 분리」(`character.md`). 소품에만 그 원리가 없었다.

★그리고 다른 추출 경로(`entity_extract_v4`)는 이미 고쳐져 있었는데
 **지금 샷 목록이 읽는 팩은 `entity_all`** 이라 안 바뀌었다(Codex 감사).
 그래서 이 시험은 **실제로 읽히는 문안**을 본다.
"""
from __future__ import annotations

import pytest

from app.modules.prompt_loader import load_prompt

_MODULE = "entity_all"


def _live(stem: str) -> str:
    """버전을 **안 주고** 읽는다 — 프로덕션(`entity_lister`)과 같은 방식."""
    return load_prompt(_MODULE, stem)


def test_the_merge_example_that_caused_it_is_gone():
    """그 예시가 되살아나면 빨간불 — 결함을 **이름으로** 적는다."""
    body = _live("prop")
    assert "총1, 총2" not in body, (
        "그림이 다른 둘을 합치라는 예시가 현행 팩에 되살아났다")
    assert "같은 종류의 일반 물체는 하나로" not in body, (
        "종류만 같으면 합치라는 줄이 되살아났다")


def test_the_drawing_test_is_the_merge_criterion():
    """합칠지 말지는 **그림으로** 가른다."""
    body = _live("prop")
    assert "이미지로 그렸을 때" in body, (
        "그림 기준이 없다 — 이름·종류만으로 합치게 된다")
    # 금지형만 남기지 않는다 — **무엇을 보고 가르는지** 준다
    for axis in ("크기", "실루엣"):
        assert axis in body, f"가르는 축({axis})이 없다"


def test_pose_alone_does_not_mint_a_new_prop():
    """★가르는 것은 **물건의 생김새**이지 그때그때의 자세가 아니다.

    「쥐는 법」을 축으로 준 뒤 생길 수 있는 오독 — 같은 권총을 한 손/두
    손으로 드는 장면이 나왔다고 소품을 둘로 발급하면 안 된다(Codex).
    """
    body = _live("prop")
    assert "자세 차이만으로는 분리하지 않는다" in body, (
        "자세만으로 분리하지 말라는 대조가 없다")
    assert "생김새" in body, "무엇이 가르는 기준인지 안 적었다"


def test_the_merge_line_is_scoped_by_visual_form():
    """「같은 기능의 변형은 하나로」가 **시각 형태 안에서**임을 명시한다."""
    body = _live("prop")
    i = body.find("같은 기능의 변형은 하나로")
    assert i > 0, "전제 확인"
    assert "시각 형태가 같은 범위에서" in body[max(0, i - 40):i + 30], (
        "기능 통합이 시각 구분보다 앞서는 것으로 읽힌다")


def test_props_and_characters_share_one_principle():
    """소품과 인물이 **같은 원리**를 쓴다 — 한쪽만 고쳐지면 또 갈린다."""
    prop = _live("prop")
    char = _live("character")
    assert "이미지로 그렸을 때" in char, "전제 확인 — 인물 팩의 원리"
    assert "이미지로 그렸을 때" in prop, "소품 팩에 같은 원리가 없다"


def test_the_visual_form_rule_survives():
    """합치더라도 **단일 시각 형태**를 지킨다 — 다른 경로에 이미 있던 조항."""
    assert "단일 시각 형태" in _live("prop")


def test_the_exclusion_rule_is_untouched():
    """「구분 못 할 정도면 제외」는 **그대로** — 같은 축의 반대편이지 모순이 아니다."""
    assert "구분하기 어렵거나 못 느낄 정도면 제외" in _live("prop")


@pytest.mark.parametrize("stem", [
    "system", "character", "location", "prop",
])
def test_the_live_pack_still_has_every_stem(stem):
    """현행 팩이 스템을 다 갖췄는지 — 한 파일만 올리면 나머지가 옛 판에서 온다."""
    assert _live(stem).strip(), f"{stem} 이 비었다"
