"""
Tests for llm/llm_registry.py

Tests cover:
- LLMRegistry class methods
- Global registry functions
- Thread safety
- Case-insensitive registration
"""

import threading
from unittest.mock import Mock


class TestLLMRegistryInit:
    """Tests for LLMRegistry initialization."""

    def test_init_creates_empty_registry(self):
        """Test that initialization creates empty registry."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()
        assert registry.list_registered() == []

    def test_init_creates_lock(self):
        """Test that initialization creates threading lock."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()
        assert registry._lock is not None
        assert isinstance(registry._lock, type(threading.Lock()))


class TestLLMRegistryRegister:
    """Tests for LLMRegistry.register method."""

    def test_register_llm_instance(self):
        """Test registering an LLM instance."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()
        mock_llm = Mock()

        registry.register("test-llm", mock_llm)

        assert registry.is_registered("test-llm")
        assert registry.get("test-llm") is mock_llm

    def test_register_llm_factory(self):
        """Test registering an LLM factory function."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()
        factory = Mock(return_value=Mock())

        registry.register("test-factory", factory)

        assert registry.is_registered("test-factory")
        assert registry.get("test-factory") is factory

    def test_register_normalizes_name_to_lowercase(self):
        """Test that registration normalizes names to lowercase."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()
        mock_llm = Mock()

        registry.register("Test-LLM", mock_llm)

        assert registry.is_registered("test-llm")
        assert registry.is_registered("TEST-LLM")
        assert registry.is_registered("Test-LLM")

    def test_register_overwrites_existing(self):
        """Test that registering overwrites existing LLM with same name."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()
        mock_llm1 = Mock(name="llm1")
        mock_llm2 = Mock(name="llm2")

        registry.register("test", mock_llm1)
        registry.register("test", mock_llm2)

        assert registry.get("test") is mock_llm2


class TestLLMRegistryUnregister:
    """Tests for LLMRegistry.unregister method."""

    def test_unregister_removes_llm(self):
        """Test that unregister removes the LLM."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()
        mock_llm = Mock()

        registry.register("test", mock_llm)
        assert registry.is_registered("test")

        registry.unregister("test")
        assert not registry.is_registered("test")

    def test_unregister_nonexistent_does_nothing(self):
        """Test that unregistering non-existent LLM doesn't raise."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()

        # Should not raise
        registry.unregister("nonexistent")

    def test_unregister_is_case_insensitive(self):
        """Test that unregister is case-insensitive."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()
        mock_llm = Mock()

        registry.register("Test-LLM", mock_llm)
        registry.unregister("TEST-LLM")

        assert not registry.is_registered("test-llm")


class TestLLMRegistryGet:
    """Tests for LLMRegistry.get method."""

    def test_get_returns_registered_llm(self):
        """Test that get returns the registered LLM."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()
        mock_llm = Mock()

        registry.register("test", mock_llm)

        assert registry.get("test") is mock_llm

    def test_get_returns_none_for_nonexistent(self):
        """Test that get returns None for non-existent LLM."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()

        assert registry.get("nonexistent") is None

    def test_get_is_case_insensitive(self):
        """Test that get is case-insensitive."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()
        mock_llm = Mock()

        registry.register("Test-LLM", mock_llm)

        assert registry.get("test-llm") is mock_llm
        assert registry.get("TEST-LLM") is mock_llm


class TestLLMRegistryIsRegistered:
    """Tests for LLMRegistry.is_registered method."""

    def test_is_registered_returns_true_for_registered(self):
        """Test that is_registered returns True for registered LLM."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()
        mock_llm = Mock()

        registry.register("test", mock_llm)

        assert registry.is_registered("test") is True

    def test_is_registered_returns_false_for_nonexistent(self):
        """Test that is_registered returns False for non-existent LLM."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()

        assert registry.is_registered("nonexistent") is False


