#!/usr/bin/env python3
"""
Performance Mode Test Runner
============================

Dedicated test runner for xwnode performance mode tests.
"""

import sys
import os
import time
import subprocess
import argparse
from pathlib import Path

def setup_environment():
    """Setup the Python environment for running tests."""
    # Use xwsystem utility for path setup
    try:
        from src.xlib.xwsystem.utils.paths import setup_python_path
        project_root, src_path = setup_python_path(__file__, levels_up=6)
        return project_root, src_path
    except ImportError:
        # Fallback to manual calculation if xwsystem not available
        current_file = Path(__file__).resolve()
        project_root = current_file.parent.parent.parent.parent.parent.parent.parent
        src_path = project_root / "src"
        
        # Add src to Python path
        src_path_str = str(src_path)
        if src_path_str not in sys.path:
            sys.path.insert(0, src_path_str)
        
        return project_root, src_path


def create_test_config_file(config_values):
    """Create a temporary test configuration file with the specified values."""
    config_content = f'''"""
Test Configuration (Auto-generated by runner)
============================================

This file is automatically generated by the runner with test size parameters.
"""
# Test data size configuration
TEST_USERS_COUNT = {config_values['users_count']}
TEST_DEEP_NESTING_LEVELS = {config_values['deep_levels']}
TEST_WIDE_STRUCTURE_KEYS = {config_values['wide_keys']}
TEST_LARGE_DATA_ITEMS = {config_values['large_items']}

# Benchmark operation counts
BENCHMARK_COMPREHENSIVE_OPS = {config_values['comprehensive_ops']}
BENCHMARK_MEMORY_OPS = {config_values['memory_ops']}
BENCHMARK_SPEED_OPS = {config_values['speed_ops']}
BENCHMARK_ADAPTIVE_OPS = {config_values['adaptive_ops']}
BENCHMARK_LEARNING_OPS = {config_values['learning_ops']}
'''
    
    config_file = Path(__file__).parent / "test_config.py"
    with open(config_file, 'w') as f:
        f.write(config_content)
    
    return config_file


# Setup environment
project_root, src_path = setup_environment()


