//! Performance benchmark suite for phprs internals.
//!
//! Reports phprs throughput only. There is no bundled comparison to stock PHP; add your own measurements if needed.

use crate::engine::array_ops::{ArrayOps, OptimizedArray};
use crate::engine::perf_alloc::StringBuilder;
use crate::engine::types::{PhpType, PhpValue, Val};
use crate::engine::vm::{execute_ex, Op, OpArray, Opcode};
use crate::vm::execute_data::ExecuteData;
use std::time::{Duration, Instant};

/// Benchmark result
#[derive(Debug)]
pub struct BenchmarkResult {
    pub name: String,
    pub iterations: u64,
    pub total_time: Duration,
    pub avg_time: Duration,
    pub ops_per_second: f64,
    pub memory_used: usize,
}

impl BenchmarkResult {
    fn new(name: &str, iterations: u64, total_time: Duration, memory_used: usize) -> Self {
        let avg_time = total_time / iterations as u32;
        let ops_per_second = iterations as f64 / total_time.as_secs_f64();

        Self {
            name: name.to_string(),
            iterations,
            total_time,
            avg_time,
            ops_per_second,
            memory_used,
        }
    }
}

/// Benchmark suite
pub struct BenchmarkSuite {
    results: Vec<BenchmarkResult>,
}

impl BenchmarkSuite {
    pub fn new() -> Self {
        Self {
            results: Vec::new(),
        }
    }

    /// Run all benchmarks
    pub fn run_all_benchmarks(&mut self) {
        println!("Running Performance Benchmarks...\n");

        self.benchmark_simple_arithmetic();
        self.benchmark_string_operations();
        self.benchmark_array_operations();
        self.benchmark_function_calls();
        self.benchmark_loop_operations();
        self.benchmark_memory_operations();
        self.benchmark_jit_performance();
        self.benchmark_opcode_cache();

        self.print_summary();
    }

    /// Benchmark simple arithmetic operations
    fn benchmark_simple_arithmetic(&mut self) {
        println!("Benchmarking Simple Arithmetic Operations...");

        let iterations = 10_000_000;
        let start = Instant::now();

        for i in 0..iterations {
            // Simulate arithmetic operations
            let a = Val::new(PhpValue::Long(i as i64), PhpType::Long);
            let b = Val::new(PhpValue::Long((i + 1) as i64), PhpType::Long);
            let _result = crate::engine::operators::zval_add(&a, &b);
        }

        let elapsed = start.elapsed();
        let result = BenchmarkResult::new("simple_arithmetic", iterations, elapsed, 0);
        self.results.push(result);

        println!(
            "  ✓ Completed: {:.2} ops/sec",
            self.results.last().unwrap().ops_per_second
        );
    }

    /// Benchmark string operations
    fn benchmark_string_operations(&mut self) {
        println!("Benchmarking String Operations...");

        let iterations = 1_000_000;
        let start = Instant::now();

        let base_string = "Hello World";
        for _ in 0..iterations {
            // Test string concatenation
            let mut builder = StringBuilder::with_capacity(base_string.len() * 2);
            builder.push_str(base_string);
            builder.push_str("!");
            let _result = builder.into_string();
        }

        let elapsed = start.elapsed();
        let result = BenchmarkResult::new("string_operations", iterations, elapsed, 0);
        self.results.push(result);

        println!(
            "  ✓ Completed: {:.2} ops/sec",
            self.results.last().unwrap().ops_per_second
        );
    }

    /// Benchmark array operations
    fn benchmark_array_operations(&mut self) {
        println!("Benchmarking Array Operations...");

        let iterations = 500_000;
        let start = Instant::now();

        for i in 0..iterations {
            // Test array operations
            let mut array = OptimizedArray::with_capacity(100);

            // Push operations
            for j in 0..100 {
                let val = Val::new(PhpValue::Long(j as i64), PhpType::Long);
                array.push(val);
            }

            // String key operations
            array.insert_string(
                "test_key",
                Val::new(PhpValue::Long(i as i64), PhpType::Long),
            );

            // Lookup operations
            let _result = array.get("test_key");
            let _result2 = array.get_index(50);

            // Map operation
            let _mapped = ArrayOps::map(&array, |val| {
                if let PhpValue::Long(n) = val.value {
                    Val::new(PhpValue::Long(n * 2), PhpType::Long)
                } else {
                    val.clone()
                }
            });
        }

        let elapsed = start.elapsed();
        let result = BenchmarkResult::new("array_operations", iterations, elapsed, 0);
        self.results.push(result);

        println!(
            "  ✓ Completed: {:.2} ops/sec",
            self.results.last().unwrap().ops_per_second
        );
    }

