"""
Comprehensive integration tests for PrecipGen Convenience API.

This module provides end-to-end integration testing with various scenarios,
cross-validation against existing PrecipGen analysis results, and performance
benchmarking to ensure the convenience API meets all requirements.
"""

import pytest
import time
import tempfile
import shutil
from pathlib import Path
from typing import Dict, Any, List, Optional
from unittest.mock import Mock, patch
import numpy as np

# Import convenience API components
from precipgen.convenience.api import ConvenienceAPI
from precipgen.convenience.batch_processor import BatchProcessor
from precipgen.convenience.config import (
    AnalysisConfig, BatchConfig, OutputConfig, StationRequirements,
    ValidationConfig, TemplateConfig
)
from precipgen.convenience.models import AnalysisResult, BatchResults
from precipgen.convenience.exceptions import (
    ConvenienceAPIError, StationDiscoveryError, WorkflowError
)
from precipgen.convenience.performance_optimizer import get_performance_optimizer

# Import core PrecipGen for cross-validation
from precipgen.engines.analytical import AnalyticalEngine
from precipgen.data.ghcn_downloader import GHCNDownloader
from precipgen.data.ghcn_parser import GHCNParser


class TestConvenienceAPIIntegration:
    """
    Integration tests for the complete ConvenienceAPI workflow.
    
    Tests the end-to-end functionality from location input to
    publication-ready results with various scenarios and configurations.
    """
    
    @pytest.fixture
    def temp_output_dir(self):
        """Create temporary output directory for tests."""
        temp_dir = tempfile.mkdtemp()
        yield temp_dir
        shutil.rmtree(temp_dir, ignore_errors=True)
    
    @pytest.fixture
    def api_instance(self):
        """Create ConvenienceAPI instance for testing."""
        return ConvenienceAPI()
    
    @pytest.fixture
    def test_locations(self):
        """Test locations with known good GHCN stations."""
        return [
            "Seattle, WA",  # USW00024233 - Sea-Tac Airport
            (47.4502, -122.3088),  # Seattle coordinates
            "Salt Lake City, UT",  # Test case from project context
            (40.7589, -111.8883),  # Salt Lake City coordinates
        ]
    
    @pytest.fixture
    def performance_config(self):
        """Configuration optimized for testing performance."""
        return AnalysisConfig(
            min_years_data=10,  # Reduced for faster testing
            validation_realizations=50,  # Reduced for faster testing
            parallel_execution=True,
            trend_window_years=20  # Reduced for faster testing
        )
    
    def test_fully_automated_workflow_text_location(self, api_instance, temp_output_dir):
        """Test fully automated workflow with text location input."""
        output_config = OutputConfig(
            base_output_dir=temp_output_dir,
            save_html_report=True,
            save_plots=True
        )
        
        # Test with reduced configuration for speed
        config = AnalysisConfig(
            min_years_data=10,
            validation_realizations=25,
            parallel_execution=False  # Avoid complexity in testing
        )
        
        start_time = time.time()
        
        try:
            result = api_instance.analyze_precipitation(
                "Seattle, WA",
                analysis_config=config,
                output_config=output_config
            )
            
            processing_time = time.time() - start_time
            
            # Validate result structure
            assert isinstance(result, AnalysisResult)
            assert result.success, f"Analysis failed: {result.error_message}"
            
            # Validate core components
            assert result.station_info is not None
            assert result.station_info.id is not None
            assert len(result.station_info.id) == 11  # GHCN station ID format
            
            assert result.parameter_manifest is not None
            assert result.validation_report is not None
            assert result.metadata is not None
            
            # Validate processing time is reasonable
            assert processing_time < 300, f"Processing took too long: {processing_time:.1f}s"
            
            # Validate output files if HTML report enabled
            if output_config.save_html_report and result.html_report:
                html_path = Path(result.html_report)
                assert html_path.exists(), "HTML report file not created"
                assert html_path.stat().st_size > 1000, "HTML report appears empty"
            
            print(f"✓ Fully automated workflow completed in {processing_time:.1f}s")
            print(f"  Station: {result.station_info.id} ({result.station_info.name})")
            print(f"  Data years: {result.station_info.data_years}")
            print(f"  Quality score: {result.station_info.data_quality_score:.2f}")
            
        except Exception as e:
            pytest.fail(f"Fully automated workflow failed: {str(e)}")
    
    def test_coordinate_based_analysis(self, api_instance, temp_output_dir):
        """Test analysis with coordinate input."""
        config = AnalysisConfig(
            min_years_data=10,
            validation_realizations=25,
            search_radius_km=100  # Larger radius for coordinate-based search
        )
        
        output_config = OutputConfig(base_output_dir=temp_output_dir)
        
        # Seattle coordinates
        coordinates = (47.6062, -122.3321)
        
        try:
            result = api_instance.analyze_precipitation(
                coordinates,
                analysis_config=config,
                output_config=output_config
            )
            
            assert result.success, f"Coordinate analysis failed: {result.error_message}"
            assert result.station_info.distance_km <= config.search_radius_km
            
            print(f"✓ Coordinate-based analysis successful")
            print(f"  Found station {result.station_info.id} at {result.station_info.distance_km:.1f}km")
            
        except Exception as e:
            pytest.fail(f"Coordinate-based analysis failed: {str(e)}")
    
    def test_station_discovery_workflow(self, api_instance):
        """Test semi-interactive workflow with station discovery."""
        try:
            # Discover stations
            requirements = StationRequirements(
                max_distance_km=100,
                min_years=15,
                min_completeness=0.7
            )
            
            candidates = api_instance.discover_stations(
                "Portland, OR",
                requirements=requirements
            )
            
            assert len(candidates) > 0, "No station candidates found"
            
            # Validate candidate structure
            for candidate in candidates[:3]:  # Check first 3
                assert candidate.id is not None
                assert candidate.name is not None
                assert candidate.distance_km <= requirements.max_distance_km
                assert candidate.data_years >= requirements.min_years
                assert candidate.data_completeness >= requirements.min_completeness
            
            # Use best candidate for analysis
            best_candidate = candidates[0]
            
            config = AnalysisConfig(
                min_years_data=10,
                validation_realizations=25
            )
            
            result = api_instance.analyze_precipitation(
                "Portland, OR",
                station_id=best_candidate.id,
                analysis_config=config
            )
            
            assert result.success, f"Station-specific analysis failed: {result.error_message}"
            assert result.station_info.id == best_candidate.id
            
            print(f"✓ Station discovery workflow successful")
            print(f"  Found {len(candidates)} candidates")
            print(f"  Used station {best_candidate.id} ({best_candidate.recommendation})")
            
        except Exception as e:
            pytest.fail(f"Station discovery workflow failed: {str(e)}")
    
    def test_expert_configuration_workflow(self, api_instance, temp_output_dir):
        """Test expert workflow with custom configuration."""
        # Expert-level configuration
        config = AnalysisConfig(
            min_years_data=20,
            search_radius_km=25,  # Strict radius
            wet_day_threshold=0.2,  # Custom threshold
            trend_window_years=30,
            validation_realizations=100,
            parallel_execution=True,
            max_workers=2
        )
        
        validation_config = ValidationConfig(
            n_realizations=100,
            parallel_execution=True,
            statistical_tests=['ks_test', 'anderson_darling']
        )
        
        output_config = OutputConfig(
            base_output_dir=temp_output_dir,
            save_html_report=True,
            save_plots=True
        )
        
        try:
            result = api_instance.analyze_precipitation(
                (40.7589, -111.8883),  # Salt Lake City coordinates
                analysis_config=config,
                output_config=output_config
            )
            
            assert result.success, f"Expert configuration failed: {result.error_message}"
            
            # Validate expert configuration was applied
            assert result.station_info.distance_km <= config.search_radius_km
            assert result.validation_report.n_realizations == validation_config.n_realizations
            
            print(f"✓ Expert configuration workflow successful")
            print(f"  Strict radius: {result.station_info.distance_km:.1f}km <= {config.search_radius_km}km")
            print(f"  Validation realizations: {result.validation_report.n_realizations}")
            
        except Exception as e:
            pytest.fail(f"Expert configuration workflow failed: {str(e)}")
    
    def test_trend_analysis_validation(self, api_instance):
        """Test trend analysis functionality and validation."""
        config = AnalysisConfig(
            min_years_data=25,  # Ensure sufficient data for trends
            trend_window_years=30,
            validation_realizations=50
        )
        
        try:
            result = api_instance.analyze_precipitation(
                "Seattle, WA",
                analysis_config=config
            )
            
            assert result.success, f"Trend analysis failed: {result.error_message}"
            
            # Validate trend analysis components
            if result.trend_analysis:
                assert result.trend_analysis.seasonal_slopes is not None
                assert result.trend_analysis.significance_tests is not None
                
                # Check that all seasons are analyzed
                expected_seasons = ['Winter', 'Spring', 'Summer', 'Fall']
                for season in expected_seasons:
                    assert season in result.trend_analysis.seasonal_slopes
                    assert season in result.trend_analysis.significance_tests
                
                # Check that all parameters are analyzed
                expected_params = ['pww', 'pwd', 'alpha', 'beta']
                for season_slopes in result.trend_analysis.seasonal_slopes.values():
                    for param in expected_params:
                        assert param in season_slopes
                
                print(f"✓ Trend analysis validation successful")
                print(f"  Trends detected: {result.has_trends}")
                if result.has_trends:
                    print(f"  Significant parameters: {result.trend_analysis.significant_parameters}")
            else:
                print("! Trend analysis not performed (insufficient data)")
            
        except Exception as e:
            pytest.fail(f"Trend analysis validation failed: {str(e)}")
    
    def test_validation_suite_comprehensive(self, api_instance):
        """Test comprehensive validation suite functionality."""
        config = AnalysisConfig(
            min_years_data=15,
            validation_realizations=75,  # Moderate for thorough testing
            parallel_execution=True
        )
        
        try:
            result = api_instance.analyze_precipitation(
                "Salt Lake City, UT",  # Use project context test case
                analysis_config=config
            )
            
            assert result.success, f"Validation suite test failed: {result.error_message}"
            assert result.validation_report is not None
            
            # Validate comprehensive validation components
            validation = result.validation_report
            
            # Check validation metrics exist
            assert validation.overall_performance is not None
            assert validation.validation_score is not None
            assert validation.seasonality_tests is not None
            assert validation.dry_spell_analysis is not None
            assert validation.intensity_comparisons is not None
            
            # Check validation score is reasonable
            assert 0 <= validation.validation_score <= 1, f"Invalid validation score: {validation.validation_score}"
            
            # Check performance categories
            valid_performances = ['excellent', 'good', 'acceptable', 'poor']
            assert validation.overall_performance in valid_performances
            
            print(f"✓ Comprehensive validation successful")
            print(f"  Overall performance: {validation.overall_performance}")
            print(f"  Validation score: {validation.validation_score:.3f}")
            print(f"  Passes validation: {validation.passes_validation}")
            
        except Exception as e:
            pytest.fail(f"Validation suite test failed: {str(e)}")
    
    def test_error_handling_and_fallbacks(self, api_instance):
        """Test error handling and fallback strategies."""
        # Test invalid location
        try:
            result = api_instance.analyze_precipitation("Invalid Location XYZ123")
            # Should either succeed with fallback or fail gracefully
            if not result.success:
                assert "geocoding" in result.error_message.lower() or "location" in result.error_message.lower()
                print("✓ Invalid location handled gracefully")
        except (ConvenienceAPIError, StationDiscoveryError) as e:
            print(f"✓ Invalid location raised appropriate exception: {type(e).__name__}")
        
        # Test invalid coordinates
        try:
            result = api_instance.analyze_precipitation((200, 200))  # Invalid coordinates
            if not result.success:
                assert "coordinate" in result.error_message.lower() or "latitude" in result.error_message.lower()
                print("✓ Invalid coordinates handled gracefully")
        except (ConvenienceAPIError, ValueError) as e:
            print(f"✓ Invalid coordinates raised appropriate exception: {type(e).__name__}")
        
        # Test configuration with impossible requirements
        impossible_config = AnalysisConfig(
            min_years_data=200,  # Truly impossible requirement (no station has 200+ years)
            search_radius_km=0.1   # Extremely small radius (100 meters)
        )
        
        try:
            result = api_instance.analyze_precipitation(
                "Seattle, WA",
                analysis_config=impossible_config
            )
            
            if not result.success:
                # Should fail gracefully with informative error
                assert len(result.error_message) > 10
                print("✓ Impossible configuration handled gracefully")
            else:
                # If it succeeds, fallbacks were used
                assert len(result.metadata.fallbacks_used) > 0
                print("✓ Impossible configuration handled with fallbacks")
                
        except ConvenienceAPIError as e:
            print(f"✓ Impossible configuration raised appropriate exception: {type(e).__name__}")


