#!/usr/bin/env python3
"""
Fair Python Performance Comparison

This benchmark provides the most accurate comparison possible between Python's
pathlib.Path.resolve(strict=False) and Rust soft_canonicalize by:

1. Minimizing Python loop overhead 
2. Using efficient Python patterns (list comprehensions)
3. Measuring individual operation costs
4. Isolating the core path resolution logic

This gives a true apples-to-apples comparison of the core algorithms.
"""

import statistics
import tempfile
import time
from pathlib import Path
import timeit

def create_test_structure():
    """Create the same test structure for fair comparison"""
    temp_dir = tempfile.mkdtemp()
    temp_path = Path(temp_dir)
    
    # Create directory structure
    (temp_path / "existing" / "nested" / "deep").mkdir(parents=True, exist_ok=True)
    (temp_path / "symlinks").mkdir(parents=True, exist_ok=True)
    
    # Create test files
    (temp_path / "existing" / "file1.txt").write_text("test")
    (temp_path / "existing" / "nested" / "file2.txt").write_text("test")
    
    return temp_path

def micro_benchmark_single_operations(temp_dir):
    """
    Micro-benchmark individual resolve operations to eliminate loop overhead.
    This gives us the purest measurement of pathlib.Path.resolve() performance.
    """
    print("🔬 Micro-benchmark: Individual Operations")
    print("=" * 45)
    
    test_cases = [
        ("existing_file", temp_dir / "existing" / "file1.txt"),
        ("existing_nested", temp_dir / "existing" / "nested" / "file2.txt"),
        ("non_existing", temp_dir / "nonexistent" / "file.txt"),
        ("with_dotdot", temp_dir / "existing" / ".." / "existing" / "file1.txt"),
        ("complex_path", temp_dir / "existing" / "." / "nested" / ".." / "file1.txt"),
        ("deep_nonexistent", temp_dir / "very" / "deeply" / "nested" / "nonexistent" / "path.txt"),
    ]
    
    individual_times = {}
    
    for case_name, path in test_cases:
        # Use timeit for the most accurate single-operation timing
        # This minimizes Python overhead and measures just the resolve() call
        def resolve_operation():
            return path.resolve(strict=False)
        
        # Warmup
        for _ in range(10):
            resolve_operation()
        
        # Micro-benchmark with timeit (most accurate for small operations)
        time_per_call = timeit.timeit(resolve_operation, number=1000) / 1000
        individual_times[case_name] = time_per_call
        
        throughput = 1.0 / time_per_call
        print(f"{case_name:<15}: {throughput:>8.0f} ops/s ({time_per_call*1000000:>6.1f} μs/op)")
    
    return individual_times

def optimized_bulk_benchmark(temp_dir):
    """
    Optimized bulk operations using Python's most efficient patterns.
    This reduces loop overhead while still measuring realistic workloads.
    """
    print("\n🚀 Optimized Bulk Operations")
    print("=" * 30)
    
    # Test paths for bulk operations
    test_paths = [
        temp_dir / "existing" / "file1.txt",
        temp_dir / "existing" / "nested" / "file2.txt", 
        temp_dir / "nonexistent" / "file.txt",
        temp_dir / "existing" / ".." / "existing" / "file1.txt",
        temp_dir / "existing" / "." / "nested" / ".." / "file1.txt",
        temp_dir / "symlinks" / ".." / "existing" / "nested" / "deep" / ".." / ".." / "file1.txt",
        temp_dir / "very" / "deeply" / "nested" / "nonexistent" / "path" / "file.txt",
        temp_dir / "existing" / "nested" / ".." / ".." / "existing" / "nested" / "file2.txt",
    ]
    
    # Method 1: List comprehension (most efficient Python pattern)
    def resolve_list_comprehension():
        return [p.resolve(strict=False) for p in test_paths]
    
    # Method 2: Map function (functional approach)
    def resolve_map():
        return list(map(lambda p: p.resolve(strict=False), test_paths))
    
    # Method 3: Traditional loop (for comparison)
    def resolve_loop():
        results = []
        for path in test_paths:
            results.append(path.resolve(strict=False))
        return results
    
    methods = [
        ("List Comprehension", resolve_list_comprehension),
        ("Map Function", resolve_map),
        ("Traditional Loop", resolve_loop),
    ]
    
    iterations = 1000
    best_throughput = 0
    
    for method_name, method_func in methods:
        # Warmup
        for _ in range(10):
            method_func()
        
        # Benchmark
        start = time.perf_counter()
        for _ in range(iterations):
            method_func()
        elapsed = time.perf_counter() - start
        
        total_operations = len(test_paths) * iterations
        throughput = total_operations / elapsed
        best_throughput = max(best_throughput, throughput)
        
        print(f"{method_name:<18}: {throughput:>8.0f} ops/s")
    
    return best_throughput

