"""
Tests for @handler decorator with named event source parameters.

These tests verify the usage pattern:
@handler(input=queue_config, topic="sns:arn")
"""

import pytest
import tempfile
import os
from pathlib import Path
from unittest.mock import patch, MagicMock

from iicy.queue import SQSQueueConfig
from iicy.features.lambda_.lambda_config import handler, LambdaInfraConfig
from iicy.features.lambda_.lambda_feature import LambdaFeature
from iicy.models.context import DiscoveryStageContext


class TestHandlerWithNamedParams:
    """Test cases for @handler(input=...) usage pattern."""
    
    def test_handler_with_input_queue_config(self):
        """Test @handler(input=queue_config) creates SQS trigger."""
        input_queue = SQSQueueConfig(name="input-messages")
        
        @handler(input=input_queue)
        def process_input(event, context):
            return {"statusCode": 200, "body": "processed"}
        
        assert isinstance(process_input, LambdaInfraConfig)
        assert "input-messages" in process_input.sqs_triggers
        assert process_input.function_name == "process_input"
    
    
    def test_handler_with_sns_string_input(self):
        """Test @handler(input="sns:arn") creates SNS trigger."""
        topic_arn = "arn:aws:sns:us-east-1:123456789012:notifications"
        
        @handler(input=topic_arn, function_name="handle-notifications")
        def handle_sns(event, context):
            return {"statusCode": 200}
        
        assert isinstance(handle_sns, LambdaInfraConfig)
        assert topic_arn in handle_sns.sns_triggers
        assert len(handle_sns.sqs_triggers) == 0
    
    def test_handler_with_multiple_sns_inputs(self):
        """Test @handler(input=[...]) with multiple SNS topics."""
        topics = [
            "arn:aws:sns:us-east-1:123456789012:topic1",
            "arn:aws:sns:us-east-1:123456789012:topic2"
        ]
        
        @handler(input=topics)
        def handle_multiple_topics(event, context):
            return {"statusCode": 200}
        
        assert isinstance(handle_multiple_topics, LambdaInfraConfig)
        assert all(topic in handle_multiple_topics.sns_triggers for topic in topics)
    
    def test_handler_with_mixed_input_sources(self):
        """Test @handler with mixed SQS and SNS input sources."""
        input_queue = SQSQueueConfig(name="input-queue")
        topic_arn = "arn:aws:sns:us-east-1:123456789012:notifications"
        
        @handler(
            input=[input_queue, topic_arn],
            function_name="mixed-processor"
        )
        def mixed_processor(event, context):
            return {"statusCode": 200}
        
        assert isinstance(mixed_processor, LambdaInfraConfig)
        assert "input-queue" in mixed_processor.sqs_triggers
        assert topic_arn in mixed_processor.sns_triggers
        assert mixed_processor.function_name == "mixed-processor"
    
    def test_handler_with_string_queue_names(self):
        """Test @handler with string queue names instead of objects."""
        @handler(input="input-queue")
        def string_queues(event, context):
            return {"statusCode": 200}
        
        assert isinstance(string_queues, LambdaInfraConfig)
        assert "input-queue" in string_queues.sqs_triggers
    
    
    def test_handler_default_function_name(self):
        """Test that function name defaults to Python function name."""
        input_queue = SQSQueueConfig(name="test-queue")
        
        @handler(input=input_queue)
        def my_descriptive_function_name(event, context):
            return {"statusCode": 200}
        
        assert isinstance(my_descriptive_function_name, LambdaInfraConfig)
        assert my_descriptive_function_name.function_name == "my_descriptive_function_name"


