"""Tests to verify all imports are correct and non-conflicting."""

import importlib
import sys
from collections import defaultdict

import pytest


class TestTopLevelImports:
    """Test top-level package imports."""

    def test_rlm_import(self):
        """Test that main rlm package can be imported."""
        import groknroll

        assert hasattr(groknroll, "RLM")
        assert "RLM" in groknroll.__all__

    def test_rlm_rlm_import(self):
        """Test that RLM class can be imported from groknroll."""
        from groknroll import RLM

        assert RLM is not None

    def test_rlm_core_rlm_import(self):
        """Test that RLM can be imported from groknroll.core.groknroll."""
        from groknroll.core.rlm import RLM

        assert RLM is not None


class TestClientImports:
    """Test client module imports."""

    def test_clients_module_import(self):
        """Test that clients module can be imported."""
        import groknroll.clients

        assert hasattr(groknroll.clients, "get_client")
        assert hasattr(groknroll.clients, "BaseLM")

    def test_base_lm_import(self):
        """Test BaseLM import."""
        from groknroll.clients.base_lm import BaseLM

        assert BaseLM is not None

    def test_openai_client_import(self):
        """Test OpenAIClient import."""
        pytest.importorskip("openai")
        from groknroll.clients.openai import OpenAIClient

        assert OpenAIClient is not None

    def test_anthropic_client_import(self):
        """Test AnthropicClient import."""
        pytest.importorskip("anthropic")
        from groknroll.clients.anthropic import AnthropicClient

        assert AnthropicClient is not None

    def test_portkey_client_import(self):
        """Test PortkeyClient import."""
        pytest.importorskip("portkey_ai")
        from groknroll.clients.portkey import PortkeyClient

        assert PortkeyClient is not None

    def test_litellm_client_import(self):
        """Test LiteLLMClient import."""
        pytest.importorskip("litellm")
        from groknroll.clients.litellm import LiteLLMClient

        assert LiteLLMClient is not None

    def test_get_client_function(self):
        """Test get_client function import."""
        from groknroll.clients import get_client

        assert callable(get_client)


class TestCoreImports:
    """Test core module imports."""

    def test_core_types_import(self):
        """Test core types imports."""
        from groknroll.core.types import (
            ClientBackend,
            CodeBlock,
            ModelUsageSummary,
            QueryMetadata,
            REPLResult,
            RLMIteration,
            RLMMetadata,
            UsageSummary,
        )

        assert ClientBackend is not None
        assert CodeBlock is not None
        assert ModelUsageSummary is not None
        assert QueryMetadata is not None
        assert REPLResult is not None
        assert RLMIteration is not None
        assert RLMMetadata is not None
        assert UsageSummary is not None

    def test_core_rlm_import(self):
        """Test core RLM import."""
        from groknroll.core.rlm import RLM

        assert RLM is not None

    def test_core_lm_handler_import(self):
        """Test LMHandler import."""
        from groknroll.core.lm_handler import LMHandler

        assert LMHandler is not None

    def test_core_comms_utils_import(self):
        """Test comms_utils imports."""
        from groknroll.core.comms_utils import (
            LMRequest,
            LMResponse,
            send_lm_request,
            send_lm_request_batched,
            socket_recv,
            socket_send,
        )

        assert LMRequest is not None
        assert LMResponse is not None
        assert callable(send_lm_request)
        assert callable(send_lm_request_batched)
        assert callable(socket_recv)
        assert callable(socket_send)


class TestEnvironmentImports:
    """Test environment module imports."""

    def test_environments_module_import(self):
        """Test that environments module can be imported."""
        import groknroll.environments

        assert hasattr(groknroll.environments, "get_environment")
        assert hasattr(groknroll.environments, "BaseEnv")
        assert hasattr(groknroll.environments, "LocalREPL")

    def test_base_env_import(self):
        """Test BaseEnv import."""
        from groknroll.environments.base_env import BaseEnv, IsolatedEnv, NonIsolatedEnv

        assert BaseEnv is not None
        assert IsolatedEnv is not None
        assert NonIsolatedEnv is not None

    def test_local_repl_import(self):
        """Test LocalREPL import."""
        from groknroll.environments.local_repl import LocalREPL

        assert LocalREPL is not None

    def test_modal_repl_import(self):
        """Test ModalREPL import."""
        pytest.importorskip("modal")
        from groknroll.environments.modal_repl import ModalREPL

        assert ModalREPL is not None

    def test_docker_repl_import(self):
        """Test DockerREPL import."""
        from groknroll.environments.docker_repl import DockerREPL

        assert DockerREPL is not None

    def test_prime_repl_import(self):
        """Test PrimeREPL import."""
        pytest.importorskip("prime_sandboxes")
        from groknroll.environments.prime_repl import PrimeREPL

        assert PrimeREPL is not None

    def test_get_environment_function(self):
        """Test get_environment function import."""
        from groknroll.environments import get_environment

        assert callable(get_environment)


