"""
Integration tests for fishertools enhancements.

Tests the interaction between all enhancement components:
- Learning System integration with Tutorial Engine and Example Repository
- Documentation Generator integration with Visual Documentation
- Error recovery and graceful degradation
- End-to-end learning scenarios
"""

import pytest
import tempfile
import os
from unittest.mock import Mock, patch
from fishertools.integration import FishertoolsIntegration, get_integration, reset_integration
from fishertools.learning.models import DifficultyLevel
from fishertools.errors.recovery import ErrorSeverity, RecoveryStrategy


class TestComponentIntegration:
    """Test integration between different components."""
    
    def setup_method(self):
        """Set up test environment."""
        reset_integration()
        self.integration = FishertoolsIntegration(project_name="test_project")
    
    def teardown_method(self):
        """Clean up after tests."""
        reset_integration()
    
    def test_learning_system_tutorial_engine_integration(self):
        """Test that Learning System properly integrates with Tutorial Engine."""
        # Start a learning session
        session = self.integration.start_learning_session("variables", "beginner", "test_user")
        
        assert session is not None
        assert session.topic == "variables"
        assert session.level == DifficultyLevel.BEGINNER
        assert len(session.exercises) > 0
        
        # Verify tutorial engine is connected
        assert self.integration.learning_system._tutorial_engine is not None
        assert self.integration.learning_system._tutorial_engine == self.integration.tutorial_engine
    
    def test_learning_system_example_repository_integration(self):
        """Test that Learning System integrates with Example Repository."""
        # Get examples for a topic
        examples = self.integration.example_repository.get_examples_by_topic("lists")
        assert len(examples) > 0
        
        # Start session and verify examples are used
        session = self.integration.start_learning_session("lists", "beginner", "test_user")
        
        # Should have interactive session if examples are available
        if hasattr(session, 'interactive_session') and session.interactive_session:
            assert session.interactive_session is not None
    
    def test_tutorial_engine_example_repository_integration(self):
        """Test that Tutorial Engine can access Example Repository."""
        # Set up the integration
        self.integration.tutorial_engine._example_repository = self.integration.example_repository
        
        # Test getting related examples
        related_examples = self.integration.tutorial_engine.get_related_examples("variables")
        
        # Should return examples if available
        assert isinstance(related_examples, list)
    
    def test_documentation_generator_visual_integration(self):
        """Test that Documentation Generator integrates with Visual Documentation."""
        if not self.integration.doc_generator or not self.integration.visual_docs:
            pytest.skip("Documentation components not available")
        
        # Set up integration
        self.integration.doc_generator._visual_docs = self.integration.visual_docs
        
        # Test enhanced documentation generation
        with tempfile.TemporaryDirectory() as temp_dir:
            test_module = os.path.join(temp_dir, "test_module.py")
            with open(test_module, 'w') as f:
                f.write('''
"""Test module for documentation."""

def test_function(param1: str, param2: int = 0) -> str:
    """Test function with parameters.
    
    Args:
        param1: First parameter
        param2: Second parameter
        
    Returns:
        str: Result string
    """
    return f"{param1}_{param2}"
''')
            
            try:
                result = self.integration.doc_generator.generate_enhanced_documentation([test_module])
                
                assert 'sphinx_docs' in result
                assert 'visual_artifacts' in result
                assert 'enhanced_files' in result
                
            except Exception as e:
                # Documentation generation might fail in test environment
                pytest.skip(f"Documentation generation failed: {e}")
    
    def test_session_manager_integration(self):
        """Test that Session Manager integrates with other components."""
        if not self.integration.session_manager:
            pytest.skip("Session manager not available")
        
        # Create session from example
        examples = self.integration.example_repository.get_examples_by_topic("variables")
        if examples:
            session = self.integration.session_manager.create_session_from_example(
                "test_user", examples[0]
            )
            
            assert session is not None
            assert len(session.exercises) > 0
            assert session.topic in examples[0].topics


