import unittest
from unittest.mock import MagicMock, patch
from semantica.pipeline.pipeline_builder import PipelineBuilder, StepStatus
from semantica.pipeline.execution_engine import ExecutionEngine, PipelineStatus

class TestPipelineModule(unittest.TestCase):

    def setUp(self):
        # Mock progress tracker
        self.mock_tracker_patcher = patch("semantica.utils.progress_tracker.get_progress_tracker")
        self.mock_get_tracker = self.mock_tracker_patcher.start()
        self.mock_tracker = MagicMock()
        self.mock_get_tracker.return_value = self.mock_tracker

    def tearDown(self):
        self.mock_tracker_patcher.stop()

    def test_pipeline_builder_basic(self):
        """Test building a simple pipeline."""
        builder = PipelineBuilder()
        builder.add_step("step1", "dummy")
        builder.add_step("step2", "dummy")
        
        # Connect step1 -> step2
        builder.connect_steps("step1", "step2")
        
        pipeline = builder.build("test_pipeline")
        
        self.assertEqual(pipeline.name, "test_pipeline")
        self.assertEqual(len(pipeline.steps), 2)
        
        step2 = next(s for s in pipeline.steps if s.name == "step2")
        self.assertIn("step1", step2.dependencies)

    def test_pipeline_builder_validation(self):
        """Test pipeline validation logic."""
        builder = PipelineBuilder()
        builder.add_step("step1", "dummy")
        
        # Try to connect to non-existent step
        with self.assertRaises(Exception): # ValidationError
            builder.connect_steps("step1", "non_existent")

    def test_execution_engine_success(self):
        """Test successful pipeline execution."""
        # Define handlers
        def step1_handler(data, **kwargs):
            return data + 1
            
        def step2_handler(data, **kwargs):
            return data * 2
            
        # Build pipeline
        builder = PipelineBuilder()
        builder.add_step("step1", "math", handler=step1_handler)
        builder.add_step("step2", "math", handler=step2_handler)
        builder.connect_steps("step1", "step2")
        
        pipeline = builder.build("math_pipeline")
        
        # Execute
        engine = ExecutionEngine()
        result = engine.execute_pipeline(pipeline, data=5)
        
        self.assertTrue(result.success)
        self.assertEqual(result.output, 12) # (5 + 1) * 2 = 12
        self.assertEqual(pipeline.steps[0].status, StepStatus.COMPLETED)

    def test_execution_engine_failure(self):
        """Test pipeline failure handling."""
        def failing_handler(data, **kwargs):
            raise ValueError("Something went wrong")
            
        builder = PipelineBuilder()
        builder.add_step("step1", "fail", handler=failing_handler)
        pipeline = builder.build("fail_pipeline")
        
        engine = ExecutionEngine()
        result = engine.execute_pipeline(pipeline, data=None)
        
        self.assertFalse(result.success)
        self.assertIn("Something went wrong", result.errors[0])
        self.assertEqual(pipeline.steps[0].status, StepStatus.FAILED)

    def test_topological_sort(self):
        """Test execution order respects dependencies."""
        execution_order = []
        
        def make_handler(name):
            def handler(data, **kwargs):
                execution_order.append(name)
                return data
            return handler
            
        builder = PipelineBuilder()
        builder.add_step("C", "type", handler=make_handler("C"))
        builder.add_step("B", "type", handler=make_handler("B"))
        builder.add_step("A", "type", handler=make_handler("A"))
        
        # Dependency: A -> B -> C
        builder.connect_steps("A", "B")
        builder.connect_steps("B", "C")
        
        pipeline = builder.build("ordered_pipeline")
        engine = ExecutionEngine()
        engine.execute_pipeline(pipeline)
        
        self.assertEqual(execution_order, ["A", "B", "C"])

    def test_imports_no_circular_dependencies(self):
        import semantica

        _ = semantica.pipeline

        from semantica.pipeline import PipelineBuilder, PipelineValidator
        from semantica.deduplication import DuplicateDetector

        builder = PipelineBuilder()
        builder.add_step("step1", "dummy")
        pipeline = builder.build("import_test_pipeline")

        validator = PipelineValidator()
        result = validator.validate_pipeline(pipeline)

        self.assertTrue(result.valid)

        detector = DuplicateDetector()
        self.assertIsNotNone(detector)

if __name__ == "__main__":
    unittest.main()
