"""W21B-wave-4 C2: shot_projection_card real-VLM provider preflight tests.

These exercise the deterministic fail-closed preflight ONLY — no real
litellm/network call. They pin Required 3 (the provider honors the
pack_version threaded from the step via prompt_context, not just its own
default) and the fail-closed arg/fs discipline.
"""
from __future__ import annotations

import pytest

from app.modules.pipeline.shot_projection_card import ProjectionCardError
from app.modules.pipeline.shot_projection_card_provider import (
    litellm_projection_card_provider,
)

_INV = [{"number": 3, "marker_layer": "base", "expected_label": "싱크대"}]


def test_provider_uses_pack_version_from_prompt_context(tmp_path):
    # A pack_version threaded via prompt_context that does not exist on
    # disk must fail closed CITING THAT version — proving the provider used
    # the step-resolved pack, not its own default (Required 3).
    png = tmp_path / "fp.png"
    png.write_bytes(b"x")
    with pytest.raises(ProjectionCardError) as ei:
        litellm_projection_card_provider(
            inventory=_INV, fp_id="fp", bg_id="b", shot_id="s",
            fp_image_path=str(png),
            prompt_context={"pack_version": "9.nonexistent_pack"},
        )
    assert "9.nonexistent_pack" in str(ei.value)


def test_provider_fails_closed_on_missing_image():
    with pytest.raises(ProjectionCardError):
        litellm_projection_card_provider(
            inventory=_INV, fp_id="fp", bg_id="b", shot_id="s",
            fp_image_path=None,
        )


def test_provider_fails_closed_on_empty_inventory(tmp_path):
    png = tmp_path / "fp.png"
    png.write_bytes(b"x")
    with pytest.raises(ProjectionCardError):
        litellm_projection_card_provider(
            inventory=[], fp_id="fp", bg_id="b", shot_id="s",
            fp_image_path=str(png),
        )


def _no_gemini_key(monkeypatch) -> None:
    """gemini 키 풀을 **비운다** — 빈 풀의 실제 동작(RuntimeError)을 흉내낸다.

    ★#92 (2026-08-27): 이 자리의 모델이 Sol → gemini 로 바뀌었다.
     막으려는 실패는 그대로다 — 「키가 없으면 부르지 않는다」.
     **어느 키냐만** 옮겼다.
    """
    from app.modules.llm import gemini_key_pool

    def _raise():
        raise RuntimeError("No Gemini API keys configured")

    monkeypatch.setattr(gemini_key_pool, "get_next_key", _raise)


def test_real_pack_loads_then_fails_at_env_or_fs(tmp_path, monkeypatch):
    # With the real default pack and no OPENAI_API_KEY, the provider must
    # fail closed at the env preflight (pack load succeeded) — never reach
    # a network call. Proves the default pack resolves on disk.
    # [2026-08-01] 키 유무의 권위가 환경변수에서 슬롯 브로커로 옮겼다 —
    # 보조 슬롯만 있어도 '있음'이므로 env 만 지우면 fail-closed 가 아니다.
    monkeypatch.delenv("OPENAI_API_KEY", raising=False)
    monkeypatch.setattr(
        "app.core.config.settings.openai_api_key", "", raising=False)
    monkeypatch.setattr(
        "app.core.config.settings.openai_api_key_secondary", "",
        raising=False)
    _no_gemini_key(monkeypatch)
    png = tmp_path / "fp.png"
    png.write_bytes(b"x")
    with pytest.raises(ProjectionCardError) as ei:
        litellm_projection_card_provider(
            inventory=_INV, fp_id="fp", bg_id="b", shot_id="s",
            fp_image_path=str(png),
        )
    assert "GEMINI_API_KEY" in str(ei.value)