class TestBatchProcessingIntegration:
    """
    Integration tests for batch processing functionality.
    
    Tests multiple location analysis, error handling, summary generation,
    and performance optimization for batch operations.
    """
    
    @pytest.fixture
    def batch_processor(self):
        """Create BatchProcessor instance for testing."""
        api = ConvenienceAPI()
        return BatchProcessor(convenience_api=api)
    
    @pytest.fixture
    def test_batch_locations(self):
        """Test locations for batch processing."""
        return [
            "Seattle, WA",
            "Portland, OR",
            (40.7589, -111.8883),  # Salt Lake City
            "Denver, CO"
        ]
    
    @pytest.fixture
    def temp_batch_dir(self):
        """Create temporary directory for batch outputs."""
        temp_dir = tempfile.mkdtemp()
        yield temp_dir
        shutil.rmtree(temp_dir, ignore_errors=True)
    
    def test_batch_processing_basic(self, batch_processor, test_batch_locations, temp_batch_dir):
        """Test basic batch processing functionality."""
        batch_config = BatchConfig(
            parallel_execution=False,  # Sequential for testing
            continue_on_error=True,
            generate_summary_report=True,
            save_intermediate_results=True
        )
        
        analysis_config = AnalysisConfig(
            min_years_data=10,
            validation_realizations=25,  # Reduced for speed
            parallel_execution=False
        )
        
        output_config = OutputConfig(
            base_output_dir=temp_batch_dir,
            save_html_report=True
        )
        
        start_time = time.time()
        
        try:
            results = batch_processor.analyze_multiple_locations(
                test_batch_locations,
                analysis_config=analysis_config,
                batch_config=batch_config,
                output_config=output_config
            )
            
            processing_time = time.time() - start_time
            
            # Validate batch results structure
            assert isinstance(results, BatchResults)
            assert results.batch_summary is not None
            
            # Check success rate
            total_locations = len(test_batch_locations)
            successful_count = len(results.successful_results)
            failed_count = len(results.failed_locations)
            
            assert successful_count + failed_count == total_locations
            assert results.success_rate == (successful_count / total_locations * 100)
            
            # Validate successful results
            for result in results.successful_results:
                assert isinstance(result, AnalysisResult)
                assert result.success
                assert result.station_info is not None
            
            # Validate batch summary
            summary = results.batch_summary
            assert summary.total_locations == total_locations
            assert summary.successful_analyses == successful_count
            assert summary.failed_analyses == failed_count
            
            print(f"✓ Batch processing completed in {processing_time:.1f}s")
            print(f"  Success rate: {results.success_rate:.1f}% ({successful_count}/{total_locations})")
            print(f"  Processing metadata: {len(results.processing_metadata)} items")
            
            if results.failed_locations:
                print(f"  Failed locations:")
                for location, error in results.failed_locations:
                    print(f"    {location}: {type(error).__name__}")
            
        except Exception as e:
            pytest.fail(f"Batch processing failed: {str(e)}")
    
    def test_batch_processing_parallel(self, batch_processor, test_batch_locations, temp_batch_dir):
        """Test parallel batch processing."""
        batch_config = BatchConfig(
            parallel_execution=True,
            max_workers=2,  # Limited for testing
            continue_on_error=True,
            parallelism_strategy="by_location"
        )
        
        analysis_config = AnalysisConfig(
            min_years_data=10,
            validation_realizations=25,
            parallel_execution=False  # Avoid nested parallelism
        )
        
        output_config = OutputConfig(base_output_dir=temp_batch_dir)
        
        start_time = time.time()
        
        try:
            results = batch_processor.analyze_multiple_locations(
                test_batch_locations,
                analysis_config=analysis_config,
                batch_config=batch_config,
                output_config=output_config
            )
            
            processing_time = time.time() - start_time
            
            assert isinstance(results, BatchResults)
            assert len(results.successful_results) + len(results.failed_locations) == len(test_batch_locations)
            
            print(f"✓ Parallel batch processing completed in {processing_time:.1f}s")
            print(f"  Success rate: {results.success_rate:.1f}%")
            
        except Exception as e:
            pytest.fail(f"Parallel batch processing failed: {str(e)}")
    
    def test_batch_error_handling(self, batch_processor, temp_batch_dir):
        """Test batch processing error handling with mixed valid/invalid locations."""
        # Mix of valid and invalid locations
        mixed_locations = [
            "Seattle, WA",  # Valid
            "Invalid Location XYZ123",  # Invalid
            (47.6062, -122.3321),  # Valid coordinates
            (200, 200),  # Invalid coordinates
            "Portland, OR"  # Valid
        ]
        
        batch_config = BatchConfig(
            continue_on_error=True,
            parallel_execution=False,
            generate_summary_report=True
        )
        
        analysis_config = AnalysisConfig(
            min_years_data=10,
            validation_realizations=25
        )
        
        output_config = OutputConfig(base_output_dir=temp_batch_dir)
        
        try:
            results = batch_processor.analyze_multiple_locations(
                mixed_locations,
                analysis_config=analysis_config,
                batch_config=batch_config,
                output_config=output_config
            )
            
            # Should have some successes and some failures
            assert len(results.successful_results) > 0, "No successful results with mixed locations"
            assert len(results.failed_locations) > 0, "No failed results with invalid locations"
            
            # Validate error information
            for location, error in results.failed_locations:
                assert isinstance(error, Exception)
                assert len(str(error)) > 0
            
            print(f"✓ Batch error handling successful")
            print(f"  Successful: {len(results.successful_results)}")
            print(f"  Failed: {len(results.failed_locations)}")
            
        except Exception as e:
            pytest.fail(f"Batch error handling test failed: {str(e)}")
    
    def test_batch_summary_generation(self, batch_processor, test_batch_locations, temp_batch_dir):
        """Test batch summary and index report generation."""
        batch_config = BatchConfig(
            generate_summary_report=True,
            generate_index_html=True,
            parallel_execution=False
        )
        
        analysis_config = AnalysisConfig(
            min_years_data=10,
            validation_realizations=25
        )
        
        output_config = OutputConfig(
            base_output_dir=temp_batch_dir,
            save_html_report=True
        )
        
        try:
            results = batch_processor.analyze_multiple_locations(
                test_batch_locations,
                analysis_config=analysis_config,
                batch_config=batch_config,
                output_config=output_config
            )
            
            # Validate batch summary
            assert results.batch_summary is not None
            summary = results.batch_summary
            
            assert summary.total_locations == len(test_batch_locations)
            assert summary.recommendations is not None
            assert len(summary.recommendations) > 0
            
            # Validate index report
            if results.index_report_path:
                index_path = Path(results.index_report_path)
                assert index_path.exists(), "Index report file not created"
                assert index_path.stat().st_size > 1000, "Index report appears empty"
                
                # Check HTML content
                with open(index_path, 'r', encoding='utf-8') as f:
                    html_content = f.read()
                    assert "PrecipGen Batch Analysis Results" in html_content
                    assert "Summary Statistics" in html_content
            
            print(f"✓ Batch summary generation successful")
            print(f"  Summary recommendations: {len(summary.recommendations)}")
            if results.index_report_path:
                print(f"  Index report: {results.index_report_path}")
            
        except Exception as e:
            pytest.fail(f"Batch summary generation failed: {str(e)}")


