#!/usr/bin/env python3
"""
Test optimized parallel implementation performance
"""

import sys
import os
import time

# Add the package directory to the path
sys.path.insert(0, os.path.join(os.path.dirname(__file__)))

try:
    from cutysoup import CutySoup
    CUTYSOUP_AVAILABLE = True
except ImportError as e:
    print(f"[ERROR] CutySoup not available: {e}")
    CUTYSOUP_AVAILABLE = False

if not CUTYSOUP_AVAILABLE:
    print("Please build CutySoup first: uv sync")
    sys.exit(1)


def create_test_document(element_count=2000):
    """Create a test document with specified element count."""
    html_parts = ['<html><head><title>Optimized Test Document</title></head><body>']
    
    # Add many articles for testing
    for i in range(element_count):
        html_parts.append(f'''
        <article class="post" id="post{i}" data-category="test{i % 5}">
            <header class="header">
                <h2 class="title">Post Title {i}</h2>
                <div class="meta">
                    <span class="author">Author {i % 10}</span>
                    <time class="date">2024-{(i % 12) + 1:02d}-01</time>
                </div>
            </header>
            <div class="content">
                <p class="intro">Introduction paragraph for post {i}.</p>
                <p class="body">Main content body for post {i} with more text.</p>
                <p class="conclusion">Conclusion for post {i}.</p>
            </div>
        </article>
        ''')
    
    html_parts.append('</body></html>')
    return ''.join(html_parts)


def benchmark_operation(name, operation, iterations=3):
    """Benchmark an operation."""
    times = []
    results = []
    
    for i in range(iterations):
        start_time = time.perf_counter()
        result = operation()
        end_time = time.perf_counter()
        
        times.append((end_time - start_time) * 1000)  # Convert to milliseconds
        results.append(len(result) if hasattr(result, '__len__') else (1 if result else 0))
    
    avg_time = sum(times) / len(times)
    avg_results = sum(results) / len(results)
    
    return {
        'name': name,
        'avg_time_ms': avg_time,
        'avg_results': avg_results,
        'times': times
    }


def test_optimized_vs_simple():
    """Test optimized parallel vs simple parallel performance."""
    print("Testing Optimized vs Simple Parallel Performance")
    print("=" * 60)
    
    document_sizes = [1000, 2000, 5000, 10000]
    
    for size in document_sizes:
        print(f"\nTesting with {size} elements...")
        
        # Create test document
        html = create_test_document(size)
        soup = CutySoup(html)
        root_node = soup._tree.get_root()
        
        print(f"Document size: {len(html):,} characters")
        
        # Test operations
        test_operations = [
            ('Find all articles', 
             lambda: root_node.find_all('article'), 
             lambda: root_node.find_all_parallel('article'),
             lambda: root_node.find_all_parallel_optimized('article')),
            ('Find all .title class', 
             lambda: root_node.find_all('', {'class': 'title'}), 
             lambda: root_node.find_all_parallel('', {'class': 'title'}),
             lambda: root_node.find_all_parallel_optimized('', {'class': 'title'})),
            ('Find all paragraphs', 
             lambda: root_node.find_all('p'), 
             lambda: root_node.find_all_parallel('p'),
             lambda: root_node.find_all_parallel_optimized('p')),
        ]
        
        for op_name, sequential_op, simple_parallel_op, optimized_parallel_op in test_operations:
            try:
                # Sequential benchmark
                seq_result = benchmark_operation(f"{op_name} (Sequential)", sequential_op)
                
                # Simple parallel benchmark
                simple_result = benchmark_operation(f"{op_name} (Simple Parallel)", simple_parallel_op)
                
                # Optimized parallel benchmark
                optimized_result = benchmark_operation(f"{op_name} (Optimized Parallel)", optimized_parallel_op)
                
                # Compare results
                seq_time = seq_result['avg_time_ms']
                simple_time = simple_result['avg_time_ms']
                opt_time = optimized_result['avg_time_ms']
                
                seq_count = seq_result['avg_results']
                simple_count = simple_result['avg_results']
                opt_count = optimized_result['avg_results']
                
                print(f"  {op_name}:")
                print(f"    Sequential:        {seq_time:.2f}ms ({seq_count:.0f} results)")
                print(f"    Simple Parallel:   {simple_time:.2f}ms ({simple_count:.0f} results)")
                print(f"    Optimized Parallel: {opt_time:.2f}ms ({opt_count:.0f} results)")
                
                # Check result correctness
                if abs(seq_count - simple_count) < 0.1 and abs(seq_count - opt_count) < 0.1:
                    # Calculate speedups
                    simple_speedup = seq_time / simple_time
                    opt_speedup = seq_time / opt_time
                    opt_vs_simple = simple_time / opt_time
                    
                    print(f"    Simple vs Sequential:   {simple_speedup:.2f}x")
                    print(f"    Optimized vs Sequential: {opt_speedup:.2f}x")
                    print(f"    Optimized vs Simple:     {opt_vs_simple:.2f}x")
                    
                    if opt_speedup > 1.0:
                        print(f"    [SUCCESS] Optimized parallel is {opt_speedup:.2f}x faster than sequential!")
                    elif opt_speedup > simple_speedup:
                        print(f"    [IMPROVEMENT] Optimized parallel improved over simple parallel")
                    else:
                        print(f"    [INFO] Still overhead, but functional")
                else:
                    print(f"    [ERROR] Result mismatch")
                    
            except Exception as e:
                print(f"  {op_name}: [ERROR] {e}")
                import traceback
                traceback.print_exc()
    
    print(f"\n[INFO] The optimized parallel implementation should show:")
    print(f"       - Better performance than simple parallel")
    print(f"       - Eventual speedup over sequential for large documents")