def run_tests(config_values=None):
    """Run all performance mode tests."""
    
    if config_values is None:
        config_values = {
            'users_count': 2000,
            'deep_levels': 15,
            'wide_keys': 3000,
            'large_items': 5000,
            'comprehensive_ops': 500,
            'memory_ops': 500,
            'speed_ops': 500,
            'adaptive_ops': 500,
            'learning_ops': 500
        }
    
    # Create test configuration file
    config_file = create_test_config_file(config_values)
    
    print("🎯 Performance Mode Test Runner")
    print("=" * 50)
    print(f"📊 Test Configuration:")
    print(f"   Users: {config_values['users_count']:,}")
    print(f"   Deep Nesting: {config_values['deep_levels']} levels")
    print(f"   Wide Structure: {config_values['wide_keys']:,} keys")
    print(f"   Large Data: {config_values['large_items']:,} items")
    print(f"   Operations: {config_values['speed_ops']} (same for all tests - fair comparison)")
    print("=" * 50)
    
    # Get the test directory
    test_dir = Path(__file__).parent
    
    # List of test files to run
    test_files = [
        "test_adaptive_mode.py",
        "test_performance_benchmark.py"  # New benchmark test with detailed metrics
    ]
    
    results = {}
    total_tests = 0
    passed_tests = 0
    failed_tests = 0
    
    for idx, test_file in enumerate(test_files, 1):
        test_path = test_dir / test_file
        if not test_path.exists():
            print(f"❌ Test file not found: {test_file}")
            continue
        
        print(f"\n📋 Running {test_file} ({idx}/{len(test_files)})...")
        print("-" * 40)
        
        try:
            # Build PYTHONPATH
            python_path_parts = [str(src_path), str(project_root)]
            existing_pythonpath = os.environ.get('PYTHONPATH', '')
            if existing_pythonpath:
                python_path_parts.append(existing_pythonpath)
            python_path = os.pathsep.join(python_path_parts)
            
            # Run the test file with pytest
            cmd = [
                sys.executable, "-m", "pytest", 
                str(test_path), 
                "-v", "-s", "--tb=short"
            ]
            
                        # Add environment variables for better test control
            env = {
                **dict(os.environ),
                'PYTHONPATH': python_path,
                'PYTEST_TIMEOUT': '60',  # 60 second timeout per test
                'PYTEST_ISOLATION': '1'  # Enable test isolation
            }
            
            start_time = time.time()
            result = subprocess.run(cmd, capture_output=True, text=True, 
                                  timeout=120,  # 2 minutes per file (reduced from 5 minutes)
                                  cwd=str(project_root),
                                  env=env)
            end_time = time.time()
            
            # Parse results more accurately
            output_lines = result.stdout.split('\n')
            test_count = 0
            passed = 0
            failed = 0
            
            # Look for the summary line
            for line in output_lines:
                if "failed" in line and "passed" in line and "in" in line:
                    # Parse line like "1 failed, 8 passed in 1.38s"
                    parts = line.split()
                    for i, part in enumerate(parts):
                        if part == "failed,":
                            failed = int(parts[i-1])
                        elif part == "passed":
                            passed = int(parts[i-1])
                    test_count = failed + passed
                    break
                elif "passed in" in line and "failed" not in line:
                    # Parse line like "9 passed in 1.38s"
                    parts = line.split()
                    for i, part in enumerate(parts):
                        if part == "passed":
                            passed = int(parts[i-1])
                    test_count = passed
                    failed = 0
                    break
            
            # If we couldn't parse the summary, fall back to counting individual results
            if test_count == 0:
                for line in output_lines:
                    if "::" in line and ("PASSED" in line or "FAILED" in line):
                        test_count += 1
                        if "PASSED" in line:
                            passed += 1
                        elif "FAILED" in line:
                            failed += 1
            
            results[test_file] = {
                'return_code': result.returncode,
                'test_count': test_count,
                'passed': passed,
                'failed': failed,
                'duration': end_time - start_time,
                'output': result.stdout,
                'error': result.stderr
            }
            
            total_tests += test_count
            passed_tests += passed
            failed_tests += failed
            
            # Print summary
            if result.returncode == 0:
                print(f"✅ {test_file}: {passed}/{test_count} tests passed ({end_time - start_time:.2f}s)")
            else:
                print(f"❌ {test_file}: {failed}/{test_count} tests failed ({end_time - start_time:.2f}s)")
                if result.stderr:
                    print(f"Error: {result.stderr}")
            
        except subprocess.TimeoutExpired:
            print(f"⏰ {test_file}: Test timed out after 2 minutes")
            print("    This indicates tests are hanging. Check for:")
            print("    - Large data structures causing memory issues")
            print("    - Missing test isolation or cleanup")
            print("    - Infinite loops in test logic")
            results[test_file] = {'error': 'Timeout - tests hanging'}
            failed_tests += 1
        except Exception as e:
            print(f"💥 {test_file}: Error running tests: {e}")
            print(f"    Full error details: {type(e).__name__}: {str(e)}")
            results[test_file] = {'error': str(e)}
            failed_tests += 1
    
    # Print overall summary
    print("\n" + "=" * 50)
    print("📊 OVERALL TEST SUMMARY")
    print("=" * 50)
    print(f"Total Tests: {total_tests}")
    print(f"Passed: {passed_tests}")
    print(f"Failed: {failed_tests}")
    print(f"Success Rate: {(passed_tests/total_tests*100):.1f}%" if total_tests > 0 else "No tests run")
    
    # Print detailed results
    print("\n📋 DETAILED RESULTS")
    print("-" * 50)
    
    total_duration = 0
    for test_file, result in results.items():
        if 'error' in result:
            print(f"❌ {test_file}: {result['error']}")
        else:
            status = "✅ PASS" if result['return_code'] == 0 else "❌ FAIL"
            print(f"{status} {test_file}: {result['passed']}/{result['test_count']} passed "
                  f"({result['duration']:.2f}s)")
            total_duration += result.get('duration', 0)
    
    # Print performance summary
    print(f"\n⏱️  PERFORMANCE SUMMARY")
    print("-" * 50)
    print(f"Total execution time: {total_duration:.2f}s")
    if total_tests > 0:
        print(f"Average time per test: {total_duration/total_tests:.2f}s")
    
    # Return overall success
    return failed_tests == 0