def realistic_workload_benchmark(temp_dir):
    """
    Realistic workload that simulates actual usage patterns.
    This balances measurement accuracy with real-world scenarios.
    """
    print("\n💼 Realistic Workload Simulation")
    print("=" * 35)
    
    # Simulate different workload patterns
    workloads = {
        "File System Walker": [
            temp_dir / "existing" / "file1.txt",
            temp_dir / "existing" / "nested" / "file2.txt",
            temp_dir / "existing" / "nested" / "deep",
        ] * 10,  # Simulate walking through existing structure
        
        "Build Tool": [
            temp_dir / "src" / ".." / "build" / "output.o",
            temp_dir / "src" / "main.c",
            temp_dir / "include" / ".." / "src" / "header.h",
        ] * 15,  # Simulate relative path resolution in builds
        
        "Config Resolver": [
            temp_dir / "config" / ".." / "config" / "app.json",
            temp_dir / "config" / "." / "database.json", 
            temp_dir / "config" / "cache" / ".." / "logging.json",
        ] * 12,  # Simulate config file lookups
    }
    
    for workload_name, paths in workloads.items():
        # Use efficient list comprehension for this realistic test
        def workload_func():
            return [p.resolve(strict=False) for p in paths]
        
        # Warmup
        for _ in range(5):
            workload_func()
        
        # Benchmark
        iterations = 100  # Fewer iterations for longer workloads
        start = time.perf_counter()
        for _ in range(iterations):
            workload_func()
        elapsed = time.perf_counter() - start
        
        total_operations = len(paths) * iterations
        throughput = total_operations / elapsed
        
        print(f"{workload_name:<18}: {throughput:>8.0f} ops/s ({len(paths)} paths)")

def main():
    print("⚖️  Fair Python vs Rust Performance Comparison")
    print("=" * 50)
    print("This benchmark minimizes Python overhead to provide the most")
    print("accurate comparison with Rust soft_canonicalize performance.")
    print()
    
    temp_dir = create_test_structure()
    print(f"Test directory: {temp_dir}")
    print()
    
    # 1. Micro-benchmark for purest measurement
    individual_times = micro_benchmark_single_operations(temp_dir)
    
    # 2. Optimized bulk operations
    best_bulk_throughput = optimized_bulk_benchmark(temp_dir)
    
    # 3. Realistic workload patterns
    realistic_workload_benchmark(temp_dir)
    
    # Calculate overall performance metrics
    print("\n📊 Performance Summary")
    print("=" * 25)
    
    avg_individual_time = statistics.mean(individual_times.values())
    avg_individual_throughput = 1.0 / avg_individual_time
    
    print(f"Individual Operations Avg: {avg_individual_throughput:>8.0f} ops/s")
    print(f"Best Bulk Operations:      {best_bulk_throughput:>8.0f} ops/s")
    print(f"Range:                     {avg_individual_throughput:>8.0f} - {best_bulk_throughput:>8.0f} ops/s")
    
    # Analysis
    print("\n🎯 Fair Comparison Analysis")
    print("=" * 30)
    print("✅ Micro-benchmarks eliminate Python loop overhead")
    print("✅ List comprehensions use optimized Python patterns")  
    print("✅ Realistic workloads simulate actual usage")
    print("✅ Multiple measurement approaches for validation")
    
    print("\n📈 Expected Rust Performance Range:")
    rust_conservative = avg_individual_throughput * 2.0
    rust_optimistic = best_bulk_throughput * 2.7
    print(f"Conservative (2.0x):       {rust_conservative:>8.0f} ops/s")
    print(f"Optimistic (2.7x):         {rust_optimistic:>8.0f} ops/s")
    print(f"Expected Range:            {rust_conservative:>8.0f} - {rust_optimistic:>8.0f} ops/s")
    
    print("\n✅ Use these Python baseline numbers for fair comparison")
    print("   with your Rust benchmark results!")
    
    # Cleanup
    import shutil
    shutil.rmtree(temp_dir)

if __name__ == "__main__":
    main()
