"""Pure-function tests for hardware warning checks — zero mocking needed."""

from unittest.mock import patch

from local_deep_research.web.warning_checks.hardware import (
    LOCAL_PROVIDERS,
    check_high_context,
    check_legacy_server_config,
    check_model_mismatch,
)


class TestLocalProviders:
    """Verify the LOCAL_PROVIDERS constant."""

    def test_contains_expected_providers(self):
        assert LOCAL_PROVIDERS == frozenset({"ollama", "llamacpp", "lmstudio"})

    def test_is_frozenset(self):
        assert isinstance(LOCAL_PROVIDERS, frozenset)


class TestCheckHighContext:
    """Tests for check_high_context."""

    def test_warns_local_provider_above_8192(self):
        result = check_high_context("ollama", 16384, dismissed=False)
        assert result is not None
        assert result["type"] == "high_context"
        assert "16,384" in result["message"]

    def test_no_warning_at_8192(self):
        assert check_high_context("ollama", 8192, dismissed=False) is None

    def test_no_warning_below_8192(self):
        assert check_high_context("ollama", 4096, dismissed=False) is None

    def test_no_warning_non_local_provider(self):
        assert check_high_context("openai", 32000, dismissed=False) is None

    def test_no_warning_when_dismissed(self):
        assert check_high_context("ollama", 16384, dismissed=True) is None

    def test_all_local_providers_trigger(self):
        for provider in LOCAL_PROVIDERS:
            result = check_high_context(provider, 16384, dismissed=False)
            assert result is not None, f"{provider} should trigger warning"

    def test_warns_at_boundary_8193(self):
        """8193 is just above the 8192 threshold — should trigger."""
        result = check_high_context("ollama", 8193, dismissed=False)
        assert result is not None
        assert result["type"] == "high_context"

    def test_context_size_in_message(self):
        result = check_high_context("ollama", 16384, dismissed=False)
        assert "16,384" in result["message"]

    def test_warning_dict_has_all_required_keys(self):
        result = check_high_context("ollama", 16384, dismissed=False)
        assert set(result.keys()) == {
            "type",
            "icon",
            "title",
            "message",
            "dismissKey",
            "actionUrl",
            "actionLabel",
        }

    def test_dismiss_key(self):
        result = check_high_context("ollama", 16384, dismissed=False)
        assert result["dismissKey"] == "app.warnings.dismiss_high_context"


class TestCheckModelMismatch:
    """Tests for check_model_mismatch."""

    def test_warns_70b_model_high_context(self):
        result = check_model_mismatch(
            "ollama", "llama3.1:70b", 16384, dismissed=False
        )
        assert result is not None
        assert result["type"] == "model_mismatch"

    def test_case_insensitive_70b(self):
        result = check_model_mismatch(
            "ollama", "Mixtral-70B-v2", 16384, dismissed=False
        )
        assert result is not None

    def test_no_warning_small_model(self):
        result = check_model_mismatch(
            "ollama", "llama3:8b", 16384, dismissed=False
        )
        assert result is None

    def test_no_warning_empty_model(self):
        result = check_model_mismatch("ollama", "", 16384, dismissed=False)
        assert result is None

    def test_no_warning_non_local_provider(self):
        result = check_model_mismatch(
            "openai", "gpt-4-70b-fake", 16384, dismissed=False
        )
        assert result is None

    def test_no_warning_at_boundary_8192(self):
        """Context exactly at 8192 should NOT trigger (uses <=)."""
        result = check_model_mismatch(
            "ollama", "llama3.1:70b", 8192, dismissed=False
        )
        assert result is None

    def test_warns_at_boundary_8193(self):
        """Context at 8193 is just above the threshold — should trigger."""
        result = check_model_mismatch(
            "ollama", "llama3.1:70b", 8193, dismissed=False
        )
        assert result is not None
        assert result["type"] == "model_mismatch"

    def test_no_warning_low_context(self):
        result = check_model_mismatch(
            "ollama", "llama3.1:70b", 4096, dismissed=False
        )
        assert result is None

    def test_no_warning_when_dismissed(self):
        result = check_model_mismatch(
            "ollama", "llama3.1:70b", 16384, dismissed=True
        )
        assert result is None

    def test_model_name_in_message(self):
        result = check_model_mismatch(
            "ollama", "deepseek-r1:70b", 16384, dismissed=False
        )
        assert "deepseek-r1:70b" in result["message"]

    def test_context_value_in_message(self):
        result = check_model_mismatch(
            "ollama", "llama3.1:70b", 32000, dismissed=False
        )
        assert "32,000" in result["message"]

    def test_warning_dict_has_all_required_keys(self):
        result = check_model_mismatch(
            "ollama", "llama3.1:70b", 16384, dismissed=False
        )
        assert set(result.keys()) == {
            "type",
            "icon",
            "title",
            "message",
            "dismissKey",
        }

    def test_no_warning_none_model(self):
        """None model should not crash — treated as falsy like empty string."""
        result = check_model_mismatch("ollama", None, 16384, dismissed=False)
        assert result is None

    def test_dismiss_key(self):
        result = check_model_mismatch(
            "ollama", "llama3.1:70b", 16384, dismissed=False
        )
        assert result["dismissKey"] == "app.warnings.dismiss_model_mismatch"