def run_quick_test():
    """Run a quick test to verify basic functionality."""
    
    print("🚀 Quick Performance Mode Test")
    print("=" * 40)
    
    try:
        # Ensure environment is set up
        global project_root, src_path
        project_root, src_path = setup_environment()
        
        # Build PYTHONPATH
        python_path_parts = [str(src_path), str(project_root)]
        existing_pythonpath = os.environ.get('PYTHONPATH', '')
        if existing_pythonpath:
            python_path_parts.append(existing_pythonpath)
        python_path = os.pathsep.join(python_path_parts)
        
        # Run a simple test with pytest
        cmd = [
            sys.executable, "-m", "pytest", 
            "src/xlib/xwnode/tests/unit/perf_mode/test_adaptive_mode.py::TestAdaptiveMode::test_adaptive_mode_creation",
            "-v", "-s"
        ]
        
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=60,
                              cwd=str(project_root),
                              env={
                                  **dict(os.environ),
                                  'PYTHONPATH': python_path
                              })
        
        if result.returncode == 0:
            print("✅ Quick test completed successfully!")
            print("Output:")
            print(result.stdout)
            return True
        else:
            print("❌ Quick test failed!")
            print("Error:")
            print(result.stderr)
            return False
        
    except Exception as e:
        print(f"❌ Quick test failed: {e}")
        import traceback
        traceback.print_exc()
        return False


def run_benchmark_only(config_values=None):
    """Run only the performance benchmark tests with detailed output."""
    
    if config_values is None:
        config_values = {
            'users_count': 2000,
            'deep_levels': 15,
            'wide_keys': 3000,
            'large_items': 5000,
            'comprehensive_ops': 500,
            'memory_ops': 500,
            'speed_ops': 500,
            'adaptive_ops': 500,
            'learning_ops': 500
        }
    
    # Create test configuration file
    config_file = create_test_config_file(config_values)
    
    print("🚀 Performance Benchmark Only")
    print("=" * 50)
    print(f"📊 Test Configuration:")
    print(f"   Users: {config_values['users_count']:,}")
    print(f"   Deep Nesting: {config_values['deep_levels']} levels")
    print(f"   Wide Structure: {config_values['wide_keys']:,} keys")
    print(f"   Large Data: {config_values['large_items']:,} items")
    print(f"   Operations: {config_values['speed_ops']} (same for all tests - fair comparison)")
    print("=" * 50)
    
    # Setup environment
    project_root, src_path = setup_environment()
    
    # Build PYTHONPATH
    python_path_parts = [str(src_path), str(project_root)]
    existing_pythonpath = os.environ.get('PYTHONPATH', '')
    if existing_pythonpath:
        python_path_parts.append(existing_pythonpath)
    python_path = os.pathsep.join(python_path_parts)
    
    # Run benchmark test with verbose output
    test_path = "src/xlib/xnode/tests/unit/perf_mode/test_performance_benchmark.py"
    cmd = [
        sys.executable, "-m", "pytest", 
        test_path,
        "-v", "-s", "--tb=short"
    ]
    
    env = {
        **dict(os.environ),
        'PYTHONPATH': python_path,
        'PYTEST_TIMEOUT': '120'  # 2 minutes for benchmark
    }
    
    try:
        result = subprocess.run(cmd, capture_output=False, text=True, 
                              timeout=180,  # 3 minutes for benchmark
                              cwd=str(project_root),
                              env=env)
        return result.returncode == 0
    except subprocess.TimeoutExpired:
        print("⏰ Benchmark timed out after 3 minutes")
        return False
    except Exception as e:
        print(f"💥 Error running benchmark: {e}")
        return False