    /// Benchmark function calls
    fn benchmark_function_calls(&mut self) {
        println!("Benchmarking Function Calls...");

        let iterations = 1_000_000;
        let start = Instant::now();

        // Create a simple function
        let mut op_array = OpArray::new("test.php".to_string());

        // Function: add($a, $b) { return $a + $b; }
        op_array.ops.push(Op::new(
            Opcode::Add,
            Val::new(PhpValue::Long(0), PhpType::Long), // $a
            Val::new(PhpValue::Long(0), PhpType::Long), // $b
            Val::new(PhpValue::Long(0), PhpType::Long), // result
            0,
        ));
        op_array.ops.push(Op::new(
            Opcode::Return,
            Val::new(PhpValue::Long(0), PhpType::Long), // result
            Val::new(PhpValue::Long(0), PhpType::Long),
            Val::new(PhpValue::Long(0), PhpType::Long),
            0,
        ));

        for i in 0..iterations {
            let mut execute_data = ExecuteData::new();

            // Set up arguments
            execute_data
                .call_args
                .push(Val::new(PhpValue::Long(i as i64), PhpType::Long));
            execute_data
                .call_args
                .push(Val::new(PhpValue::Long((i + 1) as i64), PhpType::Long));

            let _result = execute_ex(&mut execute_data, &op_array);
        }

        let elapsed = start.elapsed();
        let result = BenchmarkResult::new("function_calls", iterations, elapsed, 0);
        self.results.push(result);

        println!(
            "  ✓ Completed: {:.2} ops/sec",
            self.results.last().unwrap().ops_per_second
        );
    }

    /// Benchmark loop operations
    fn benchmark_loop_operations(&mut self) {
        println!("Benchmarking Loop Operations...");

        let iterations = 100_000;
        let start = Instant::now();

        for _ in 0..iterations {
            // Test loop performance with array operations
            let array = ArrayOps::range(0, 1000, 1);

            // Filter operation
            let _filtered = ArrayOps::filter(&array, |val| {
                if let PhpValue::Long(n) = val.value {
                    n % 2 == 0
                } else {
                    false
                }
            });

            // Reduce operation
            let mut _sum = 0i64;
            for bucket in &array.inner.ar_data {
                if let PhpValue::Long(n) = bucket.val.value {
                    _sum += n;
                }
            }
        }

        let elapsed = start.elapsed();
        let result = BenchmarkResult::new("loop_operations", iterations, elapsed, 0);
        self.results.push(result);

        println!(
            "  ✓ Completed: {:.2} ops/sec",
            self.results.last().unwrap().ops_per_second
        );
    }

    /// Benchmark memory operations
    fn benchmark_memory_operations(&mut self) {
        println!("Benchmarking Memory Operations...");

        let iterations = 2_000_000;
        let start = Instant::now();

        for i in 0..iterations {
            // Test memory allocation and deallocation
            let size = 64 + (i % 256);
            let mut vec = Vec::with_capacity(size);
            vec.resize(size, 0);

            // Simulate string operations
            let string = format!("test_string_{}", i);
            let _hash = crate::engine::perf_alloc::fast_hash(string.as_bytes());
        }

        let elapsed = start.elapsed();
        let result = BenchmarkResult::new("memory_operations", iterations as u64, elapsed, 0);
        self.results.push(result);

        println!(
            "  ✓ Completed: {:.2} ops/sec",
            self.results.last().unwrap().ops_per_second
        );
    }

    /// Benchmark JIT performance
    fn benchmark_jit_performance(&mut self) {
        println!("Benchmarking JIT Performance...");

        let iterations = 500_000;
        let start = Instant::now();

        // Simulate JIT hot code compilation
        for i in 0..iterations {
            // Track execution for JIT
            let func_name = if i % 3 == 0 {
                "add"
            } else if i % 3 == 1 {
                "multiply"
            } else {
                "concat"
            };
            let _should_compile = crate::engine::jit::increment_execution_counter(func_name);

            // Simulate JIT inline optimization
            if i % 100 == 0 {
                let a = Val::new(PhpValue::Long(i as i64), PhpType::Long);
                let b = Val::new(PhpValue::Long((i + 1) as i64), PhpType::Long);
                let _result = crate::engine::jit::try_inline_operation(Opcode::Add, &a, &b);
            }
        }

        let elapsed = start.elapsed();
        let result = BenchmarkResult::new("jit_performance", iterations, elapsed, 0);
        self.results.push(result);

        println!(
            "  ✓ Completed: {:.2} ops/sec",
            self.results.last().unwrap().ops_per_second
        );
    }

    /// Benchmark opcode cache
    fn benchmark_opcode_cache(&mut self) {
        println!("Benchmarking Opcode Cache...");

        let iterations = 100_000;
        let start = Instant::now();

        for i in 0..iterations {
            let filename = format!("test_file_{}.php", i % 10);

            // Test cache operations
            let cache = crate::engine::opcode_cache::get_opcode_cache();
            let _cached = cache.get(&filename);

            if i % 1000 == 0 {
                // Simulate cache store
                let ops = vec![Op::new(
                    Opcode::Nop,
                    Val::new(PhpValue::Long(0), PhpType::Long),
                    Val::new(PhpValue::Long(0), PhpType::Long),
                    Val::new(PhpValue::Long(0), PhpType::Long),
                    0,
                )];
                cache.store(
                    &filename,
                    ops,
                    crate::engine::opcode_cache::OptimizationLevel::Basic,
                );
            }
        }

        let elapsed = start.elapsed();
        let result = BenchmarkResult::new("opcode_cache", iterations, elapsed, 0);
        self.results.push(result);

        println!(
            "  ✓ Completed: {:.2} ops/sec",
            self.results.last().unwrap().ops_per_second
        );
    }

