"""providers.py 注册表测试(M1 设计 §7;register_provider 为纯函数,无可变全局)。

2026-09-04 起 profile 存的是 `ThinkingWire`(off / on_base / effort_key)而非两个
固定片段——档位型模型(GLM-5.3、kimi-k3、deepseek-v4…)的"开"档需要附一个档位值,
两个固定片段表达不了。
"""

import pytest

from polygateway.providers import (
    DEFAULT_PROFILES,
    ProviderProfile,
    ThinkingWire,
    get_provider,
    register_provider,
)

_EXPECTED_SEGMENTS = frozenset(
    {"qwen", "deepseek", "zhipu", "moonshot", "minimax", "openai", "anthropic", "google"}
)


class TestDefaultProfiles:
    def test_all_eight_profiles_registered(self):
        """issue #20: 智谱缺段,下游只能把 GLM 挂在 openai 兜底段下再手写 extra_body。"""
        assert set(DEFAULT_PROFILES) == _EXPECTED_SEGMENTS

    def test_qwen_is_a_switch_with_no_tiers(self):
        w = get_provider("qwen").thinking
        assert w.on_base == {"enable_thinking": True}
        assert w.off == {"enable_thinking": False}
        assert w.effort_key is None  # 百炼靠 thinking_budget 调深度,不是档位
        assert get_provider("qwen").strip_think_tags is True

    def test_deepseek_carries_both_switch_and_tier(self):
        w = get_provider("deepseek").thinking
        assert w.on_base == {"thinking": {"type": "enabled"}}
        assert w.off == {"thinking": {"type": "disabled"}}
        assert w.effort_key == "reasoning_effort"

    def test_zhipu_matches_the_vendor_migration_note(self):
        """智谱官方: thinking.type=enabled + reasoning_effort 才是 GLM-5.3 的正确形态。"""
        w = get_provider("zhipu").thinking
        assert w.on_base == {"thinking": {"type": "enabled"}}
        assert w.off == {"thinking": {"type": "disabled"}}
        assert w.effort_key == "reasoning_effort"

    def test_openai_family_sends_the_standard_field_only(self):
        """gpt/claude/gemini 经网关都吃 OpenAI 标准的 reasoning_effort,不下发厂商方言。"""
        for name in ("openai", "anthropic", "google", "minimax"):
            w = get_provider(name).thinking
            assert w.on_base == {}, name
            assert w.off == {"reasoning_effort": "none"}, name
            assert w.effort_key == "reasoning_effort", name

    def test_unknown_provider_fails_loudly(self):
        """消灭子串猜测: 未注册 provider 装配期即报错,不做模糊匹配。"""
        with pytest.raises(ValueError, match="glm"):
            get_provider("glm")  # 段名是 zhipu,不是 glm
        with pytest.raises(ValueError):
            get_provider("qwen2")  # 子串相似也不放行

    def test_error_lists_every_registered_segment(self):
        with pytest.raises(ValueError, match="zhipu"):
            get_provider("nope")


class TestWireNoneSemantics:
    """三个 `None` 语义互不重叠(issue #5 的成果,不可退回成"注入了个寂寞")。"""

    def test_on_base_none_means_shape_unknown(self):
        w = ThinkingWire(off=None, on_base=None, effort_key=None)
        assert w.on_base is None

    def test_off_none_means_no_off_shape(self):
        """有开启形态但没有关闭形态,与"整个形态未知"是两回事。"""
        w = ThinkingWire(off=None, on_base={"x": 1}, effort_key=None)
        assert w.on_base is not None and w.off is None

    def test_effort_key_none_means_switch_only(self):
        """qwen 是这一档: 能开能关,但没有档位可谈。"""
        assert get_provider("qwen").thinking.effort_key is None

    def test_empty_on_base_is_not_none(self):
        """`{}` = 已知无需注入任何参数即处于该档;`None` = 不知道怎么表达。"""
        w = get_provider("minimax").thinking
        assert w.on_base == {} and w.on_base is not None


class TestPureFunctionRegistration:
    def test_register_returns_new_mapping(self):
        glm = ProviderProfile(
            name="glm",
            thinking=ThinkingWire(off={}, on_base={}, effort_key=None),
            strip_think_tags=False,
        )
        table = register_provider(glm)
        assert get_provider("glm", registry=table) is glm
        # 默认表未被污染(无可变全局状态铁律)
        with pytest.raises(ValueError):
            get_provider("glm")

    def test_register_on_custom_base_and_override(self):
        custom = ProviderProfile(
            name="qwen",
            thinking=ThinkingWire(off={}, on_base={"x": 1}, effort_key=None),
            strip_think_tags=False,
        )
        table = register_provider(custom, base=DEFAULT_PROFILES)
        assert get_provider("qwen", registry=table).thinking.on_base == {"x": 1}
        assert get_provider("qwen").thinking.on_base == {"enable_thinking": True}

    def test_default_profiles_mapping_is_read_only(self):
        with pytest.raises(TypeError):
            DEFAULT_PROFILES["hack"] = None  # type: ignore[index]