class TestCheckLegacyServerConfig:
    """Tests for check_legacy_server_config."""

    def test_no_warning_when_dismissed(self, tmp_path):
        config_file = tmp_path / "server_config.json"
        config_file.write_text('{"port": 9999}', encoding="utf-8")
        with patch(
            "local_deep_research.web.server_config.get_server_config_path",
            return_value=config_file,
        ):
            assert check_legacy_server_config(dismissed=True) is None

    def test_no_warning_when_file_missing(self, tmp_path):
        config_file = tmp_path / "server_config.json"
        with patch(
            "local_deep_research.web.server_config.get_server_config_path",
            return_value=config_file,
        ):
            assert check_legacy_server_config(dismissed=False) is None

    def test_no_warning_when_all_defaults(self, tmp_path):
        """File exists but all values match defaults — no warning."""
        import json

        from local_deep_research.web.server_config import _DEFAULTS

        config_file = tmp_path / "server_config.json"
        config_file.write_text(json.dumps(_DEFAULTS), encoding="utf-8")
        with patch(
            "local_deep_research.web.server_config.get_server_config_path",
            return_value=config_file,
        ):
            assert check_legacy_server_config(dismissed=False) is None

    def test_no_warning_when_empty_object(self, tmp_path):
        """Empty JSON object has no customizations — no warning."""
        config_file = tmp_path / "server_config.json"
        config_file.write_text("{}", encoding="utf-8")
        with patch(
            "local_deep_research.web.server_config.get_server_config_path",
            return_value=config_file,
        ):
            assert check_legacy_server_config(dismissed=False) is None

    def test_warns_when_non_default_value(self, tmp_path):
        """File has a customized value — should warn."""
        config_file = tmp_path / "server_config.json"
        config_file.write_text('{"port": 9999}', encoding="utf-8")
        with patch(
            "local_deep_research.web.server_config.get_server_config_path",
            return_value=config_file,
        ):
            result = check_legacy_server_config(dismissed=False)
        assert result is not None
        assert result["type"] == "legacy_server_config"
        assert result["dismissKey"] == "app.warnings.dismiss_legacy_config"
        assert set(result.keys()) == {
            "type",
            "icon",
            "title",
            "message",
            "dismissKey",
        }

    def test_warns_when_unrecognized_keys(self, tmp_path):
        """File has unrecognized keys — should warn."""
        config_file = tmp_path / "server_config.json"
        config_file.write_text('{"custom_thing": true}', encoding="utf-8")
        with patch(
            "local_deep_research.web.server_config.get_server_config_path",
            return_value=config_file,
        ):
            result = check_legacy_server_config(dismissed=False)
        assert result is not None
        assert result["type"] == "legacy_server_config"

    def test_no_warning_on_malformed_json(self, tmp_path):
        """Malformed JSON should not crash — just return None."""
        config_file = tmp_path / "server_config.json"
        config_file.write_text("not valid json", encoding="utf-8")
        with patch(
            "local_deep_research.web.server_config.get_server_config_path",
            return_value=config_file,
        ):
            assert check_legacy_server_config(dismissed=False) is None

    def test_no_warning_on_non_dict_json(self, tmp_path):
        """JSON array instead of object should not crash — just return None."""
        config_file = tmp_path / "server_config.json"
        config_file.write_text("[1, 2, 3]", encoding="utf-8")
        with patch(
            "local_deep_research.web.server_config.get_server_config_path",
            return_value=config_file,
        ):
            assert check_legacy_server_config(dismissed=False) is None

    def test_warns_with_partial_defaults_and_one_custom(self, tmp_path):
        """Mix of default and non-default values — should warn on the mismatch."""
        import json

        config_file = tmp_path / "server_config.json"
        config_file.write_text(
            json.dumps({"host": "0.0.0.0", "port": 5000, "debug": True}),
            encoding="utf-8",
        )
        with patch(
            "local_deep_research.web.server_config.get_server_config_path",
            return_value=config_file,
        ):
            result = check_legacy_server_config(dismissed=False)
        assert result is not None
        assert result["type"] == "legacy_server_config"

    def test_warns_unrecognized_keys_with_default_recognized(self, tmp_path):
        """All recognized keys match defaults but an unknown key exists — should warn."""
        import json

        from local_deep_research.web.server_config import _DEFAULTS

        data = dict(_DEFAULTS)
        data["unknown"] = "val"
        config_file = tmp_path / "server_config.json"
        config_file.write_text(json.dumps(data), encoding="utf-8")
        with patch(
            "local_deep_research.web.server_config.get_server_config_path",
            return_value=config_file,
        ):
            result = check_legacy_server_config(dismissed=False)
        assert result is not None
        assert result["type"] == "legacy_server_config"

    def test_warns_when_value_is_none(self, tmp_path):
        """null value differs from default — should warn."""
        config_file = tmp_path / "server_config.json"
        config_file.write_text('{"port": null}', encoding="utf-8")
        with patch(
            "local_deep_research.web.server_config.get_server_config_path",
            return_value=config_file,
        ):
            result = check_legacy_server_config(dismissed=False)
        assert result is not None
        assert result["type"] == "legacy_server_config"

    def test_warns_when_type_differs_from_default(self, tmp_path):
        """String '5000' != int 5000 — strict comparison should catch this."""
        config_file = tmp_path / "server_config.json"
        config_file.write_text('{"port": "5000"}', encoding="utf-8")
        with patch(
            "local_deep_research.web.server_config.get_server_config_path",
            return_value=config_file,
        ):
            result = check_legacy_server_config(dismissed=False)
        assert result is not None
        assert result["type"] == "legacy_server_config"