class TestLoggerImports:
    """Test logger module imports."""

    def test_logger_module_import(self):
        """Test that logger module can be imported."""
        import groknroll.logger

        assert hasattr(groknroll.logger, "RLMLogger")
        assert hasattr(groknroll.logger, "VerbosePrinter")
        assert "RLMLogger" in groknroll.logger.__all__
        assert "VerbosePrinter" in groknroll.logger.__all__

    def test_rlm_logger_import(self):
        """Test RLMLogger import."""
        from groknroll.logger.rlm_logger import RLMLogger

        assert RLMLogger is not None

    def test_verbose_import(self):
        """Test VerbosePrinter import."""
        from groknroll.logger.verbose import VerbosePrinter

        assert VerbosePrinter is not None


class TestUtilsImports:
    """Test utils module imports."""

    def test_parsing_import(self):
        """Test parsing module import."""
        from groknroll.utils.parsing import (
            find_code_blocks,
            find_final_answer,
            format_execution_result,
            format_iteration,
        )

        assert callable(find_code_blocks)
        assert callable(find_final_answer)
        assert callable(format_iteration)
        assert callable(format_execution_result)

    def test_prompts_import(self):
        """Test prompts module import."""
        from groknroll.utils.prompts import (
            RLM_SYSTEM_PROMPT,
            USER_PROMPT,
            build_rlm_system_prompt,
            build_user_prompt,
        )

        assert RLM_SYSTEM_PROMPT is not None
        assert USER_PROMPT is not None
        assert callable(build_rlm_system_prompt)
        assert callable(build_user_prompt)

    def test_rlm_utils_import(self):
        """Test rlm_utils module import."""
        from groknroll.utils.rlm_utils import filter_sensitive_keys

        assert callable(filter_sensitive_keys)


class TestImportConflicts:
    """Test for import conflicts and naming issues."""

    def test_no_duplicate_names_in_rlm_all(self):
        """Test that __all__ in groknroll.__init__ has no duplicates."""
        import groknroll

        if hasattr(groknroll, "__all__"):
            all_items = groknroll.__all__
            assert len(all_items) == len(set(all_items)), (
                f"Duplicate items in groknroll.__all__: {all_items}"
            )

    def test_no_duplicate_names_in_logger_all(self):
        """Test that __all__ in groknroll.logger.__init__ has no duplicates."""
        import groknroll.logger

        if hasattr(groknroll.logger, "__all__"):
            all_items = groknroll.logger.__all__
            assert len(all_items) == len(set(all_items)), (
                f"Duplicate items in groknroll.logger.__all__: {all_items}"
            )

    def test_all_declarations_match_exports(self):
        """Test that __all__ declarations match actual exports."""
        import groknroll
        import groknroll.logger

        # Test groknroll.__all__
        if hasattr(groknroll, "__all__"):
            for name in groknroll.__all__:
                assert hasattr(groknroll, name), (
                    f"groknroll.__all__ declares '{name}' but it's not exported"
                )

        # Test groknroll.logger.__all__
        if hasattr(groknroll.logger, "__all__"):
            for name in groknroll.logger.__all__:
                assert hasattr(groknroll.logger, name), (
                    f"groknroll.logger.__all__ declares '{name}' but it's not exported"
                )

    def test_no_circular_imports(self):
        """Test that modules can be imported without circular import errors."""
        # Core modules that should always be importable
        core_modules = [
            "groknroll",
            "groknroll.clients",
            "groknroll.clients.base_lm",
            "groknroll.core",
            "groknroll.core.types",
            "groknroll.core.rlm",
            "groknroll.core.lm_handler",
            "groknroll.core.comms_utils",
            "groknroll.environments",
            "groknroll.environments.base_env",
            "groknroll.environments.local_repl",
            "groknroll.environments.docker_repl",
            "groknroll.logger",
            "groknroll.logger.rlm_logger",
            "groknroll.logger.verbose",
            "groknroll.utils",
            "groknroll.utils.parsing",
            "groknroll.utils.prompts",
            "groknroll.utils.rlm_utils",
        ]

        # Optional modules that may not be available
        optional_modules = [
            ("groknroll.clients.openai", "openai"),
            ("groknroll.clients.anthropic", "anthropic"),
            ("groknroll.clients.portkey", "portkey_ai"),
            ("groknroll.clients.litellm", "litellm"),
            ("groknroll.environments.modal_repl", "modal"),
            ("groknroll.environments.prime_repl", "prime_sandboxes"),
        ]

        # Test core modules
        for module_name in core_modules:
            # Remove from sys.modules if present to test fresh import
            if module_name in sys.modules:
                del sys.modules[module_name]
            try:
                importlib.import_module(module_name)
            except ImportError as e:
                pytest.fail(f"Failed to import {module_name}: {e}")

        # Test optional modules (skip if dependency not available)
        for module_name, dependency in optional_modules:
            # Check if dependency is available
            try:
                importlib.import_module(dependency)
            except ImportError:
                continue  # Skip this module if dependency not available

            # If dependency is available, test the module import
            if module_name in sys.modules:
                del sys.modules[module_name]
            try:
                importlib.import_module(module_name)
            except ImportError as e:
                pytest.fail(f"Failed to import {module_name}: {e}")

    def test_no_naming_conflicts_across_modules(self):
        """Test that there are no naming conflicts across different modules."""
        # Collect all public names from each module
        module_exports: dict[str, set[str]] = {}

        # Check main modules
        import groknroll
        import groknroll.clients
        import groknroll.environments
        import groknroll.logger

        if hasattr(groknroll, "__all__"):
            module_exports["groknroll"] = set(groknroll.__all__)
        else:
            module_exports["groknroll"] = {name for name in dir(groknroll) if not name.startswith("_")}

        if hasattr(groknroll.clients, "__all__"):
            module_exports["groknroll.clients"] = set(groknroll.clients.__all__)
        else:
            module_exports["groknroll.clients"] = {
                name for name in dir(groknroll.clients) if not name.startswith("_")
            }

        if hasattr(groknroll.environments, "__all__"):
            module_exports["groknroll.environments"] = set(groknroll.environments.__all__)
        else:
            module_exports["groknroll.environments"] = {
                name for name in dir(groknroll.environments) if not name.startswith("_")
            }

        if hasattr(groknroll.logger, "__all__"):
            module_exports["groknroll.logger"] = set(groknroll.logger.__all__)
        else:
            module_exports["groknroll.logger"] = {
                name for name in dir(groknroll.logger) if not name.startswith("_")
            }

        # Check for conflicts (same name in multiple modules)
        name_to_modules: dict[str, list[str]] = defaultdict(list)
        for module_name, exports in module_exports.items():
            for export_name in exports:
                name_to_modules[export_name].append(module_name)

        conflicts = {name: modules for name, modules in name_to_modules.items() if len(modules) > 1}
        # Filter out common Python builtins/dunders and typing imports that are expected
        expected_duplicates = {
            "__file__",
            "__name__",
            "__package__",
            "__path__",
            "__doc__",
            "__loader__",
            "__spec__",
            "__cached__",
            "Any",  # Common typing import
            "Literal",  # Common typing import
            "Optional",  # Common typing import
            "Union",  # Common typing import
            "Dict",  # Common typing import
            "List",  # Common typing import
            "Tuple",  # Common typing import
            "Callable",  # Common typing import
        }
        conflicts = {
            name: modules for name, modules in conflicts.items() if name not in expected_duplicates
        }

        if conflicts:
            conflict_msg = "\n".join(
                f"  '{name}' exported from: {', '.join(modules)}"
                for name, modules in conflicts.items()
            )
            pytest.fail(f"Found naming conflicts across modules:\n{conflict_msg}")