class TestPerformanceOptimization:
    """
    Performance optimization and benchmarking tests.
    
    Tests caching effectiveness, memory management, parallel processing
    optimization, and overall performance characteristics.
    """
    
    @pytest.fixture
    def performance_optimizer(self):
        """Get performance optimizer for testing."""
        return get_performance_optimizer()
    
    def test_caching_effectiveness(self, performance_optimizer):
        """Test caching system effectiveness."""
        cache = performance_optimizer.cache
        
        # Clear cache for clean test
        cache.clear()
        
        # Test cache miss and hit
        test_key = "test_operation"
        test_value = {"data": "test_data", "timestamp": time.time()}
        
        # First access should be miss
        result = cache.get(test_key)
        assert result is None
        
        # Put value in cache
        cache.put(test_key, test_value)
        
        # Second access should be hit
        result = cache.get(test_key)
        assert result == test_value
        
        # Check cache stats
        stats = cache.get_stats()
        assert stats['hits'] >= 1
        assert stats['misses'] >= 1
        assert stats['size'] >= 1
        
        print(f"✓ Caching effectiveness test passed")
        print(f"  Cache stats: {stats}")
    
    def test_memory_management(self, performance_optimizer):
        """Test memory management functionality."""
        memory_manager = performance_optimizer.memory_manager
        
        # Get initial memory usage
        initial_memory = memory_manager.get_memory_usage()
        assert 'rss_mb' in initial_memory
        assert 'percent' in initial_memory
        
        # Test memory monitoring
        with memory_manager.memory_monitor("test_operation"):
            # Simulate some memory usage
            large_data = [i for i in range(100000)]
            time.sleep(0.1)  # Brief pause
        
        # Check memory snapshots
        assert len(memory_manager.memory_snapshots) > 0
        latest_snapshot = memory_manager.memory_snapshots[-1]
        assert latest_snapshot['operation'] == "test_operation"
        assert 'memory_delta_mb' in latest_snapshot
        
        # Test memory optimization
        optimization_result = memory_manager.optimize_memory()
        assert 'freed_memory_mb' in optimization_result
        assert 'objects_collected' in optimization_result
        
        print(f"✓ Memory management test passed")
        print(f"  Initial memory: {initial_memory['rss_mb']:.1f}MB")
        print(f"  Optimization result: {optimization_result}")
    
    def test_parallel_processing_optimization(self, performance_optimizer):
        """Test parallel processing optimization."""
        parallel_optimizer = performance_optimizer.parallel_optimizer
        
        # Test worker count optimization
        from precipgen.convenience.config import ParallelConfig
        
        config = ParallelConfig(max_workers=4)
        
        # Test different task types
        cpu_workers = parallel_optimizer.optimize_worker_count('cpu_bound', 100.0, config)
        io_workers = parallel_optimizer.optimize_worker_count('io_bound', 100.0, config)
        mixed_workers = parallel_optimizer.optimize_worker_count('mixed', 100.0, config)
        
        assert cpu_workers >= 1
        assert io_workers >= 1
        assert mixed_workers >= 1
        
        # Test executor type selection
        cpu_executor_type = parallel_optimizer.choose_executor_type('cpu_bound', 100.0, 4)
        io_executor_type = parallel_optimizer.choose_executor_type('io_bound', 100.0, 4)
        
        assert cpu_executor_type in ['thread', 'process']
        assert io_executor_type in ['thread', 'process']
        
        print(f"✓ Parallel processing optimization test passed")
        print(f"  CPU workers: {cpu_workers}, IO workers: {io_workers}, Mixed workers: {mixed_workers}")
        print(f"  CPU executor: {cpu_executor_type}, IO executor: {io_executor_type}")
    
    def test_performance_profiling(self, performance_optimizer):
        """Test performance profiling functionality."""
        profiler = performance_optimizer.profiler
        
        # Test operation profiling
        with profiler.profile_operation("test_operation", test_metadata="test_value"):
            time.sleep(0.1)  # Simulate work
        
        # Check profiling results
        summary = profiler.get_performance_summary()
        assert summary['operations_profiled'] >= 1
        assert summary['total_executions'] >= 1
        assert 'test_operation' in summary['operation_summaries']
        
        # Test bottleneck identification
        bottlenecks = profiler.identify_bottlenecks()
        assert isinstance(bottlenecks, list)
        
        # Generate optimization report
        report = profiler.generate_optimization_report()
        assert 'performance_summary' in report
        assert 'bottlenecks' in report
        assert 'recommendations' in report
        
        print(f"✓ Performance profiling test passed")
        print(f"  Operations profiled: {summary['operations_profiled']}")
        print(f"  Bottlenecks identified: {len(bottlenecks)}")
    
    def test_end_to_end_performance_benchmark(self, performance_optimizer):
        """Test end-to-end performance with optimization enabled."""
        # Create API with performance optimization
        api = ConvenienceAPI()
        
        # Configure for performance testing
        config = AnalysisConfig(
            min_years_data=10,
            validation_realizations=25,
            parallel_execution=True,
            max_workers=2
        )
        
        # Benchmark multiple operations
        locations = ["Seattle, WA", "Portland, OR"]
        execution_times = []
        
        for location in locations:
            start_time = time.time()
            
            try:
                result = api.analyze_precipitation(location, analysis_config=config)
                execution_time = time.time() - start_time
                execution_times.append(execution_time)
                
                assert result.success, f"Performance benchmark failed for {location}"
                
            except Exception as e:
                pytest.fail(f"Performance benchmark failed for {location}: {str(e)}")
        
        # Analyze performance
        avg_time = sum(execution_times) / len(execution_times)
        max_time = max(execution_times)
        
        # Performance assertions (reasonable thresholds)
        assert avg_time < 180, f"Average execution time too high: {avg_time:.1f}s"
        assert max_time < 300, f"Maximum execution time too high: {max_time:.1f}s"
        
        # Get optimization status
        optimization_status = performance_optimizer.get_optimization_status()
        
        print(f"✓ End-to-end performance benchmark passed")
        print(f"  Average execution time: {avg_time:.1f}s")
        print(f"  Maximum execution time: {max_time:.1f}s")
        print(f"  Cache hit rate: {optimization_status['cache_stats']['hit_rate_percent']:.1f}%")


