"""VLM 은 gemini 3.1 pro + grok 만 (2026-08-27, #92).

사용자 지시를 네 번 받았다 — 「gpt sol vlm 은 성능이 안좋아 … 무조건
gemini 3.1 pro 와 grok 최신 모델 둘」. 매번 조금씩 남겨서 시험으로 세운다.

기준 = **이미지 bytes 가 실리는 모듈**. 텍스트 전용은 대상이 아니다.
"""
from __future__ import annotations

import ast
import pathlib

APP = pathlib.Path(__file__).resolve().parents[2] / "app"

FORBIDDEN = ("gpt-5.6-sol", "gpt-5.5", "gpt-4")
IMAGE_MARKS = ("image_url", "png_part", "data:image", "b64encode")


def _vlm_model_constants():
    """이미지를 싣는 모듈의 `*MODEL*` 상수 — `AnnAssign` 도 본다."""
    for py in sorted(APP.rglob("*.py")):
        try:
            src = py.read_text(encoding="utf-8")
            tree = ast.parse(src)
        except Exception:
            continue
        if not any(m in src for m in IMAGE_MARKS):
            continue
        for n in tree.body:
            if isinstance(n, ast.AnnAssign):
                targets, val = [n.target], n.value
            elif isinstance(n, ast.Assign):
                targets, val = n.targets, n.value
            else:
                continue
            if not (isinstance(val, ast.Constant)
                    and isinstance(val.value, str)):
                continue
            for t in targets:
                if isinstance(t, ast.Name) and "MODEL" in t.id:
                    yield str(py.relative_to(APP.parent)), t.id, val.value


def test_no_sol_in_a_vlm_model_constant():
    """★이미지를 싣는 모듈의 모델 상수에 Sol 이 없다.

    ★TEXT_MODEL 은 뺀다 — 지시는 VLM 에 대한 것이고 분석 주력 alias 는
     별개다(`CLAUDE.md` 모델 분업).
    """
    bad = [f"{f}  {n} = {v}" for f, n, v in _vlm_model_constants()
           if "TEXT_MODEL" not in n
           and any(b in v.lower() for b in FORBIDDEN)]
    assert not bad, "VLM 자리에 Sol 이 남았다:\n  " + "\n  ".join(bad)


def test_the_settings_vlm_slots_are_not_sol():
    """설정도 본다 — 코드 상수만 보면 `.env` 가 덮는 것을 놓친다."""
    from app.core.config import settings

    slots = ("dwelling_zone_map_vision_model",
             "indoor_shared_pose_guide_qc_model",
             "space_set_bg_vision_model",
             "indoor_shared_pose_guide_judge_model")
    bad = [f"{k} = {getattr(settings, k)}" for k in slots
           if any(b in str(getattr(settings, k, "")).lower()
                  for b in FORBIDDEN)]
    assert not bad, "설정의 VLM 칸에 Sol 이 남았다:\n  " + "\n  ".join(bad)


def test_the_floor_readback_single_path_is_gemini():
    """★두 모델을 안 켠 판에서도 Sol 로 읽으면 안 된다."""
    import inspect
    from app.core.steps import floor_plan_geometry_readback_step as st

    tree = ast.parse(inspect.getsource(st.FloorPlanGeometryReadbackStep))
    names = {n.id for n in ast.walk(tree) if isinstance(n, ast.Name)}
    assert "gemini_vlm_provider" in names
    assert "litellm_vlm_provider" not in names


def test_a_text_only_constant_may_stay():
    """반대편 — 텍스트 전용은 안 막는다."""
    found = [n for _, n, _ in _vlm_model_constants() if "TEXT_MODEL" in n]
    assert found, "TEXT_MODEL 상수가 없다 — 가르는 기준을 다시 볼 것"