class TestLLMRegistryListRegistered:
    """Tests for LLMRegistry.list_registered method."""

    def test_list_registered_returns_empty_for_new_registry(self):
        """Test that list_registered returns empty list for new registry."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()

        assert registry.list_registered() == []

    def test_list_registered_returns_all_names(self):
        """Test that list_registered returns all registered names."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()

        registry.register("llm1", Mock())
        registry.register("llm2", Mock())
        registry.register("llm3", Mock())

        names = registry.list_registered()
        assert len(names) == 3
        assert "llm1" in names
        assert "llm2" in names
        assert "llm3" in names


class TestLLMRegistryClear:
    """Tests for LLMRegistry.clear method."""

    def test_clear_removes_all_llms(self):
        """Test that clear removes all registered LLMs."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()

        registry.register("llm1", Mock())
        registry.register("llm2", Mock())
        registry.register("llm3", Mock())

        assert len(registry.list_registered()) == 3

        registry.clear()

        assert registry.list_registered() == []


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

    def test_register_llm_function(self):
        """Test global register_llm function."""
        from local_deep_research.llm.llm_registry import (
            register_llm,
            is_llm_registered,
            clear_llm_registry,
        )

        try:
            mock_llm = Mock()
            register_llm("global-test", mock_llm)
            assert is_llm_registered("global-test")
        finally:
            clear_llm_registry()

    def test_unregister_llm_function(self):
        """Test global unregister_llm function."""
        from local_deep_research.llm.llm_registry import (
            register_llm,
            unregister_llm,
            is_llm_registered,
            clear_llm_registry,
        )

        try:
            register_llm("global-test", Mock())
            assert is_llm_registered("global-test")

            unregister_llm("global-test")
            assert not is_llm_registered("global-test")
        finally:
            clear_llm_registry()

    def test_get_llm_from_registry_function(self):
        """Test global get_llm_from_registry function."""
        from local_deep_research.llm.llm_registry import (
            register_llm,
            get_llm_from_registry,
            clear_llm_registry,
        )

        try:
            mock_llm = Mock()
            register_llm("global-test", mock_llm)

            assert get_llm_from_registry("global-test") is mock_llm
        finally:
            clear_llm_registry()

    def test_list_registered_llms_function(self):
        """Test global list_registered_llms function."""
        from local_deep_research.llm.llm_registry import (
            register_llm,
            list_registered_llms,
            clear_llm_registry,
        )

        try:
            register_llm("test1", Mock())
            register_llm("test2", Mock())

            names = list_registered_llms()
            assert "test1" in names
            assert "test2" in names
        finally:
            clear_llm_registry()

    def test_clear_llm_registry_function(self):
        """Test global clear_llm_registry function."""
        from local_deep_research.llm.llm_registry import (
            register_llm,
            list_registered_llms,
            clear_llm_registry,
        )

        register_llm("test1", Mock())
        register_llm("test2", Mock())

        clear_llm_registry()

        assert list_registered_llms() == []


class TestThreadSafety:
    """Tests for thread safety of LLMRegistry."""

    def test_concurrent_registration(self):
        """Test that concurrent registrations are thread-safe."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()
        errors = []

        def register_llm(name):
            try:
                registry.register(name, Mock())
            except Exception as e:
                errors.append(e)

        threads = [
            threading.Thread(target=register_llm, args=(f"llm-{i}",))
            for i in range(100)
        ]

        for t in threads:
            t.start()
        for t in threads:
            t.join()

        assert len(errors) == 0
        assert len(registry.list_registered()) == 100

    def test_concurrent_get(self):
        """Test that concurrent gets are thread-safe."""
        from local_deep_research.llm.llm_registry import LLMRegistry

        registry = LLMRegistry()
        mock_llm = Mock()
        registry.register("test", mock_llm)
        results = []

        def get_llm():
            result = registry.get("test")
            results.append(result)

        threads = [threading.Thread(target=get_llm) for _ in range(100)]

        for t in threads:
            t.start()
        for t in threads:
            t.join()

        assert len(results) == 100
        assert all(r is mock_llm for r in results)
