"""
Comprehensive integration tests for providers/__init__.py including all functionality.
"""

import pytest
from unittest.mock import Mock, patch, MagicMock
import sys
import importlib
import inspect


class TestProvidersInitCompleteIntegration:
    """Complete integration tests for providers/__init__.py."""
    
    def test_full_module_import_structure(self):
        """Test complete module import structure and dependencies."""
        # Test importing the entire module
        import ai_utilities.providers as providers_module
        
        # Verify all expected attributes exist
        expected_attributes = [
            'BaseProvider', 'OpenAIProvider', 'OpenAICompatibleProvider',
            'create_provider', 'provider_factory', 'openai_compatible_provider',
            'ProviderCapabilities', 'ProviderCapabilityError',
            'ProviderConfigurationError', 'FileTransferError', 
            'MissingOptionalDependencyError'
        ]
        
        for attr in expected_attributes:
            assert hasattr(providers_module, attr), f"Missing attribute: {attr}"
        
        # Verify __all__ is correct
        assert providers_module.__all__ == expected_attributes
        
        # Verify all items in __all__ are importable
        for item in providers_module.__all__:
            obj = getattr(providers_module, item)
            assert obj is not None, f"Item {item} is None"
    
    def test_lazy_openai_provider_complete_lifecycle(self):
        """Test complete lifecycle of LazyOpenAIProvider."""
        from ai_utilities.providers import OpenAIProvider
        
        # Test multiple attribute accesses
        try:
            # Test class-level attributes
            if hasattr(OpenAIProvider, '__name__'):
                name = OpenAIProvider.__name__
                assert isinstance(name, str)
            
            # Test method access (if available)
            if hasattr(OpenAIProvider, '__doc__'):
                doc = OpenAIProvider.__doc__
                assert isinstance(doc, (str, type(None)))
            
            # Test that it behaves like a class
            assert callable(OpenAIProvider)
            
        except Exception as e:
            # If OpenAI is not available, should get MissingOptionalDependencyError
            assert "OpenAI provider requires extra 'openai'" in str(e)
    
    def test_openai_provider_lazy_loading_with_real_imports(self):
        """Test lazy loading with actual import behavior."""
        # Test direct instantiation of OpenAIProvider
        try:
            from ai_utilities.providers import OpenAIProvider
            
            # If successful, should be callable (like a class)
            assert callable(OpenAIProvider)
            
            # Test that it has expected attributes
            if hasattr(OpenAIProvider, '__name__'):
                assert 'OpenAI' in OpenAIProvider.__name__
                
        except Exception as e:
            # If OpenAI is not available, should get proper error
            assert "OpenAI provider requires extra 'openai'" in str(e)
    
    def test_provider_factory_integration(self):
        """Test integration with provider factory."""
        from ai_utilities.providers import create_provider
        
        # Test that create_provider is importable and callable
        assert callable(create_provider)
        
        # Test that it exists in the module
        assert hasattr(create_provider, '__call__')
    
    def test_exception_hierarchy_complete(self):
        """Test complete exception hierarchy and behavior."""
        from ai_utilities.providers import (
            ProviderCapabilityError, ProviderConfigurationError,
            FileTransferError, MissingOptionalDependencyError
        )
        
        # Test ProviderCapabilityError
        cap_error = ProviderCapabilityError("image_generation", "openai")
        assert cap_error.capability == "image_generation"
        assert cap_error.provider == "openai"
        assert "image_generation" in str(cap_error)
        
        # Test inheritance
        assert isinstance(cap_error, Exception)
        
        # Test ProviderConfigurationError
        config_error = ProviderConfigurationError("Missing API key", "openai")
        assert config_error.message == "Missing API key"
        assert config_error.provider == "openai"
        assert "Missing API key" in str(config_error)
        
        # Test FileTransferError with nested exception
        inner_error = IOError("File not found")
        file_error = FileTransferError("download", "openai", inner_error)
        assert file_error.operation == "download"
        assert file_error.provider == "openai"
        assert file_error.inner_error == inner_error
        
        # Test MissingOptionalDependencyError
        dep_error = MissingOptionalDependencyError("openai")
        assert "openai" in str(dep_error)
        
        # Test all exceptions can be caught as Exception
        exceptions = [cap_error, config_error, file_error, dep_error]
        for exc in exceptions:
            assert isinstance(exc, Exception)
    
    def test_base_provider_abstract_interface(self):
        """Test BaseProvider abstract interface compliance."""
        from ai_utilities.providers import BaseProvider
        import inspect
        
        # Verify it's abstract
        with pytest.raises(TypeError):
            BaseProvider()
        
        # Get abstract methods
        abstract_methods = []
        for name, method in inspect.getmembers(BaseProvider, predicate=inspect.isfunction):
            if getattr(method, '__isabstractmethod__', False):
                abstract_methods.append(name)
        
        # Should have abstract methods
        assert len(abstract_methods) > 0
        
        # Test that concrete providers implement these
        from ai_utilities.providers import OpenAICompatibleProvider
        assert issubclass(OpenAICompatibleProvider, BaseProvider)
        
        # Verify OpenAICompatibleProvider implements abstract methods
        for method_name in abstract_methods:
            assert hasattr(OpenAICompatibleProvider, method_name)
    
    def test_openai_compatible_provider_complete_interface(self):
        """Test OpenAICompatibleProvider complete interface."""
        from ai_utilities.providers import OpenAICompatibleProvider, BaseProvider
        import inspect
        
        # Verify class structure
        assert inspect.isclass(OpenAICompatibleProvider)
        assert issubclass(OpenAICompatibleProvider, BaseProvider)
        
        # Test that it has expected methods (even if not implemented)
        expected_methods = ['ask', 'ask_many', 'upload_file', 'download_file', 'generate_image']
        for method in expected_methods:
            assert hasattr(OpenAICompatibleProvider, method)
    
    def test_provider_capabilities_complete(self):
        """Test ProviderCapabilities complete functionality."""
        from ai_utilities.providers import ProviderCapabilities
        
        # Test instantiation
        capabilities = ProviderCapabilities()
        assert capabilities is not None
        
        # Test that it's a proper class with attributes
        assert hasattr(capabilities, '__dict__')
        
        # Test that it can be subclassed if needed
        class CustomCapabilities(ProviderCapabilities):
            def __init__(self):
                super().__init__()
                self.custom_feature = True
        
        custom = CustomCapabilities()
        assert custom.custom_feature is True
    
    def test_lazy_provider_error_propagation_complete(self):
        """Test complete error propagation in lazy provider."""
        from ai_utilities.providers import OpenAIProvider
        
        # Test that OpenAIProvider is callable and raises appropriate error when OpenAI is not available
        # This test assumes OpenAI is available in the test environment
        assert callable(OpenAIProvider)
        
        # Test that it has expected attributes
        if hasattr(OpenAIProvider, '__name__'):
            assert isinstance(OpenAIProvider.__name__, str)
    
    def test_multiple_lazy_provider_access_patterns(self):
        """Test various patterns of accessing lazy provider."""
        from ai_utilities.providers import OpenAIProvider
        
        # Test that OpenAIProvider can be accessed in multiple ways
        assert callable(OpenAIProvider)
        
        # Test access to common attributes
        if hasattr(OpenAIProvider, '__name__'):
            name = OpenAIProvider.__name__
            assert isinstance(name, str)
        
        if hasattr(OpenAIProvider, '__doc__'):
            doc = OpenAIProvider.__doc__
            assert isinstance(doc, (str, type(None)))
    
    def test_module_reload_behavior(self):
        """Test module reload behavior and state management."""
        import ai_utilities.providers
        
        # Ensure module is registered in sys.modules (robust to global reset fixtures)
        providers_mod = ai_utilities.providers
        sys.modules[providers_mod.__name__] = providers_mod
        
        # Get initial state
        initial_all = list(providers_mod.__all__)
        initial_openai_provider = providers_mod.OpenAIProvider
        
        # Reload module
        reloaded = importlib.reload(providers_mod)
        
        # Verify state is preserved (contract-first: focus on observable behavior)
        assert reloaded.__all__ == initial_all
        assert hasattr(reloaded, "OpenAIProvider")
        assert callable(reloaded.OpenAIProvider) or isinstance(reloaded.OpenAIProvider, type)
        
        # Verify provider resolution stability after reload
        reloaded_provider = reloaded.OpenAIProvider
        assert reloaded_provider.__name__ == initial_openai_provider.__name__
        assert reloaded_provider.__module__ == initial_openai_provider.__module__

    def test_concurrent_module_access(self):
        """Test concurrent access to provider module."""
        import threading
        import ai_utilities.providers
        
        results = []
        errors = []
        
        def access_module():
            try:
                # Test various accesses
                provider = ai_utilities.providers.OpenAIProvider
                create_func = ai_utilities.providers.create_provider
                error_class = ai_utilities.providers.ProviderCapabilityError
                
                results.append((provider, create_func, error_class))
            except Exception as e:
                errors.append(e)
        
        # Create multiple threads accessing the module
        threads = []
        for _ in range(5):
            thread = threading.Thread(target=access_module)
            threads.append(thread)
            thread.start()
        
        # Wait for all threads to complete
        for thread in threads:
            thread.join()
        
        # Verify no errors occurred
        assert len(errors) == 0
        assert len(results) == 5
    
    def test_memory_efficiency_lazy_loading(self):
        """Test memory efficiency of lazy loading."""
        from ai_utilities.providers import OpenAIProvider
        
        # Test that OpenAIProvider is available and callable
        assert callable(OpenAIProvider)
        
        # Test that accessing attributes doesn't cause issues
        if hasattr(OpenAIProvider, '__name__'):
            name = OpenAIProvider.__name__
            assert isinstance(name, str)
        
        # Multiple accesses should work fine
        for _ in range(3):
            if hasattr(OpenAIProvider, '__doc__'):
                doc = OpenAIProvider.__doc__
                assert isinstance(doc, (str, type(None)))
    
    def test_provider_module_completeness(self):
        """Test that provider module exports are complete and consistent."""
        import ai_utilities.providers as providers
        
        # Check that all exported items are actually available
        for item in providers.__all__:
            assert hasattr(providers, item), f"Exported item {item} not found"
            
            # Check that items are properly typed
            obj = getattr(providers, item)
            assert obj is not None, f"Exported item {item} is None"
        
        # Check that no unexpected public items are exported
        public_items = [attr for attr in dir(providers) if not attr.startswith('_')]
        exported_items = set(providers.__all__)
        unexpected_items = set(public_items) - exported_items
        
        # Only allow unexpected items that are modules or special attributes
        for item in unexpected_items:
            obj = getattr(providers, item)
            # Should be either a module or have special meaning
            assert inspect.ismodule(obj) or item in ['openai_provider', 'LazyOpenAIProvider', 'LazyOpenAICompatibleProvider'], f"Unexpected public export: {item}"
    
    def test_submodule_imports_consistency(self):
        """Test that submodule imports are consistent with __init__ exports."""
        # Test that we can import from submodules
        from ai_utilities.providers.base_provider import BaseProvider
        from ai_utilities.providers.openai_compatible_provider import OpenAICompatibleProvider
        from ai_utilities.providers.provider_factory import create_provider
        from ai_utilities.providers.provider_capabilities import ProviderCapabilities
        from ai_utilities.providers.provider_exceptions import (
            ProviderCapabilityError, ProviderConfigurationError,
            FileTransferError, MissingOptionalDependencyError
        )
        
        # Verify they're the same objects as imported from __init__
        import ai_utilities.providers as providers
        
        assert providers.BaseProvider is BaseProvider
        assert providers.OpenAICompatibleProvider is OpenAICompatibleProvider
        assert providers.create_provider is create_provider
        assert providers.ProviderCapabilities is ProviderCapabilities
        assert providers.ProviderCapabilityError is ProviderCapabilityError
        assert providers.ProviderConfigurationError is ProviderConfigurationError
        assert providers.FileTransferError is FileTransferError
        assert providers.MissingOptionalDependencyError is MissingOptionalDependencyError
    
    def test_error_message_quality(self):
        """Test quality and consistency of error messages."""
        from ai_utilities.providers import (
            ProviderCapabilityError, ProviderConfigurationError,
            FileTransferError, MissingOptionalDependencyError
        )
        
        # Test error message formats
        cap_error = ProviderCapabilityError("test_capability", "test_provider")
        assert "test_capability" in str(cap_error)
        # ProviderCapabilityError only includes the capability in the string representation
        # not the provider name
        
        config_error = ProviderConfigurationError("test_message", "test_provider")
        assert "test_message" in str(config_error)
        assert "test_provider" in str(config_error)
        
        file_error = FileTransferError("test_operation", "test_provider", Exception("inner"))
        assert "test_operation" in str(file_error)
        assert "test_provider" in str(file_error)
        
        dep_error = MissingOptionalDependencyError("test_package")
        assert "test_package" in str(dep_error)
    
    def test_lazy_provider_with_complex_attributes(self):
        """Test lazy provider with complex attribute access patterns."""
        from ai_utilities.providers import OpenAIProvider
        
        # Test that OpenAIProvider is available and callable
        assert callable(OpenAIProvider)
        
        # Test that accessing various attributes works
        attributes_to_check = ['__name__', '__doc__', '__module__']
        for attr in attributes_to_check:
            if hasattr(OpenAIProvider, attr):
                value = getattr(OpenAIProvider, attr)
                # Just verify we can access it without error
                assert value is not None or isinstance(value, (str, type(None)))
    
    def test_provider_module_documentation(self):
        """Test that provider module has proper documentation."""
        import ai_utilities.providers as providers
        
        # Check module docstring
        assert providers.__doc__ is not None
        assert len(providers.__doc__.strip()) > 0
        
        # Check that main classes have docstrings
        assert providers.BaseProvider.__doc__ is not None
        assert providers.OpenAICompatibleProvider.__doc__ is not None
        assert providers.ProviderCapabilities.__doc__ is not None
        
        # Check that exceptions have docstrings
        assert providers.ProviderCapabilityError.__doc__ is not None
        assert providers.ProviderConfigurationError.__doc__ is not None
        assert providers.FileTransferError.__doc__ is not None
        assert providers.MissingOptionalDependencyError.__doc__ is not None