class TestCrossValidation:
    """
    Cross-validation tests against existing PrecipGen analysis results.
    
    Validates that the convenience API produces results consistent with
    the core PrecipGen library when using equivalent configurations.
    """
    
    @pytest.fixture
    def reference_station_id(self):
        """Known good station ID for cross-validation."""
        return "USW00024233"  # Seattle Sea-Tac Airport
    
    def test_parameter_consistency_validation(self, reference_station_id):
        """Test parameter consistency between convenience API and core library."""
        # This test would require actual GHCN data and core library comparison
        # For now, we'll test the structure and basic validation
        
        api = ConvenienceAPI()
        
        config = AnalysisConfig(
            min_years_data=15,
            validation_realizations=50,
            trend_window_years=25
        )
        
        try:
            result = api.analyze_precipitation(
                "Seattle, WA",
                station_id=reference_station_id,
                analysis_config=config
            )
            
            assert result.success, f"Cross-validation analysis failed: {result.error_message}"
            
            # Validate parameter manifest structure
            manifest = result.parameter_manifest
            assert manifest is not None
            
            # Check that monthly parameters exist
            assert hasattr(manifest, 'monthly_parameters')
            
            # Validate parameter ranges (basic sanity checks)
            if hasattr(manifest, 'monthly_parameters') and manifest.monthly_parameters:
                for month, params in manifest.monthly_parameters.items():
                    # P(W|W) should be between 0 and 1
                    if hasattr(params, 'pww'):
                        assert 0 <= params.pww <= 1, f"Invalid P(W|W) for month {month}: {params.pww}"
                    
                    # P(W|D) should be between 0 and 1
                    if hasattr(params, 'pwd'):
                        assert 0 <= params.pwd <= 1, f"Invalid P(W|D) for month {month}: {params.pwd}"
                    
                    # Alpha should be positive
                    if hasattr(params, 'alpha'):
                        assert params.alpha > 0, f"Invalid alpha for month {month}: {params.alpha}"
                    
                    # Beta should be positive
                    if hasattr(params, 'beta'):
                        assert params.beta > 0, f"Invalid beta for month {month}: {params.beta}"
            
            print(f"✓ Parameter consistency validation passed")
            print(f"  Station: {result.station_info.id}")
            print(f"  Data years: {result.station_info.data_years}")
            
        except Exception as e:
            pytest.fail(f"Parameter consistency validation failed: {str(e)}")
    
    def test_validation_metrics_consistency(self, reference_station_id):
        """Test validation metrics consistency and reasonableness."""
        api = ConvenienceAPI()
        
        config = AnalysisConfig(
            min_years_data=15,
            validation_realizations=75,
            parallel_execution=True
        )
        
        try:
            result = api.analyze_precipitation(
                "Seattle, WA",
                station_id=reference_station_id,
                analysis_config=config
            )
            
            assert result.success, f"Validation metrics test failed: {result.error_message}"
            
            validation = result.validation_report
            assert validation is not None
            
            # Validate metric ranges
            assert 0 <= validation.validation_score <= 1, f"Invalid validation score: {validation.validation_score}"
            
            # Check that validation components exist
            assert validation.seasonality_tests is not None
            assert validation.dry_spell_analysis is not None
            assert validation.intensity_comparisons is not None
            
            # Validate performance category
            valid_performances = ['excellent', 'good', 'acceptable', 'poor']
            assert validation.overall_performance in valid_performances
            
            print(f"✓ Validation metrics consistency passed")
            print(f"  Validation score: {validation.validation_score:.3f}")
            print(f"  Overall performance: {validation.overall_performance}")
            print(f"  Passes validation: {validation.passes_validation}")
            
        except Exception as e:
            pytest.fail(f"Validation metrics consistency test failed: {str(e)}")


