"""Task B6 — static/audit allowlist 가드 (Codex 추가).

Phase B 이후 gpt-image-2 직접 호출은 전부 call_gpt_image_bytes wrapper 를 경유해야
한다(생성물 capture 누락 방지·미래 재도입 차단). AST 로 5개 모듈에 직접
``X.images.generate(...)`` / ``X.images.edit(...)`` 호출이 0 임을 잠근다(docstring/주석은
Call 노드가 아니라 자연 제외). + 제외 사이트(png_metadata in-place 재저장)에 capture
호출이 없어 이중캡처가 0 임을 잠근다.
"""

import ast
from pathlib import Path

_BACKEND = Path(__file__).resolve().parents[3]

# gpt-image-2 직접 호출이 wrapper 로 치환된 5개 모듈.
# ★ 신규 gpt-image 호출 모듈을 추가하면 반드시 call_gpt_image_bytes 로 호출하고 이
#   allowlist 에 등록할 것(이 가드는 아래 5개 모듈만 잠근다 — Codex MINOR).
_WRAPPED_MODULES = [
    "app/modules/pipeline/background_render.py",
    "app/modules/pipeline/background_chain_render.py",
    "app/modules/pipeline/floor_plan_render.py",
    "app/modules/pipeline/location_floor_plan.py",
    "app/modules/pipeline/space_set_bg_provider.py",
]


def _direct_image_call_lines(path: Path):
    """AST: ``<expr>.images.generate(...)`` / ``.images.edit(...)`` 호출 라인."""
    tree = ast.parse(path.read_text(encoding="utf-8"))
    hits = []
    for node in ast.walk(tree):
        if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute):
            method = node.func  # .generate / .edit
            if method.attr in ("generate", "edit") and isinstance(
                method.value, ast.Attribute
            ) and method.value.attr == "images":
                hits.append(node.lineno)
    return hits


def test_no_direct_image_calls_outside_wrapper():
    offenders = {}
    for m in _WRAPPED_MODULES:
        hits = _direct_image_call_lines(_BACKEND / m)
        if hits:
            offenders[m] = hits
    assert offenders == {}, (
        f"wrapper 밖 직접 images.generate/edit 호출 잔존: {offenders} — "
        "call_gpt_image_bytes 로 치환할 것"
    )


def test_wrapper_module_is_the_single_call_site():
    """wrapper 모듈에는 정확히 generate 1 + edit 1 직접 호출이 있어야 한다."""
    hits = _direct_image_call_lines(_BACKEND / "app/modules/llm/gpt_image_primitive.py")
    assert len(hits) == 2, f"wrapper 의 직접 호출 수 예상=2, 실제={hits}"


def test_png_metadata_inplace_resave_has_no_capture():
    """png_metadata 는 이미 캡처된 이미지를 in-place 재저장 → capture 0(이중캡처 금지)."""
    src = (_BACKEND / "app/modules/png_metadata.py").read_text(encoding="utf-8")
    assert "capture_generated_image" not in src
    assert "capture_artifact" not in src