class TestEndToEndScenarios:
    """Test complete end-to-end learning scenarios."""
    
    def setup_method(self):
        """Set up test environment."""
        reset_integration()
        self.integration = FishertoolsIntegration(project_name="test_project")
    
    def teardown_method(self):
        """Clean up after tests."""
        reset_integration()
    
    def test_complete_learning_flow(self):
        """Test a complete learning flow from start to finish."""
        user_id = "test_learner"
        topic = "variables"
        
        # 1. Start learning session
        session = self.integration.start_learning_session(topic, "beginner", user_id)
        assert session is not None
        
        # 2. Get step-by-step explanation for code
        code = "name = 'Alice'"
        explanation_result = self.integration.explain_code_with_examples(code, include_visuals=False)
        
        assert 'step_explanations' in explanation_result
        assert 'related_examples' in explanation_result
        assert 'concepts_covered' in explanation_result
        assert len(explanation_result['step_explanations']) > 0
        
        # 3. Get learning recommendations
        recommendations = self.integration.get_learning_recommendations(user_id, topic)
        
        assert 'next_topics' in recommendations
        assert 'recommended_examples' in recommendations
        assert 'progress_summary' in recommendations
    
    def test_learning_with_progress_tracking(self):
        """Test learning scenario with progress tracking."""
        user_id = "progress_user"
        
        # Start multiple learning sessions
        topics = ["variables", "lists", "functions"]
        
        for topic in topics:
            session = self.integration.start_learning_session(topic, "beginner", user_id)
            assert session is not None
            
            # Simulate completing the topic
            if self.integration.learning_system:
                self.integration.learning_system.track_progress(user_id, topic, True)
        
        # Get final recommendations
        recommendations = self.integration.get_learning_recommendations(user_id)
        
        if recommendations.get('progress_summary'):
            progress = recommendations['progress_summary']
            assert 'completed_topics' in progress
            assert len(progress['completed_topics']) <= len(topics)
    
    def test_code_explanation_with_examples(self):
        """Test comprehensive code explanation with examples."""
        # Test with different types of code
        test_codes = [
            "x = 5",
            "numbers = [1, 2, 3]",
            "def greet(name): return f'Hello, {name}!'",
            "for item in [1, 2, 3]: print(item)"
        ]
        
        for code in test_codes:
            result = self.integration.explain_code_with_examples(code, include_visuals=False)
            
            assert 'step_explanations' in result
            assert 'related_examples' in result
            assert 'concepts_covered' in result
            
            # Should have at least one explanation
            assert len(result['step_explanations']) > 0
            
            # Should identify some concepts
            assert len(result['concepts_covered']) > 0
    
    def test_documentation_generation_flow(self):
        """Test complete documentation generation flow."""
        if not self.integration.doc_generator:
            pytest.skip("Documentation generator not available")
        
        # Create a temporary module to document
        with tempfile.TemporaryDirectory() as temp_dir:
            test_module = os.path.join(temp_dir, "example_module.py")
            with open(test_module, 'w') as f:
                f.write('''
"""Example module for testing documentation generation."""

class ExampleClass:
    """An example class for demonstration."""
    
    def __init__(self, name: str):
        """Initialize with a name."""
        self.name = name
    
    def greet(self) -> str:
        """Return a greeting message."""
        return f"Hello, {self.name}!"

def example_function(x: int, y: int = 0) -> int:
    """Add two numbers together.
    
    Args:
        x: First number
        y: Second number (default 0)
        
    Returns:
        int: Sum of x and y
    """
    return x + y
''')
            
            try:
                # Generate comprehensive documentation
                result = self.integration.generate_comprehensive_documentation([test_module])
                
                assert 'sphinx_docs' in result
                assert 'visual_artifacts' in result
                assert 'publish_result' in result
                
                # Check that documentation was generated
                sphinx_docs = result['sphinx_docs']
                assert sphinx_docs.source_files
                assert 'index.rst' in sphinx_docs.source_files
                
            except Exception as e:
                # Documentation generation might fail in test environment
                pytest.skip(f"Documentation generation failed: {e}")


class TestErrorRecoveryIntegration:
    """Test error recovery and graceful degradation."""
    
    def setup_method(self):
        """Set up test environment."""
        reset_integration()
    
    def teardown_method(self):
        """Clean up after tests."""
        reset_integration()
    
    def test_graceful_degradation_on_component_failure(self):
        """Test that system degrades gracefully when components fail."""
        # Mock a component initialization failure
        with patch('fishertools.learning.TutorialEngine') as mock_tutorial:
            mock_tutorial.side_effect = Exception("Tutorial engine failed")
            
            # Should still initialize with minimal components
            integration = FishertoolsIntegration(project_name="test_project")
            
            # Should have basic learning system
            assert integration.learning_system is not None
            assert integration.example_repository is not None
    
    def test_error_recovery_in_learning_session(self):
        """Test error recovery during learning session creation."""
        integration = FishertoolsIntegration(project_name="test_project")
        
        # Mock session manager to fail
        if integration.session_manager:
            with patch.object(integration.session_manager, 'create_session') as mock_create:
                mock_create.side_effect = Exception("Session creation failed")
                
                # Should still create basic tutorial session
                session = integration.start_learning_session("variables", "beginner", "test_user")
                assert session is not None
    
    def test_error_recovery_in_documentation_generation(self):
        """Test error recovery during documentation generation."""
        integration = FishertoolsIntegration(project_name="test_project")
        
        if not integration.doc_generator:
            pytest.skip("Documentation generator not available")
        
        # Test with invalid module path
        try:
            result = integration.generate_comprehensive_documentation(["/nonexistent/module.py"])
            # Should either succeed with error handling or raise appropriate exception
            assert result is not None or True  # Either works or fails gracefully
        except Exception as e:
            # Should be a fishertools error, not a raw exception
            assert "fishertools" in str(type(e)).lower() or "documentation" in str(e).lower()
    
    def test_configuration_error_recovery(self):
        """Test recovery from configuration errors."""
        # Test with invalid configuration path
        integration = FishertoolsIntegration(
            config_path="/nonexistent/config.json",
            project_name="test_project"
        )
        
        # Should initialize with default configuration
        assert integration.config is not None
        assert integration.config_manager is not None
    
    def test_recovery_manager_integration(self):
        """Test that recovery manager is properly integrated."""
        integration = FishertoolsIntegration(project_name="test_project")
        
        assert integration.recovery_manager is not None
        
        # Test error statistics
        stats = integration.recovery_manager.get_error_statistics()
        assert 'total_errors' in stats
        assert 'error_counts_by_type' in stats