def parse_arguments():
    """Parse command line arguments for test configuration."""
    parser = argparse.ArgumentParser(description='Performance Mode Test Runner')
    
    # Test modes
    parser.add_argument('--quick', action='store_true', help='Run quick test only')
    parser.add_argument('--benchmark', action='store_true', help='Run benchmark tests only')
    
    # Data size configuration
    parser.add_argument('--users', type=int, default=2000, help='Number of users in test data (default: 2000)')
    parser.add_argument('--deep-levels', type=int, default=15, help='Deep nesting levels (default: 15)')
    parser.add_argument('--wide-keys', type=int, default=3000, help='Wide structure keys (default: 3000)')
    parser.add_argument('--large-items', type=int, default=5000, help='Large data items (default: 5000)')
    
    # Operation counts
    parser.add_argument('--comprehensive-ops', type=int, default=500, help='Comprehensive benchmark operations (default: 500)')
    parser.add_argument('--memory-ops', type=int, default=500, help='Memory efficiency operations (default: 500)')
    parser.add_argument('--speed-ops', type=int, default=500, help='Speed comparison operations (default: 500)')
    parser.add_argument('--adaptive-ops', type=int, default=500, help='Adaptive mode operations (default: 500)')
    parser.add_argument('--learning-ops', type=int, default=500, help='Learning metrics operations (default: 500)')
    
    # Presets
    parser.add_argument('--small', action='store_true', help='Use small test sizes (fast)')
    parser.add_argument('--medium', action='store_true', help='Use medium test sizes (balanced)')
    parser.add_argument('--large', action='store_true', help='Use large test sizes (thorough)')
    parser.add_argument('--extreme', action='store_true', help='Use extreme test sizes (stress test)')
    
    return parser.parse_args()


def get_config_from_args(args):
    """Get configuration values from command line arguments."""
    config = {
        'users_count': args.users,
        'deep_levels': args.deep_levels,
        'wide_keys': args.wide_keys,
        'large_items': args.large_items,
        'comprehensive_ops': args.comprehensive_ops,
        'memory_ops': args.memory_ops,
        'speed_ops': args.speed_ops,
        'adaptive_ops': args.adaptive_ops,
        'learning_ops': args.learning_ops
    }
    
    # Apply presets if specified
    if args.small:
        config.update({
            'users_count': 500,
            'deep_levels': 8,
            'wide_keys': 1000,
            'large_items': 2000,
            'comprehensive_ops': 20,
            'memory_ops': 50,
            'speed_ops': 75,
            'adaptive_ops': 100,
            'learning_ops': 100
        })
    elif args.medium:
        config.update({
            'users_count': 2000,
            'deep_levels': 15,
            'wide_keys': 3000,
            'large_items': 5000,
            'comprehensive_ops': 50,
            'memory_ops': 100,
            'speed_ops': 150,
            'adaptive_ops': 200,
            'learning_ops': 200
        })
    elif args.large:
        config.update({
            'users_count': 5000,
            'deep_levels': 20,
            'wide_keys': 8000,
            'large_items': 15000,
            'comprehensive_ops': 100,
            'memory_ops': 200,
            'speed_ops': 300,
            'adaptive_ops': 400,
            'learning_ops': 400
        })
    elif args.extreme:
        config.update({
            'users_count': 10000,
            'deep_levels': 25,
            'wide_keys': 15000,
            'large_items': 30000,
            'comprehensive_ops': 200,
            'memory_ops': 400,
            'speed_ops': 600,
            'adaptive_ops': 800,
            'learning_ops': 800
        })
    
    return config


def main():
    """Main entry point."""
    args = parse_arguments()
    config_values = get_config_from_args(args)
    
    if args.quick:
        success = run_quick_test()
    elif args.benchmark:
        success = run_benchmark_only(config_values)
    else:
        success = run_tests(config_values)
    
    sys.exit(0 if success else 1)


if __name__ == "__main__":
    main()