class TestASTAnalysisForNamedParams:
    """Test AST analysis for the new named parameter usage."""
    
    def test_ast_analysis_detects_input_queue_config(self):
        """Test AST analysis detects input=queue_config."""
        test_code = '''
from iicy.queue import SQSQueueConfig
from iicy.features.lambda_.lambda_config import handler

input_queue = SQSQueueConfig(name="ast-input-queue")

@handler(input=input_queue)
def process_ast_input(event, context):
    return {"statusCode": 200}
'''
        
        with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
            f.write(test_code)
            temp_file_path = f.name
        
        try:
            lambda_feature = LambdaFeature()
            context = DiscoveryStageContext(file_path=Path(temp_file_path))
            
            configs = lambda_feature.discover(context)
            
            # Find our config
            config = None
            for c in configs:
                if hasattr(c, 'function_name') and c.function_name in ['process_ast_input', 'processAstInput']:
                    config = c
                    break
            
            assert config is not None
            assert isinstance(config, LambdaInfraConfig)
            assert "ast-input-queue" in config.sqs_triggers
        
        finally:
            os.unlink(temp_file_path)
    
    def test_ast_analysis_mixed_input_sources(self):
        """Test AST analysis with mixed SQS and SNS input sources."""
        test_code = '''
from iicy.queue import SQSQueueConfig
from iicy.features.lambda_.lambda_config import handler

input_queue = SQSQueueConfig(name="pipeline-input")
topic_arn = "arn:aws:sns:us-east-1:123456789012:pipeline-notifications"

@handler(
    input=[input_queue, topic_arn],
    function_name="ast-pipeline-processor"
)
def pipeline_processor(event, context):
    return {"statusCode": 200}
'''
        
        with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
            f.write(test_code)
            temp_file_path = f.name
        
        try:
            lambda_feature = LambdaFeature()
            context = DiscoveryStageContext(file_path=Path(temp_file_path))
            
            configs = lambda_feature.discover(context)
            
            config = None
            for c in configs:
                if hasattr(c, 'function_name') and c.function_name == "ast-pipeline-processor":
                    config = c
                    break
            
            assert config is not None
            assert "pipeline-input" in config.sqs_triggers
            assert "arn:aws:sns:us-east-1:123456789012:pipeline-notifications" in config.sns_triggers
        
        finally:
            os.unlink(temp_file_path)




@pytest.fixture
def temp_python_file():
    """Fixture to create temporary Python files for testing."""
    temp_files = []
    
    def create_temp_file(content: str) -> str:
        with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
            f.write(content)
            temp_files.append(f.name)
            return f.name
    
    yield create_temp_file
    
    # Cleanup
    for temp_file in temp_files:
        if os.path.exists(temp_file):
            os.unlink(temp_file)


class TestRealWorldScenarios:
    """Real-world usage scenarios for @handler decorator."""
    
    def test_message_processing_pipeline(self, temp_python_file):
        """Test a realistic message processing pipeline scenario."""
        
        test_code = '''
from iicy.queue import SQSQueueConfig
from iicy.features.lambda_.lambda_config import handler

# Define the message flow
raw_messages = SQSQueueConfig(name="raw-user-events")
validated_messages = SQSQueueConfig(name="validated-events")
failed_messages = SQSQueueConfig(name="failed-validations")

@handler(input=raw_messages)
def validate_events(event, context):
    """Validate incoming user events."""
    valid_count = 0
    for record in event.get('Records', []):
        # Validation logic here
        valid_count += 1
    
    return {"statusCode": 200, "validated": valid_count}

@handler(input=validated_messages)
def process_events(event, context):
    """Process validated events."""
    processed_count = 0
    for record in event.get('Records', []):
        # Processing logic here
        processed_count += 1
    
    return {"statusCode": 200, "processed": processed_count}

@handler(
    input=[failed_messages, "arn:aws:sns:us-east-1:123456789012:failure-alerts"]
)
def handle_failures(event, context):
    """Handle failed message processing."""
    return {"statusCode": 200, "alerts_sent": len(event.get('Records', []))}
'''
        
        temp_file_path = temp_python_file(test_code)
        
        # Test discovery
        lambda_feature = LambdaFeature()
        context = DiscoveryStageContext(file_path=Path(temp_file_path))
        
        configs = lambda_feature.discover(context)
        
        # Should find all three functions
        assert len(configs) >= 3
        
        validate_config = None
        process_config = None
        failure_config = None
        
        for config in configs:
            if hasattr(config, 'function_name'):
                # Handle both original and camelCase function names
                if config.function_name in ['validate_events', 'validateEvents']:
                    validate_config = config
                elif config.function_name in ['process_events', 'processEvents']:
                    process_config = config
                elif config.function_name in ['handle_failures', 'handleFailures']:
                    failure_config = config
        
        # Verify validate_events configuration
        assert validate_config is not None
        assert "raw-user-events" in validate_config.sqs_triggers
        
        # Verify process_events configuration
        assert process_config is not None
        assert "validated-events" in process_config.sqs_triggers
        
        # Verify handle_failures configuration
        assert failure_config is not None
        assert "failed-validations" in failure_config.sqs_triggers
        assert "arn:aws:sns:us-east-1:123456789012:failure-alerts" in failure_config.sns_triggers