def test_threshold_analysis():
    """Test to find optimal thresholds for parallel processing."""
    print("\nOptimal Threshold Analysis")
    print("=" * 60)
    
    # Test with the optimized implementation
    thresholds = [500, 1000, 2000, 5000, 10000]
    
    print("Element Count | Sequential (ms) | Optimized Parallel (ms) | Speedup")
    print("-" * 70)
    
    for count in thresholds:
        html = create_test_document(count)
        soup = CutySoup(html)
        root_node = soup._tree.get_root()
        
        # Test article search (most common operation)
        seq_result = benchmark_operation("Sequential", lambda: root_node.find_all('article'), iterations=3)
        opt_result = benchmark_operation("Optimized", lambda: root_node.find_all_parallel_optimized('article'), iterations=3)
        
        seq_time = seq_result['avg_time_ms']
        opt_time = opt_result['avg_time_ms']
        speedup = seq_time / opt_time
        
        status = "[SPEEDUP]" if speedup > 1.0 else "[OVERHEAD]"
        print(f"{count:11d} | {seq_time:13.2f} | {opt_time:22.2f} | {speedup:6.2f}x {status}")


def main():
    """Main test function."""
    print("CutySoup Optimized Parallel Test")
    print("=" * 60)
    
    # Check if optimized methods are available
    try:
        html = "<html><body><p>test</p></body></html>"
        soup = CutySoup(html)
        root_node = soup._tree.get_root()
        
        if hasattr(root_node, 'find_all_parallel_optimized'):
            print("[OK] Optimized parallel methods available")
        else:
            print("[ERROR] Optimized parallel methods not available")
            return
            
    except Exception as e:
        print(f"[ERROR] Failed to test optimized methods: {e}")
        return
    
    try:
        test_optimized_vs_simple()
        test_threshold_analysis()
        
        print("\n[COMPLETE] Optimized parallel testing complete!")
        print("The optimized implementation should show reduced overhead and better performance.")
        
    except Exception as e:
        print(f"\n[ERROR] Test failed: {e}")
        import traceback
        traceback.print_exc()


if __name__ == "__main__":
    main()