"""
Performance Validation Script for CutySoup

This script comprehensively validates CutySoup's 10x performance claims
against BeautifulSoup4 across various operations and document sizes.

Benchmarks include:
- Document parsing
- Element searching (find, find_all)  
- CSS selectors
- Tree traversal
- Text extraction
- Large document operations
"""

import time
import statistics
from typing import Tuple, Dict, Any, Callable
import sys
import os

# 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:
    print("❌ CutySoup not available - cannot run performance tests")
    CUTYSOUP_AVAILABLE = False

try:
    from bs4 import BeautifulSoup
    BS4_AVAILABLE = True
except ImportError:
    print("❌ BeautifulSoup4 not available - cannot run performance tests")
    BS4_AVAILABLE = False

if not (CUTYSOUP_AVAILABLE and BS4_AVAILABLE):
    sys.exit(1)


class PerformanceBenchmark:
    """Comprehensive performance benchmark suite."""
    
    def __init__(self):
        self.results = {}
        self.test_htmls = self._generate_test_documents()
        
    def _generate_test_documents(self) -> Dict[str, str]:
        """Generate test documents of various sizes."""
        documents = {}
        
        # Small document
        documents['small'] = '''
        <html>
            <head><title>Small Document</title></head>
            <body>
                <div class="container">
                    <h1 class="title">Main Title</h1>
                    <p class="intro">Introduction paragraph</p>
                    <article class="post">
                        <h2>Article Title</h2>
                        <p class="content">Article content with <em>emphasis</em>.</p>
                        <ul class="tags">
                            <li class="tag">python</li>
                            <li class="tag">parsing</li>
                        </ul>
                    </article>
                </div>
            </body>
        </html>
        '''
        
        # Medium document
        medium_content = '<html><head><title>Medium Document</title></head><body><div class="container">'
        for i in range(50):
            medium_content += f'''
            <article class="post post-{i}" id="article{i}" data-index="{i}">
                <header class="post-header">
                    <h2 class="post-title">Article Title {i}</h2>
                    <div class="post-meta">
                        <span class="author">Author {i % 5}</span>
                        <time class="date" datetime="2024-01-01">Jan 1, 2024</time>
                    </div>
                </header>
                <div class="post-content">
                    <p class="intro">Introduction for article {i}</p>
                    <p class="content">Main content for article {i} with various <em>formatting</em> and <strong>styles</strong>.</p>
                    <p class="conclusion">Conclusion for article {i}</p>
                </div>
                <footer class="post-footer">
                    <ul class="tags">
                        <li class="tag primary">tag{i % 10}</li>
                        <li class="tag secondary">category{i % 5}</li>
                    </ul>
                    <div class="social">
                        <a href="#" class="social-link twitter">Twitter</a>
                        <a href="#" class="social-link facebook">Facebook</a>
                    </div>
                </footer>
            </article>
            '''
        medium_content += '</div></body></html>'
        documents['medium'] = medium_content
        
        # Large document
        large_content = '<html><head><title>Large Document</title></head><body><div class="container">'
        for i in range(200):
            large_content += f'''
            <section class="section section-{i}" id="section{i}" data-category="cat{i % 10}">
                <header class="section-header">
                    <h2 class="section-title title-{i % 3}">Section {i}</h2>
                    <nav class="section-nav">
                        <a href="#prev" class="nav-link prev">Previous</a>
                        <a href="#next" class="nav-link next">Next</a>
                    </nav>
                </header>
                <div class="section-content">
                    <article class="article primary" data-id="{i}">
                        <h3 class="article-title">Article {i}</h3>
                        <div class="article-meta">
                            <span class="author" data-author-id="{i % 20}">Author {i % 20}</span>
                            <time class="published" datetime="2024-01-{(i % 30) + 1:02d}">Date {i}</time>
                            <div class="rating" data-score="{(i % 5) + 1}">
                                {"★" * ((i % 5) + 1)}
                            </div>
                        </div>
                        <div class="article-body">
                            <p class="lead">Lead paragraph for article {i} with important information.</p>
                            <p class="content">Main content paragraph with <em>emphasis</em>, <strong>bold text</strong>, and <code>inline code</code>.</p>
                            <blockquote class="quote" cite="source{i}">
                                <p>Quote content for article {i}</p>
                                <footer>— Author {i % 10}</footer>
                            </blockquote>
                            <ul class="features">
                                <li class="feature important">Feature {i * 3}</li>
                                <li class="feature">Feature {i * 3 + 1}</li>
                                <li class="feature optional">Feature {i * 3 + 2}</li>
                            </ul>
                        </div>
                        <footer class="article-footer">
                            <div class="tags">
                                <span class="tag primary">tag{i % 15}</span>
                                <span class="tag secondary">cat{i % 8}</span>
                                <span class="tag tertiary">type{i % 5}</span>
                            </div>
                            <div class="actions">
                                <button class="btn like" data-action="like" data-count="{i * 2}">Like</button>
                                <button class="btn share" data-action="share">Share</button>
                                <button class="btn comment" data-action="comment" data-count="{i % 10}">Comment</button>
                            </div>
                        </footer>
                    </article>
                </div>
            </section>
            '''
        large_content += '</div></body></html>'
        documents['large'] = large_content
        
        return documents

    def benchmark_function(self, func: Callable, *args, iterations: int = 5) -> Tuple[float, float]:
        """Benchmark a function and return average time and standard deviation."""
        times = []
        
        for _ in range(iterations):
            start_time = time.perf_counter()
            try:
                result = func(*args)
                end_time = time.perf_counter()
                times.append(end_time - start_time)
            except Exception as e:
                print(f"Error in benchmark: {e}")
                return float('inf'), float('inf')
        
        avg_time = statistics.mean(times)
        std_dev = statistics.stdev(times) if len(times) > 1 else 0.0
        return avg_time, std_dev

    def benchmark_parsing(self) -> Dict[str, Dict[str, Tuple[float, float]]]:
        """Benchmark document parsing performance."""
        print("Benchmarking document parsing...")
        results = {}
        
        for doc_size, html in self.test_htmls.items():
            print(f"  Testing {doc_size} document ({len(html):,} characters)")
            
            # Benchmark CutySoup parsing
            cs_time, cs_std = self.benchmark_function(CutySoup, html)
            
            # Benchmark BeautifulSoup parsing
            bs_time, bs_std = self.benchmark_function(BeautifulSoup, html, 'html.parser')
            
            results[doc_size] = {
                'cutysoup': (cs_time, cs_std),
                'beautifulsoup': (bs_time, bs_std),
                'speedup': bs_time / cs_time if cs_time > 0 else float('inf')
            }
            
            print(f"    CutySoup: {cs_time:.4f}s ± {cs_std:.4f}s")
            print(f"    BeautifulSoup: {bs_time:.4f}s ± {bs_std:.4f}s")
            print(f"    Speedup: {results[doc_size]['speedup']:.2f}x")
        
        return results

    def benchmark_searching(self) -> Dict[str, Dict[str, Any]]:
        """Benchmark element searching performance."""
        print("\nBenchmarking element searching...")
        results = {}
        
        for doc_size, html in self.test_htmls.items():
            print(f"  Testing {doc_size} document")
            
            cs_soup = CutySoup(html)
            bs_soup = BeautifulSoup(html, 'html.parser')
            
            search_tests = [
                ('find_by_tag', lambda soup: soup.find('p')),
                ('find_by_class', lambda soup: soup.find('p', class_='content')),
                ('find_by_id', lambda soup: soup.find(id='article1') if doc_size != 'small' else soup.find('h1')),
                ('find_all_tag', lambda soup: soup.find_all('p')),
                ('find_all_class', lambda soup: soup.find_all(class_='tag')),
                ('find_with_attrs', lambda soup: soup.find_all('article', {'class': 'post'}) if doc_size != 'small' else soup.find_all('li'))
            ]
            
            doc_results = {}
            for test_name, test_func in search_tests:
                try:
                    cs_time, cs_std = self.benchmark_function(test_func, cs_soup)
                    bs_time, bs_std = self.benchmark_function(test_func, bs_soup)
                    
                    speedup = bs_time / cs_time if cs_time > 0 else float('inf')
                    doc_results[test_name] = {
                        'cutysoup': (cs_time, cs_std),
                        'beautifulsoup': (bs_time, bs_std),
                        'speedup': speedup
                    }
                    
                    print(f"    {test_name}: {speedup:.2f}x speedup")
                except Exception as e:
                    print(f"    {test_name}: Error - {e}")
                    doc_results[test_name] = {'error': str(e)}
            
            results[doc_size] = doc_results
        
        return results

    def benchmark_css_selectors(self) -> Dict[str, Dict[str, Any]]:
        """Benchmark CSS selector performance."""
        print("\nBenchmarking CSS selectors...")
        results = {}
        
        for doc_size, html in self.test_htmls.items():
            print(f"  Testing {doc_size} document")
            
            cs_soup = CutySoup(html)
            bs_soup = BeautifulSoup(html, 'html.parser')
            
            css_tests = [
                ('select_tag', 'p'),
                ('select_class', '.content'),
                ('select_id', '#article1' if doc_size != 'small' else 'h1'),
                ('select_descendant', 'div p'),
                ('select_attribute', '[class]'),
                ('select_multiple', '.tag, .title')
            ]
            
            doc_results = {}
            for test_name, selector in css_tests:
                try:
                    cs_time, cs_std = self.benchmark_function(lambda soup, sel=selector: soup.select(sel), cs_soup)
                    bs_time, bs_std = self.benchmark_function(lambda soup, sel=selector: soup.select(sel), bs_soup)
                    
                    speedup = bs_time / cs_time if cs_time > 0 else float('inf')
                    doc_results[test_name] = {
                        'cutysoup': (cs_time, cs_std),
                        'beautifulsoup': (bs_time, bs_std),
                        'speedup': speedup
                    }
                    
                    print(f"    {test_name} ('{selector}'): {speedup:.2f}x speedup")
                except Exception as e:
                    print(f"    {test_name}: Error - {e}")
                    doc_results[test_name] = {'error': str(e)}
            
            results[doc_size] = doc_results
        
        return results

    def benchmark_text_extraction(self) -> Dict[str, Dict[str, Any]]:
        """Benchmark text extraction performance."""
        print("\nBenchmarking text extraction...")
        results = {}
        
        for doc_size, html in self.test_htmls.items():
            print(f"  Testing {doc_size} document")
            
            cs_soup = CutySoup(html)
            bs_soup = BeautifulSoup(html, 'html.parser')
            
            text_tests = [
                ('get_text_full', lambda soup: soup.get_text()),
                ('get_text_stripped', lambda soup: soup.get_text(strip=True)),
                ('get_text_separator', lambda soup: soup.get_text(separator=' | ')),
                ('strings_iteration', lambda soup: list(soup.strings)),
                ('stripped_strings', lambda soup: list(soup.stripped_strings))
            ]
            
            doc_results = {}
            for test_name, test_func in text_tests:
                try:
                    cs_time, cs_std = self.benchmark_function(test_func, cs_soup)
                    bs_time, bs_std = self.benchmark_function(test_func, bs_soup)
                    
                    speedup = bs_time / cs_time if cs_time > 0 else float('inf')
                    doc_results[test_name] = {
                        'cutysoup': (cs_time, cs_std),
                        'beautifulsoup': (bs_time, bs_std),
                        'speedup': speedup
                    }
                    
                    print(f"    {test_name}: {speedup:.2f}x speedup")
                except Exception as e:
                    print(f"    {test_name}: Error - {e}")
                    doc_results[test_name] = {'error': str(e)}
            
            results[doc_size] = doc_results
        
        return results

    def benchmark_navigation(self) -> Dict[str, Dict[str, Any]]:
        """Benchmark tree navigation performance."""
        print("\nBenchmarking tree navigation...")
        results = {}
        
        for doc_size, html in self.test_htmls.items():
            print(f"  Testing {doc_size} document")
            
            cs_soup = CutySoup(html)
            bs_soup = BeautifulSoup(html, 'html.parser')
            
            # Get a reference element for navigation tests
            cs_element = cs_soup.find('p')
            bs_element = bs_soup.find('p')
            
            if not cs_element or not bs_element:
                print(f"    Skipping {doc_size} - no suitable elements found")
                continue
            
            nav_tests = [
                ('parent_access', lambda elem: elem.parent),
                ('children_iteration', lambda elem: list(elem.children)),
                ('descendants_iteration', lambda elem: list(elem.descendants())),
                ('siblings_iteration', lambda elem: list(elem.next_siblings())),
                ('parents_iteration', lambda elem: list(elem.parents()))
            ]
            
            doc_results = {}
            for test_name, test_func in nav_tests:
                try:
                    cs_time, cs_std = self.benchmark_function(test_func, cs_element)
                    bs_time, bs_std = self.benchmark_function(test_func, bs_element)
                    
                    speedup = bs_time / cs_time if cs_time > 0 else float('inf')
                    doc_results[test_name] = {
                        'cutysoup': (cs_time, cs_std),
                        'beautifulsoup': (bs_time, bs_std),
                        'speedup': speedup
                    }
                    
                    print(f"    {test_name}: {speedup:.2f}x speedup")
                except Exception as e:
                    print(f"    {test_name}: Error - {e}")
                    doc_results[test_name] = {'error': str(e)}
            
            results[doc_size] = doc_results
        
        return results

    def run_comprehensive_benchmark(self) -> Dict[str, Any]:
        """Run all benchmarks and return comprehensive results."""
        print("Starting Comprehensive CutySoup Performance Validation")
        print("=" * 70)
        
        results = {
            'parsing': self.benchmark_parsing(),
            'searching': self.benchmark_searching(),
            'css_selectors': self.benchmark_css_selectors(),
            'text_extraction': self.benchmark_text_extraction(),
            'navigation': self.benchmark_navigation()
        }
        
        return results

    def generate_performance_report(self, results: Dict[str, Any]) -> str:
        """Generate a comprehensive performance report."""
        report = []
        report.append("CutySoup Performance Validation Report")
        report.append("=" * 50)
        report.append("")
        
        # Calculate overall statistics
        all_speedups = []
        category_summaries = {}
        
        for category, category_results in results.items():
            category_speedups = []
            
            for doc_size, doc_results in category_results.items():
                if isinstance(doc_results, dict):
                    for test_name, test_results in doc_results.items():
                        if isinstance(test_results, dict) and 'speedup' in test_results:
                            speedup = test_results['speedup']
                            if speedup != float('inf') and speedup > 0:
                                category_speedups.append(speedup)
                                all_speedups.append(speedup)
            
            if category_speedups:
                avg_speedup = statistics.mean(category_speedups)
                max_speedup = max(category_speedups)
                min_speedup = min(category_speedups)
                
                category_summaries[category] = {
                    'average': avg_speedup,
                    'max': max_speedup,
                    'min': min_speedup,
                    'count': len(category_speedups)
                }
        
        # Overall summary
        if all_speedups:
            overall_avg = statistics.mean(all_speedups)
            overall_max = max(all_speedups)
            overall_min = min(all_speedups)
            
            report.append("OVERALL PERFORMANCE SUMMARY")
            report.append("-" * 30)
            report.append(f"Average Speedup: {overall_avg:.2f}x")
            report.append(f"Maximum Speedup: {overall_max:.2f}x")
            report.append(f"Minimum Speedup: {overall_min:.2f}x")
            report.append(f"Total Tests: {len(all_speedups)}")
            report.append("")
            
            # Performance verdict
            if overall_avg >= 10.0:
                verdict = "EXCELLENT - Exceeds 10x performance target!"
            elif overall_avg >= 5.0:
                verdict = "GOOD - Strong performance, approaching target"
            elif overall_avg >= 2.0:
                verdict = "MODERATE - Noticeable improvement, needs optimization"
            else:
                verdict = "NEEDS WORK - Performance below expectations"
            
            report.append(f"VERDICT: {verdict}")
            report.append("")
        
        # Category breakdown
        report.append("CATEGORY BREAKDOWN")
        report.append("-" * 20)
        
        for category, summary in category_summaries.items():
            report.append(f"{category.upper()}:")
            report.append(f"  Average: {summary['average']:.2f}x")
            report.append(f"  Range: {summary['min']:.2f}x - {summary['max']:.2f}x")
            report.append(f"  Tests: {summary['count']}")
            report.append("")
        
        # Detailed results
        report.append("DETAILED RESULTS")
        report.append("-" * 16)
        
        for category, category_results in results.items():
            report.append(f"\n{category.upper()} BENCHMARKS:")
            
            for doc_size, doc_results in category_results.items():
                report.append(f"  {doc_size.capitalize()} Document:")
                
                if isinstance(doc_results, dict):
                    for test_name, test_results in doc_results.items():
                        if isinstance(test_results, dict):
                            if 'speedup' in test_results:
                                speedup = test_results['speedup']
                                cs_time = test_results['cutysoup'][0]
                                bs_time = test_results['beautifulsoup'][0]
                                report.append(f"    {test_name}: {speedup:.2f}x (CS: {cs_time:.4f}s, BS: {bs_time:.4f}s)")
                            elif 'error' in test_results:
                                report.append(f"    {test_name}: ERROR - {test_results['error']}")
                        else:
                            report.append(f"    {test_name}: {test_results}")
                else:
                    report.append(f"    {doc_results}")
        
        return "\n".join(report)


def main():
    """Main function to run performance validation."""
    print("CutySoup Performance Validation Suite")
    print("=" * 50)
    
    if not CUTYSOUP_AVAILABLE:
        print("ERROR: CutySoup not available. Please build the C++ extension first:")
        print("   uv sync")
        return
    
    if not BS4_AVAILABLE:
        print("ERROR: BeautifulSoup4 not available. Please install it:")
        print("   pip install beautifulsoup4")
        return
    
    benchmark = PerformanceBenchmark()
    results = benchmark.run_comprehensive_benchmark()
    
    print("\n" + "=" * 70)
    print("GENERATING PERFORMANCE REPORT")
    print("=" * 70)
    
    report = benchmark.generate_performance_report(results)
    print(report)
    
    # Save report to file
    report_file = "performance_validation_report.txt"
    with open(report_file, 'w', encoding='utf-8') as f:
        f.write(report)
    
    print(f"\nFull report saved to: {report_file}")
    print("Performance validation complete!")


if __name__ == "__main__":
    main()