"""Tests for the shared max_tokens helper.

Documents the 80%-of-context-window cap, the omit-the-kwarg behavior for
unset ``llm.max_tokens`` (matches the pre-refactor live-class path), and
the ``llm.supports_max_tokens=False`` opt-out.
"""

from unittest.mock import patch

from local_deep_research.llm.providers._helpers import compute_max_tokens


SETTINGS = (
    "local_deep_research.config.thread_settings.get_setting_from_snapshot"
)


def _set(values):
    """Build a side_effect that returns mapped values for known keys."""

    def side_effect(key, default=None, *args, **kwargs):
        return values.get(key, default)

    return side_effect


class TestComputeMaxTokens:
    def test_returns_none_when_supports_max_tokens_false(self):
        with patch(
            SETTINGS, side_effect=_set({"llm.supports_max_tokens": False})
        ):
            assert compute_max_tokens() is None

    def test_returns_none_when_setting_unset(self):
        # Absent llm.max_tokens → None → caller omits the kwarg and the
        # provider SDK default applies. This matches the pre-refactor
        # live-class behavior; the dead chain's 100000 default exceeded
        # the output limit of most cloud models.
        with patch(
            SETTINGS,
            side_effect=_set({"llm.supports_max_tokens": True}),
        ):
            assert compute_max_tokens() is None

    def test_caps_at_80_percent_of_context_window(self):
        # 1000 * 0.8 == 800; min(2000, 800) == 800
        with patch(
            SETTINGS,
            side_effect=_set(
                {
                    "llm.supports_max_tokens": True,
                    "llm.max_tokens": 2000,
                }
            ),
        ):
            assert compute_max_tokens(context_window_size=1000) == 800

    def test_uses_setting_when_below_80_percent_cap(self):
        with patch(
            SETTINGS,
            side_effect=_set(
                {
                    "llm.supports_max_tokens": True,
                    "llm.max_tokens": 500,
                }
            ),
        ):
            # 500 < 1000*0.8=800, so the setting wins
            assert compute_max_tokens(context_window_size=1000) == 500

    def test_returns_none_when_max_tokens_explicitly_none(self):
        # Distinct from "setting unset" — explicit None means caller should
        # omit the kwarg rather than pass the legacy default.
        with patch(
            SETTINGS,
            side_effect=_set(
                {
                    "llm.supports_max_tokens": True,
                    "llm.max_tokens": None,
                }
            ),
        ):
            assert compute_max_tokens(context_window_size=1000) is None