    /// Print performance summary
    fn print_summary(&self) {
        println!("\n{}", "=".repeat(60));
        println!("PHPRS BENCHMARK SUMMARY (ops/sec)");
        println!("{}", "=".repeat(60));

        for result in &self.results {
            println!("{:<20} | {:>12.2} ops/sec", result.name, result.ops_per_second);
        }

        // Print optimization statistics
        println!("\nOPTIMIZATION STATISTICS:");

        // JIT stats
        let jit = crate::engine::jit::get_jit_compiler();
        let jit = jit.read().unwrap();
        let jit_stats = jit.get_stats();
        println!("  JIT Functions Compiled: {}", jit_stats.0);
        println!("  JIT Total Executions: {}", jit_stats.1);
        println!("  JIT Hits: {}", jit_stats.2);

        // Opcode cache stats
        let cache_stats = crate::engine::opcode_cache::get_opcode_cache().get_stats();
        println!("  Cache Hits: {}", cache_stats.0);
        println!("  Cache Misses: {}", cache_stats.1);
        println!("  Cache Evictions: {}", cache_stats.2);

        // Function optimizer stats
        let func_opt = crate::engine::function_optimizer::get_function_optimizer();
        let func_opt = func_opt.read().unwrap();
        let func_stats = func_opt.get_stats();
        println!("  Functions Optimized: {}", func_stats.0);
        println!("  Functions Inlined: {}", func_stats.1);
        println!("  Calls Saved: {}", func_stats.2);

        // Memory stats
        let memory_stats = crate::engine::perf_alloc::get_memory_stats().get_stats();
        println!("  Total Allocations: {}", memory_stats.0);
        println!("  Peak Memory Usage: {} bytes", memory_stats.2);

        println!("\n{}", "=".repeat(60));
    }

    /// Export results to JSON
    pub fn export_results(&self) -> String {
        use serde_json;

        let mut results_map = serde_json::Map::new();

        for result in &self.results {
            let mut result_data = serde_json::Map::new();
            result_data.insert(
                "iterations".to_string(),
                serde_json::Value::Number(serde_json::Number::from(result.iterations)),
            );
            result_data.insert(
                "total_time_ms".to_string(),
                serde_json::Value::Number(serde_json::Number::from(
                    result.total_time.as_millis() as i64
                )),
            );
            result_data.insert(
                "avg_time_ns".to_string(),
                serde_json::Value::Number(serde_json::Number::from(
                    result.avg_time.as_nanos() as i64
                )),
            );
            result_data.insert(
                "ops_per_second".to_string(),
                serde_json::Value::Number(
                    serde_json::Number::from_f64(result.ops_per_second).unwrap(),
                ),
            );
            result_data.insert(
                "memory_used".to_string(),
                serde_json::Value::Number(serde_json::Number::from(result.memory_used as i64)),
            );

            results_map.insert(result.name.clone(), serde_json::Value::Object(result_data));
        }

        serde_json::to_string_pretty(&serde_json::Value::Object(results_map)).unwrap()
    }
}

impl Default for BenchmarkSuite {
    fn default() -> Self {
        Self::new()
    }
}

/// Run performance benchmarks
pub fn run_performance_benchmarks() {
    let mut suite = BenchmarkSuite::new();
    suite.run_all_benchmarks();

    // Export results
    let json_results = suite.export_results();
    std::fs::write("benchmark_results.json", json_results).unwrap();

    println!("\nBenchmark results exported to benchmark_results.json");
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_benchmark_suite_creation() {
        let suite = BenchmarkSuite::new();
        assert!(suite.results.is_empty());
    }

    #[test]
    fn test_benchmark_result_creation() {
        let duration = Duration::from_millis(1000);
        let result = BenchmarkResult::new("test", 1000, duration, 1024);

        assert_eq!(result.name, "test");
        assert_eq!(result.iterations, 1000);
        assert_eq!(result.memory_used, 1024);
        assert!(result.ops_per_second > 0.0);
    }

    #[test]
    fn test_export_results_json_shape() {
        let mut suite = BenchmarkSuite::new();
        suite.results.push(BenchmarkResult::new(
            "simple_arithmetic",
            100,
            Duration::from_millis(10),
            0,
        ));
        let json = suite.export_results();
        assert!(json.contains("simple_arithmetic"));
        assert!(json.contains("ops_per_second"));
        assert!(!json.contains("speedup_vs_php8"));
    }
}
