"""Tests for LLM registry module."""

from unittest.mock import MagicMock

import pytest

from local_deep_research.llm.llm_registry import (
    LLMRegistry,
    register_llm,
    unregister_llm,
    get_llm_from_registry,
    is_llm_registered,
    list_registered_llms,
    clear_llm_registry,
    _llm_registry,
)


class TestLLMRegistry:
    """Tests for LLMRegistry class."""

    @pytest.fixture(autouse=True)
    def clean_registry(self):
        """Clean registry before each test."""
        _llm_registry.clear()
        yield
        _llm_registry.clear()

    def test_register_stores_llm(self):
        """Should store LLM in registry."""
        registry = LLMRegistry()
        mock_llm = MagicMock()

        registry.register("test", mock_llm)

        assert registry.get("test") is mock_llm

    def test_register_normalizes_name_to_lowercase(self):
        """Should normalize name to lowercase."""
        registry = LLMRegistry()
        mock_llm = MagicMock()

        registry.register("TestLLM", mock_llm)

        assert registry.get("testllm") is mock_llm
        assert registry.get("TestLLM") is mock_llm
        assert registry.get("TESTLLM") is mock_llm

    def test_register_overwrites_existing(self):
        """Should overwrite existing LLM with same name."""
        registry = LLMRegistry()
        llm1 = MagicMock()
        llm2 = MagicMock()

        registry.register("test", llm1)
        registry.register("test", llm2)

        assert registry.get("test") is llm2

    def test_unregister_removes_llm(self):
        """Should remove LLM from registry."""
        registry = LLMRegistry()
        mock_llm = MagicMock()
        registry.register("test", mock_llm)

        registry.unregister("test")

        assert registry.get("test") is None

    def test_unregister_case_insensitive(self):
        """Should unregister case-insensitively."""
        registry = LLMRegistry()
        mock_llm = MagicMock()
        registry.register("TestLLM", mock_llm)

        registry.unregister("TESTLLM")

        assert registry.get("testllm") is None

    def test_unregister_nonexistent_no_error(self):
        """Should not raise error for nonexistent LLM."""
        registry = LLMRegistry()
        registry.unregister("nonexistent")  # Should not raise

    def test_get_returns_none_for_nonexistent(self):
        """Should return None for nonexistent LLM."""
        registry = LLMRegistry()
        assert registry.get("nonexistent") is None

    def test_is_registered_returns_true_when_exists(self):
        """Should return True when LLM is registered."""
        registry = LLMRegistry()
        registry.register("test", MagicMock())

        assert registry.is_registered("test") is True
        assert registry.is_registered("TEST") is True

    def test_is_registered_returns_false_when_not_exists(self):
        """Should return False when LLM not registered."""
        registry = LLMRegistry()
        assert registry.is_registered("nonexistent") is False

    def test_list_registered_returns_names(self):
        """Should return list of registered names."""
        registry = LLMRegistry()
        registry.register("llm1", MagicMock())
        registry.register("LLM2", MagicMock())

        names = registry.list_registered()

        assert "llm1" in names
        assert "llm2" in names  # Normalized to lowercase

    def test_list_registered_returns_empty_when_empty(self):
        """Should return empty list when no LLMs registered."""
        registry = LLMRegistry()
        assert registry.list_registered() == []

    def test_clear_removes_all(self):
        """Should remove all registered LLMs."""
        registry = LLMRegistry()
        registry.register("llm1", MagicMock())
        registry.register("llm2", MagicMock())

        registry.clear()

        assert registry.list_registered() == []


class TestGlobalRegistryFunctions:
    """Tests for global registry functions."""

    @pytest.fixture(autouse=True)
    def clean_registry(self):
        """Clean registry before each test."""
        clear_llm_registry()
        yield
        clear_llm_registry()

    def test_register_llm_adds_to_global_registry(self):
        """Should add LLM to global registry."""
        mock_llm = MagicMock()

        register_llm("global_test", mock_llm)

        assert get_llm_from_registry("global_test") is mock_llm

    def test_unregister_llm_removes_from_global_registry(self):
        """Should remove LLM from global registry."""
        mock_llm = MagicMock()
        register_llm("to_remove", mock_llm)

        unregister_llm("to_remove")

        assert get_llm_from_registry("to_remove") is None

    def test_get_llm_from_registry_returns_none_for_nonexistent(self):
        """Should return None for nonexistent LLM."""
        assert get_llm_from_registry("nonexistent") is None

    def test_is_llm_registered_checks_global_registry(self):
        """Should check global registry."""
        register_llm("check_test", MagicMock())

        assert is_llm_registered("check_test") is True
        assert is_llm_registered("not_registered") is False

    def test_list_registered_llms_returns_global_list(self):
        """Should return list from global registry."""
        register_llm("list_test1", MagicMock())
        register_llm("list_test2", MagicMock())

        names = list_registered_llms()

        assert "list_test1" in names
        assert "list_test2" in names

    def test_clear_llm_registry_clears_global_registry(self):
        """Should clear global registry."""
        register_llm("clear_test", MagicMock())

        clear_llm_registry()

        assert list_registered_llms() == []

    def test_register_factory_function(self):
        """Should accept factory function."""

        def factory_fn(model_name, temperature, settings_snapshot):
            return MagicMock()

        register_llm("factory_test", factory_fn)

        result = get_llm_from_registry("factory_test")
        assert callable(result)