class TestSystemStatus:
    """Test system status and health checks."""
    
    def setup_method(self):
        """Set up test environment."""
        reset_integration()
        self.integration = FishertoolsIntegration(project_name="test_project")
    
    def teardown_method(self):
        """Clean up after tests."""
        reset_integration()
    
    def test_system_status_reporting(self):
        """Test that system status is properly reported."""
        status = self.integration.get_system_status()
        
        # Should have status for all components
        expected_components = [
            'learning_system',
            'documentation_generator',
            'example_repository',
            'visual_documentation',
            'configuration_manager'
        ]
        
        for component in expected_components:
            assert component in status
            assert status[component] in ['initialized', 'failed']
        
        # Should have configuration info
        assert 'current_config' in status
        assert 'total_examples' in status
    
    def test_global_integration_instance(self):
        """Test global integration instance management."""
        # Get global instance
        global_integration = get_integration(project_name="global_test")
        assert global_integration is not None
        
        # Should return same instance on subsequent calls
        same_integration = get_integration()
        assert same_integration is global_integration
        
        # Reset and get new instance
        reset_integration()
        new_integration = get_integration(project_name="new_test")
        assert new_integration is not global_integration
    
    def test_convenience_functions(self):
        """Test convenience functions work properly."""
        from fishertools.integration import start_learning, explain_code, get_recommendations
        
        # Test start_learning convenience function
        session = start_learning("variables", "beginner", "convenience_user")
        assert session is not None
        
        # Test explain_code convenience function
        result = explain_code("x = 42", include_visuals=False)
        assert 'step_explanations' in result
        
        # Test get_recommendations convenience function
        recommendations = get_recommendations("convenience_user", "variables")
        assert 'next_topics' in recommendations


class TestConfigurationIntegration:
    """Test configuration system integration."""
    
    def setup_method(self):
        """Set up test environment."""
        reset_integration()
    
    def teardown_method(self):
        """Clean up after tests."""
        reset_integration()
    
    def test_configuration_update_integration(self):
        """Test that configuration updates are applied to all components."""
        integration = FishertoolsIntegration(project_name="config_test")
        
        # Update configuration
        new_config = {
            'default_level': 'intermediate',
            'docs_output_dir': 'custom_docs'
        }
        
        try:
            integration.update_configuration(new_config)
            
            # Verify configuration was updated
            assert integration.config.default_level == 'intermediate'
            
            # Verify it was applied to components
            if integration.doc_generator:
                assert integration.doc_generator.output_dir == 'custom_docs'
                
        except Exception as e:
            # Configuration update might fail in test environment
            pytest.skip(f"Configuration update failed: {e}")
    
    def test_configuration_validation_integration(self):
        """Test configuration validation during integration."""
        # Test with invalid configuration
        invalid_config = {
            'default_level': 'invalid_level',
            'explanation_verbosity': 'invalid_verbosity'
        }
        
        integration = FishertoolsIntegration(project_name="validation_test")
        
        try:
            integration.update_configuration(invalid_config)
            # Should either succeed with validation or raise appropriate error
        except ValueError as e:
            # Expected for invalid configuration
            assert "configuration" in str(e).lower() or "invalid" in str(e).lower()
        except Exception as e:
            # Other exceptions should be handled gracefully
            assert integration.config is not None  # Should still have valid config


if __name__ == "__main__":
    pytest.main([__file__])