"""GPT-5.6 이관(2026-07-10) — alias 경계 분류 회귀 잠금.

Codex 리뷰 BLOCKING_1 대응: gpt-mini 가 Gemini 3.5 Flash → GPT-5.6 Luna 로
이관되고 gpt-terra/gpt-luna alias 가 신설되면서, 모델 분류 집합
(_GEMINI_ALIASES / _OPENAI_ALIASES / _NO_TEMPERATURE_ALIASES)이 alias 이동을
따라와야 한다. 미갱신 시:

- gpt-mini 에 Gemini safety_settings 가 주입돼 OpenAI 호출이 오염
- gpt-mini/gpt-terra/gpt-luna 에 OpenAI strict-schema sanitizer 미적용
  → uniqueItems 잔존으로 FINDING 10 BadRequest 재발
- gpt-terra/gpt-luna 의 temperature 명시 sanitizer 미동작
  (deployment drop_params 만으로는 일관되지 않는 버전 존재 — 집합이 계약)
"""
import pytest

# 2026-07-11 Gemini 원복: gpt-mini 는 Gemini flash 매핑 복귀 — OpenAI 목록 제외
GPT56_ALIASES = ["gpt", "gpt-terra", "gpt-luna"]
GEMINI_ALIASES = ["gemini-pro", "gemini-flash", "gemini-lite", "gpt-mini"]


def _schema_with_unique_items():
    return {
        "type": "object",
        "properties": {
            "tags": {
                "type": "array",
                "items": {"type": "string"},
                "uniqueItems": True,
            },
        },
    }


def _response_format_kwargs(schema):
    return {
        "response_format": {
            "type": "json_schema",
            "json_schema": {"name": "x", "schema": schema, "strict": True},
        },
    }


class TestAliasSets:
    def test_gpt_mini_back_in_gemini_aliases(self):
        # 2026-07-11 Gemini 원복 — safety 주입 대상 복귀
        from app.modules.llm.llm_client import _GEMINI_ALIASES
        assert "gpt-mini" in _GEMINI_ALIASES

    def test_gpt56_aliases_in_openai_set(self):
        from app.modules.llm.llm_client import _OPENAI_ALIASES
        for alias in GPT56_ALIASES:
            assert alias in _OPENAI_ALIASES, alias

    def test_gpt56_aliases_in_no_temperature_set(self):
        from app.modules.llm.llm_client import _NO_TEMPERATURE_ALIASES
        for alias in GPT56_ALIASES:
            assert alias in _NO_TEMPERATURE_ALIASES, alias


class TestSafetyInjectionBoundary:
    @pytest.mark.parametrize("alias", GPT56_ALIASES)
    def test_openai_alias_gets_no_gemini_safety(self, alias):
        from app.modules.llm.llm_client import _apply_gemini_safety
        kwargs = {}
        _apply_gemini_safety(alias, kwargs)
        assert "safety_settings" not in kwargs, alias

    @pytest.mark.parametrize("alias", GEMINI_ALIASES)
    def test_gemini_alias_still_gets_safety(self, alias):
        from app.modules.llm.llm_client import _apply_gemini_safety
        kwargs = {}
        _apply_gemini_safety(alias, kwargs)
        assert kwargs.get("safety_settings"), alias


class TestTemperatureSanitizer:
    @pytest.mark.parametrize("alias", GPT56_ALIASES)
    def test_temperature_stripped(self, alias):
        from app.modules.llm.llm_client import _sanitize_kwargs_for_model
        kwargs = {"temperature": 0.2}
        _sanitize_kwargs_for_model(alias, kwargs)
        assert "temperature" not in kwargs, alias


class TestSchemaKeySanitizer:
    @pytest.mark.parametrize("alias", GPT56_ALIASES)
    def test_unique_items_stripped_for_openai_alias(self, alias):
        from app.modules.llm.llm_client import (
            _sanitize_response_format_for_model,
        )
        kwargs = _response_format_kwargs(_schema_with_unique_items())
        _sanitize_response_format_for_model(alias, kwargs)
        sent = kwargs["response_format"]["json_schema"]["schema"]
        assert "uniqueItems" not in sent["properties"]["tags"], alias

    @pytest.mark.parametrize("alias", GEMINI_ALIASES)
    def test_unique_items_kept_for_gemini_alias(self, alias):
        from app.modules.llm.llm_client import (
            _sanitize_response_format_for_model,
        )
        kwargs = _response_format_kwargs(_schema_with_unique_items())
        _sanitize_response_format_for_model(alias, kwargs)
        sent = kwargs["response_format"]["json_schema"]["schema"]
        assert sent["properties"]["tags"].get("uniqueItems") is True, alias


def _strict_kwargs(schema):
    return {
        "response_format": {
            "type": "json_schema",
            "json_schema": {"name": "t", "schema": schema, "strict": True},
        },
    }


class TestStrictDowngrade:
    """OpenAI strict 비호환 스키마(Gemini 팩 유래) → strict=False 강등.

    2회차 E2E shot_extract 30/30 실측: optional 필드('characters' 미required)
    스키마가 strict=True 로 가면 BadRequestError. 강등 후에도 스키마 준수는
    _validate_local_schema + retry 가 전 tier 에서 보증.
    """

    def _incompatible(self):
        # Gemini 팩 관용: optional 필드 + additionalProperties 미명시
        return {
            "type": "object",
            "properties": {
                "shots": {"type": "array", "items": {
                    "type": "object",
                    "properties": {
                        "description": {"type": "string"},
                        "characters": {"type": "array",
                                       "items": {"type": "string"}},
                    },
                    "required": ["description"],  # characters 누락
                }},
            },
            "required": ["shots"],
        }

    def _compatible(self):
        return {
            "type": "object",
            "properties": {"a": {"type": "string"}},
            "required": ["a"],
            "additionalProperties": False,
        }

    def test_incompatible_schema_downgrades_strict(self):
        from app.modules.llm.llm_client import (
            _sanitize_response_format_for_model,
        )
        kwargs = _strict_kwargs(self._incompatible())
        _sanitize_response_format_for_model("gpt", kwargs)
        assert kwargs["response_format"]["json_schema"]["strict"] is False

    def test_compatible_schema_keeps_strict_true(self):
        from app.modules.llm.llm_client import (
            _sanitize_response_format_for_model,
        )
        kwargs = _strict_kwargs(self._compatible())
        _sanitize_response_format_for_model("gpt", kwargs)
        assert kwargs["response_format"]["json_schema"]["strict"] is True

    def test_gemini_alias_untouched(self):
        from app.modules.llm.llm_client import (
            _sanitize_response_format_for_model,
        )
        kwargs = _strict_kwargs(self._incompatible())
        _sanitize_response_format_for_model("gemini-pro", kwargs)
        assert kwargs["response_format"]["json_schema"]["strict"] is True

    def test_schema_content_not_mutated_by_downgrade(self):
        from app.modules.llm.llm_client import (
            _sanitize_response_format_for_model,
        )
        original = self._incompatible()
        kwargs = _strict_kwargs(original)
        _sanitize_response_format_for_model("gpt", kwargs)
        sent = kwargs["response_format"]["json_schema"]["schema"]
        # required 목록 자체는 원본 그대로 (강등만, 스키마 변조 없음)
        assert sent["properties"]["shots"]["items"]["required"] == [
            "description"]
        assert original["required"] == ["shots"]

    def test_nested_incompatibility_detected(self):
        from app.modules.llm.llm_client import _is_openai_strict_compatible
        assert _is_openai_strict_compatible(self._compatible()) is True
        assert _is_openai_strict_compatible(self._incompatible()) is False