# Performance benchmark utilities
def run_performance_benchmark():
    """Run comprehensive performance benchmark."""
    print("Running PrecipGen Convenience API Performance Benchmark...")
    
    # Initialize components
    api = ConvenienceAPI()
    optimizer = get_performance_optimizer()
    
    # Test locations
    test_locations = [
        "Seattle, WA",
        "Portland, OR", 
        "Salt Lake City, UT",
        "Denver, CO"
    ]
    
    # Performance configuration
    config = AnalysisConfig(
        min_years_data=15,
        validation_realizations=50,
        parallel_execution=True,
        max_workers=4
    )
    
    # Run benchmark
    start_time = time.time()
    results = []
    
    for location in test_locations:
        location_start = time.time()
        
        try:
            result = api.analyze_precipitation(location, analysis_config=config)
            location_time = time.time() - location_start
            
            results.append({
                'location': location,
                'success': result.success,
                'time': location_time,
                'station_id': result.station_info.id if result.success else None
            })
            
            print(f"  {location}: {location_time:.1f}s ({'✓' if result.success else '✗'})")
            
        except Exception as e:
            location_time = time.time() - location_start
            results.append({
                'location': location,
                'success': False,
                'time': location_time,
                'error': str(e)
            })
            print(f"  {location}: {location_time:.1f}s (✗ {type(e).__name__})")
    
    total_time = time.time() - start_time
    successful_results = [r for r in results if r['success']]
    
    # Performance summary
    print(f"\nBenchmark Summary:")
    print(f"  Total time: {total_time:.1f}s")
    print(f"  Success rate: {len(successful_results)}/{len(test_locations)} ({len(successful_results)/len(test_locations)*100:.1f}%)")
    
    if successful_results:
        avg_time = sum(r['time'] for r in successful_results) / len(successful_results)
        print(f"  Average time per location: {avg_time:.1f}s")
    
    # Optimization status
    opt_status = optimizer.get_optimization_status()
    print(f"  Cache hit rate: {opt_status['cache_stats']['hit_rate_percent']:.1f}%")
    print(f"  Memory usage: {opt_status['memory_report']['current_usage']['rss_mb']:.1f}MB")
    
    return results


if __name__ == "__main__":
    # Run performance benchmark when executed directly
    run_performance_benchmark()