"""
Integration tests for the complete text generation pipeline.
Tests how the core modules work together.
"""

import pytest
from unittest.mock import Mock, patch
import sys
import os

# Add src to path for importing modules
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src'))

from fixtures.sample_data import *


class TestTextGenerationPipeline:
    """Integration tests for the complete text generation workflow"""
    
    @pytest.mark.integration
    @patch('src.core.text_processor.wrap_text_to_width')
    @patch('src.core.text_fitting.find_optimal_font_size')
    @patch('src.core.text_fitting.calculate_text_position')
    def test_basic_text_generation_workflow(self, mock_calc_position, mock_find_size, mock_wrap_text):
        """Test the basic workflow from text input to positioned output"""
        # Setup mocks
        mock_wrap_text.return_value = ['Hello', 'World']
        mock_find_size.return_value = 24
        mock_calc_position.return_value = {
            'x': 50, 'y': 50, 
            'text_width': 100, 'text_height': 40
        }
        
        # Simulate a basic workflow
        text = "Hello\nWorld"
        font = Mock()
        canvas_width, canvas_height = 300, 200
        
        # Step 1: Normalize and wrap text
        from src.core.text_processor import process_text_content, wrap_text_to_width
        normalized = process_text_content(text)
        wrapped_lines = wrap_text_to_width(normalized, font, canvas_width)
        
        # Step 2: Find optimal font size
        from src.core.text_fitting import find_optimal_font_size
        optimal_size = find_optimal_font_size(text, "/fake/font.ttf", 200, 100)
        
        # Step 3: Calculate position
        from src.core.text_fitting import calculate_text_position
        available_area = {'x': 0, 'y': 0, 'width': canvas_width, 'height': canvas_height}
        position = calculate_text_position(text, font, available_area, 'center')
        
        # Verify workflow completed
        assert wrapped_lines == ['Hello', 'World']
        assert optimal_size == 24
        assert position['x'] == 50 and position['y'] == 50
        
        # Verify functions were called in sequence
        mock_wrap_text.assert_called_once_with(normalized, font, canvas_width)
        mock_find_size.assert_called_once()
        mock_calc_position.assert_called_once()

    @pytest.mark.integration
    @patch('src.core.text_fitting.check_text_overlap')
    @patch('src.core.text_fitting.resolve_text_conflicts')
    def test_overlap_detection_and_resolution_workflow(self, mock_resolve, mock_check):
        """Test the workflow for detecting and resolving text overlaps"""
        # Setup mocks
        mock_check.return_value = [(0, 1)]  # Positions 0 and 1 overlap
        mock_resolve.return_value = [
            {'x': 0, 'y': 0, 'width': 100, 'height': 50},
            {'x': 0, 'y': 60, 'width': 100, 'height': 50}  # Moved down
        ]
        
        # Initial overlapping positions
        positions = [
            {'x': 0, 'y': 0, 'width': 100, 'height': 50},
            {'x': 50, 'y': 25, 'width': 100, 'height': 50}  # Overlaps
        ]
        
        # Step 1: Check for overlaps
        from src.core.text_fitting import check_text_overlap
        overlaps = check_text_overlap(positions)
        
        # Step 2: Resolve conflicts
        from src.core.text_fitting import resolve_text_conflicts
        resolved = resolve_text_conflicts(positions, 500, 400)
        
        # Verify workflow
        assert len(overlaps) == 1
        assert len(resolved) == 2
        assert resolved[1]['y'] == 60  # Second position moved down
        
        mock_check.assert_called_once_with(positions)
        mock_resolve.assert_called_once()

    @pytest.mark.integration
    def test_text_metrics_and_fitting_integration(self):
        """Test integration between text metrics calculation and fitting"""
        with patch('src.core.text_processor.calculate_text_metrics') as mock_metrics, \
             patch('src.core.text_fitting.calculate_text_scaling') as mock_scaling:
            
            # Setup mocks
            mock_metrics.return_value = {
                'width': 200, 'height': 50, 
                'ascent': 40, 'descent': 10
            }
            mock_scaling.return_value = (1.5, 2.0, 1.5)  # width_scale, height_scale, uniform_scale
            
            # Test workflow
            font = Mock()
            text = "Sample Text"
            
            from src.core.text_processor import calculate_text_metrics
            from src.core.text_fitting import calculate_text_scaling
            
            # Get text metrics
            metrics = calculate_text_metrics(text, font)
            
            # Calculate scaling based on metrics
            scaling = calculate_text_scaling(text, font, 300, 100)
            
            # Verify integration
            assert metrics['width'] == 200
            assert metrics['height'] == 50
            assert scaling[2] == 1.5  # uniform_scale
            
            mock_metrics.assert_called_once_with(text, font)
            mock_scaling.assert_called_once()

    @pytest.mark.integration
    @patch('src.core.text_fitting.calculate_available_text_area')
    @patch('src.core.text_processor.optimize_text_layout')  
    def test_area_calculation_and_layout_optimization(self, mock_optimize, mock_calc_area):
        """Test integration between area calculation and layout optimization"""
        # Setup mocks
        mock_calc_area.return_value = {
            'x': 10, 'y': 10, 'width': 300, 'height': 200, 'area': 60000
        }
        mock_optimize.return_value = [
            {'x': 10, 'y': 10, 'width': 100, 'height': 50, 'text': 'Block 1'},
            {'x': 150, 'y': 10, 'width': 80, 'height': 40, 'text': 'Block 2'}
        ]
        
        # Test data
        canvas_width, canvas_height = 400, 300
        overlays = [{'x': 350, 'y': 250, 'width': 50, 'height': 50}]
        text_blocks = [
            {'x': 50, 'y': 50, 'width': 100, 'height': 50, 'text': 'Block 1'},
            {'x': 75, 'y': 75, 'width': 80, 'height': 40, 'text': 'Block 2'}  # Overlaps
        ]
        
        # Test workflow
        from src.core.text_fitting import calculate_available_text_area
        from src.core.text_processor import optimize_text_layout
        
        # Step 1: Calculate available area
        available_area = calculate_available_text_area(canvas_width, canvas_height, overlays)
        
        # Step 2: Optimize layout
        optimized_blocks = optimize_text_layout(text_blocks, canvas_width, canvas_height)
        
        # Verify integration
        assert available_area['width'] == 300
        assert available_area['area'] == 60000
        assert len(optimized_blocks) == 2
        assert optimized_blocks[0]['x'] == 10  # First block positioned in available area
        
        mock_calc_area.assert_called_once_with(canvas_width, canvas_height, overlays)
        mock_optimize.assert_called_once_with(text_blocks, canvas_width, canvas_height)


class TestErrorHandlingIntegration:
    """Integration tests for error handling across modules"""
    
    @pytest.mark.integration
    def test_graceful_degradation_on_font_errors(self):
        """Test that the system degrades gracefully when font operations fail"""
        with patch('PIL.ImageFont.truetype') as mock_truetype:
            mock_truetype.side_effect = OSError("Font not found")
            
            # Test that font size finding falls back gracefully
            from src.core.text_fitting import find_optimal_font_size
            result = find_optimal_font_size("Test", "/fake/font.ttf", 200, 100)
            
            assert result == 8  # Should fall back to minimum size

    @pytest.mark.integration  
    def test_error_handling_in_text_processing_chain(self):
        """Text wrapping should fail gracefully when PIL raises errors."""
        with patch('PIL.Image.new'), patch('PIL.ImageDraw.Draw') as mock_draw_class:
            mock_draw = Mock()
            mock_draw_class.return_value = mock_draw
            mock_draw.textbbox.side_effect = Exception("Text measurement failed")
            
            from src.core.text_processor import wrap_text_to_width
            result = wrap_text_to_width("Test text", Mock(), 200)
            
            assert result == ["Test text"]