class TestImportCompleteness:
    """Test that all expected imports are available."""

    def test_all_client_classes_importable(self):
        """Test that all client classes can be imported."""
        from groknroll.clients.base_lm import BaseLM

        # Verify BaseLM is a class
        assert isinstance(BaseLM, type)

        # Test optional client classes
        try:
            pytest.importorskip("openai")
            from groknroll.clients.openai import OpenAIClient

            assert isinstance(OpenAIClient, type)
        except Exception:
            pass

        try:
            pytest.importorskip("anthropic")
            from groknroll.clients.anthropic import AnthropicClient

            assert isinstance(AnthropicClient, type)
        except Exception:
            pass

        try:
            pytest.importorskip("portkey_ai")
            from groknroll.clients.portkey import PortkeyClient

            assert isinstance(PortkeyClient, type)
        except Exception:
            pass

        try:
            pytest.importorskip("litellm")
            from groknroll.clients.litellm import LiteLLMClient

            assert isinstance(LiteLLMClient, type)
        except Exception:
            pass

    def test_all_environment_classes_importable(self):
        """Test that all environment classes can be imported."""
        from groknroll.environments.base_env import BaseEnv, IsolatedEnv, NonIsolatedEnv
        from groknroll.environments.docker_repl import DockerREPL
        from groknroll.environments.local_repl import LocalREPL

        # Verify they're all classes
        assert isinstance(BaseEnv, type)
        assert isinstance(IsolatedEnv, type)
        assert isinstance(NonIsolatedEnv, type)
        assert isinstance(LocalREPL, type)
        assert isinstance(DockerREPL, type)

        # Test optional ModalREPL
        try:
            pytest.importorskip("modal")
            from groknroll.environments.modal_repl import ModalREPL

            assert isinstance(ModalREPL, type)
        except Exception:
            pass

        # Test optional PrimeREPL
        try:
            pytest.importorskip("prime_sandboxes")
            from groknroll.environments.prime_repl import PrimeREPL

            assert isinstance(PrimeREPL, type)
        except Exception:
            pass
