"""Unit tests for ToolRegistry, ProfileRegistry, BackendRegistry."""

import pytest

from navi.core.registry import BackendRegistry, ProfileRegistry, ToolRegistry
from navi.context_providers import ContextProviderRegistry
from navi.exceptions import ProfileNotFound, ToolNotFound
from tests.conftest_factory import FakeLLMBackend, FakeTool, make_profile


class TestToolRegistry:
    def test_register_and_get(self):
        reg = ToolRegistry()
        tool = FakeTool("test")
        reg.register(tool)
        assert reg.get("test") is tool

    def test_get_missing_raises(self):
        reg = ToolRegistry()
        with pytest.raises(ToolNotFound):
            reg.get("missing")

    def test_resolve(self):
        reg = ToolRegistry()
        reg.register(FakeTool("a"), builtin=True)
        reg.register(FakeTool("b"))
        tools = reg.resolve(["a", "b"])
        assert [t.name for t in tools] == ["a", "b"]

    def test_all(self):
        reg = ToolRegistry()
        reg.register(FakeTool("a"))
        reg.register(FakeTool("b"))
        assert len(reg.all()) == 2

    def test_builtin_names_preserved_on_reload(self):
        reg = ToolRegistry()
        reg.register(FakeTool("builtin"), builtin=True)
        reg.register(FakeTool("user"))
        # reload_user_tools would drop non-builtins — simulate by checking names
        assert "builtin" in reg._builtin_names
        assert "user" not in reg._builtin_names


class TestProfileRegistry:
    def test_register_and_get(self):
        reg = ProfileRegistry()
        p = make_profile("dev")
        reg.register(p)
        assert reg.get("dev") is p

    def test_get_missing_raises(self):
        reg = ProfileRegistry()
        with pytest.raises(ProfileNotFound):
            reg.get("missing")

    def test_all(self):
        reg = ProfileRegistry()
        reg.register(make_profile("a"))
        reg.register(make_profile("b"))
        assert len(reg.all()) == 2


class TestBackendRegistry:
    def test_register_and_get(self):
        reg = BackendRegistry()
        backend = FakeLLMBackend()
        reg.register("ollama", backend)
        assert reg.get("ollama") is backend

    def test_get_missing_raises(self):
        reg = BackendRegistry()
        with pytest.raises(KeyError, match="not registered"):
            reg.get("missing")

    def test_all_keys(self):
        reg = BackendRegistry()
        reg.register("a", FakeLLMBackend())
        reg.register("b", FakeLLMBackend())
        assert sorted(reg.all_keys()) == ["a", "b"]


class TestBackendDiscovery:
    def test_primary_ollama_uses_expanded_timeout(self, monkeypatch):
        import navi.core.registry as registry_mod
        import navi.llm.fallback as fallback_mod
        from navi.config import Settings

        captured = {}

        class FakeOllamaBackend:
            def __init__(self, **kwargs):
                captured.update(kwargs)

        test_settings = Settings(
            ollama_backends_file="",
            openai_api_key="",
            ollama_request_timeout=30,
            llm_complete_timeout=120,
            llm_stream_first_chunk_timeout=180,
        )
        monkeypatch.setattr(registry_mod, "settings", test_settings)
        monkeypatch.setattr("navi.config.settings", test_settings)
        monkeypatch.setattr(fallback_mod, "OllamaBackend", FakeOllamaBackend)

        discovered = registry_mod._discover_backends()

        assert discovered[0][0] == "ollama"
        assert isinstance(discovered[0][1], fallback_mod.FallbackOllamaBackend)
        # _get_client lazily creates the inner OllamaBackend
        discovered[0][1]._get_client(fallback_mod.ServerEntry(host="http://test"))
        assert captured["timeout"] == 180


class TestContextProviderRegistry:
    def test_all_returns_registered_providers(self):
        reg = ContextProviderRegistry()

        class Provider:
            name = "provider"
            description = "Provider"

            async def get_context(self):
                return None

        reg.register(Provider)

        assert reg.all() == [Provider]
