"""location_part 의 metadata 를 모델이 dict 로 지어내도 검증 앞에서 계약 모양으로 접힌다.

★실측 (2026-09-02 canary ①): `validate_entity_metadata_shape: location_part entity
must have visual_identity=None, got dict` 가 LP01~LP05 전부에 나고 셋(LP01·LP02·
LP03)은 3회 다 실패해 `entity_t2i` 가 partial 로 끝났다. 결정적이라 다시 열면
27호출을 또 태운다. 그 갈래엔 실을 값이 없다 — 접는 것이 계약이다.
"""
from __future__ import annotations

import ast
import inspect
import textwrap

import pytest

from app.core import entity_metadata as em
from app.core.steps import entity_steps as es


class TestNeutralShapesAreFolded:
    @pytest.mark.parametrize("etype", sorted(em.neutral_shape_types()))
    def test_a_made_up_dict_is_folded_to_the_contract_shape(self, etype):
        got = em.normalize_metadata_for_type(
            etype, {"location": {"x": 1}, "visual_identity": {"face": "…"}})
        assert got == {"location": None, "visual_identity": None}
        em.validate_entity_metadata_shape(etype, got, short_id="X01")   # 안 선다

    def test_location_part_is_a_neutral_shape(self):
        assert "location_part" in em.neutral_shape_types()
        assert "character" in em.neutral_shape_types()

    @pytest.mark.parametrize("etype", ["prop", "location"])
    def test_value_bearing_types_are_left_alone(self, etype):
        """★음성 대조 — prop·location 은 값이 곧 SOT 라 손대지 않는다."""
        md = {"location": {"k": 1}, "visual_identity": {"k": 2}}
        assert em.normalize_metadata_for_type(etype, md) is md

    def test_the_bad_shape_still_fails_validation_without_folding(self):
        """★양성 대조 — 접지 않으면 실측 그대로 선다."""
        with pytest.raises(Exception, match="visual_identity"):
            em.validate_entity_metadata_shape(
                "location_part", {"location": None, "visual_identity": {"a": 1}},
                short_id="LP01")


class TestTheStepFoldsBeforeItValidates:
    def test_normalize_is_called_before_validate_in_entity_t2i(self):
        src = textwrap.dedent(inspect.getsource(es.EntityT2iStep))
        i_norm = src.index("normalize_metadata_for_type(etype, md)")
        i_val = src.index("validate_entity_metadata_shape(\n")
        assert i_norm < i_val, "★검증이 정규화보다 앞에 있다"

    def test_the_fold_reads_no_entity_name(self):
        """★이름·부분문자열로 갈래를 정하지 않는다 — entity_type 만."""
        tree = ast.parse(textwrap.dedent(inspect.getsource(em.normalize_metadata_for_type)))
        names = {n.id for n in ast.walk(tree) if isinstance(n, ast.Name)}
        assert not names & {"name", "short_id", "ename"}, names
