// SPRINT5-002: Performance benchmarking and optimization
// PMAT Complexity: <10 per function
use crate::notebook::testing::types::Notebook;
use std::collections::HashMap;
use std::time::{Duration, Instant};
use std::sync::{Arc, Mutex};
/// Performance benchmarking system
pub struct PerformanceBenchmarker {
    benchmarks: Vec<Benchmark>,
    results: Vec<BenchmarkResult>,
}
pub struct Benchmark {
    pub id: String,
    pub name: String,
    pub setup: Box<dyn Fn()>,
    pub run: Box<dyn Fn()>,
    pub teardown: Box<dyn Fn()>,
    pub iterations: usize,
}
#[derive(Debug, Clone)]
pub struct BenchmarkResult {
    pub id: String,
    pub mean_time_ms: f64,
    pub median_time_ms: f64,
    pub std_dev_ms: f64,
    pub min_time_ms: f64,
    pub max_time_ms: f64,
    pub percentile_95_ms: f64,
}
impl Default for PerformanceBenchmarker {
    fn default() -> Self {
        Self::new()
    }
}

impl PerformanceBenchmarker {
/// # Examples
/// 
/// ```
/// use ruchy::notebook::testing::performance::PerformanceBenchmarker;
/// 
/// let instance = PerformanceBenchmarker::new();
/// // Verify behavior
/// ```
/// # Examples
/// 
/// ```
/// use ruchy::notebook::testing::performance::PerformanceBenchmarker;
/// 
/// let instance = PerformanceBenchmarker::new();
/// // Verify behavior
/// ```
/// # Examples
/// 
/// ```
/// use ruchy::notebook::testing::performance::PerformanceBenchmarker;
/// 
/// let instance = PerformanceBenchmarker::new();
/// // Verify behavior
/// ```
/// # Examples
/// 
/// ```
/// use ruchy::notebook::testing::performance::PerformanceBenchmarker;
/// 
/// let instance = PerformanceBenchmarker::new();
/// // Verify behavior
/// ```
/// # Examples
/// 
/// ```
/// use ruchy::notebook::testing::performance::PerformanceBenchmarker;
/// 
/// let instance = PerformanceBenchmarker::new();
/// // Verify behavior
/// ```
/// # Examples
/// 
/// ```
/// use ruchy::notebook::testing::performance::PerformanceBenchmarker;
/// 
/// let instance = PerformanceBenchmarker::new();
/// // Verify behavior
/// ```
/// # Examples
/// 
/// ```
/// use ruchy::notebook::testing::performance::PerformanceBenchmarker;
/// 
/// let instance = PerformanceBenchmarker::new();
/// // Verify behavior
/// ```
pub fn new() -> Self {
        Self {
            benchmarks: Vec::new(),
            results: Vec::new(),
        }
    }
    /// Add a benchmark to the suite
/// # Examples
/// 
/// ```
/// use ruchy::notebook::testing::performance::PerformanceBenchmarker;
/// 
/// let mut instance = PerformanceBenchmarker::new();
/// let result = instance.add_benchmark();
/// // Verify behavior
/// ```
pub fn add_benchmark(&mut self, benchmark: Benchmark) {
        self.benchmarks.push(benchmark);
    }
    /// Run all benchmarks
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::run_all;
/// 
/// let result = run_all(());
/// assert_eq!(result, Ok(()));
/// ```
pub fn run_all(&mut self) -> Vec<BenchmarkResult> {
        self.results.clear();
        for benchmark in &self.benchmarks {
            let result = self.run_benchmark(benchmark);
            self.results.push(result);
        }
        self.results.clone()
    }
    fn run_benchmark(&self, benchmark: &Benchmark) -> BenchmarkResult {
        let mut times = Vec::new();
        for _ in 0..benchmark.iterations {
            (benchmark.setup)();
            let start = Instant::now();
            (benchmark.run)();
            let duration = start.elapsed();
            (benchmark.teardown)();
            times.push(duration.as_secs_f64() * 1000.0);
        }
        self.calculate_statistics(&benchmark.id, &mut times)
    }
    fn calculate_statistics(&self, id: &str, times: &mut [f64]) -> BenchmarkResult {
        times.sort_by(|a, b| a.partial_cmp(b).unwrap());
        let mean = times.iter().sum::<f64>() / times.len() as f64;
        let median = times[times.len() / 2];
        let min = times[0];
        let max = times[times.len() - 1];
        let percentile_95 = times[(times.len() as f64 * 0.95) as usize];
        let variance = times.iter()
            .map(|t| (t - mean).powi(2))
            .sum::<f64>() / times.len() as f64;
        BenchmarkResult {
            id: id.to_string(),
            mean_time_ms: mean,
            median_time_ms: median,
            std_dev_ms: variance.sqrt(),
            min_time_ms: min,
            max_time_ms: max,
            percentile_95_ms: percentile_95,
        }
    }
}
/// Parallel test executor
#[derive(Debug)]
pub struct ParallelTestExecutor {
    num_threads: usize,
}
#[derive(Debug, Clone)]
pub struct TestExecutionResult {
    pub cell_id: String,
    pub success: bool,
    pub output: String,
    pub duration_ms: u64,
}
impl Default for ParallelTestExecutor {
    fn default() -> Self {
        Self::new()
    }
}

impl ParallelTestExecutor {
    pub fn new() -> Self {
        Self {
            num_threads: num_cpus::get(),
        }
    }
/// # Examples
/// 
/// ```
/// use ruchy::notebook::testing::performance::ParallelTestExecutor;
/// 
/// let mut instance = ParallelTestExecutor::new();
/// let result = instance.with_threads();
/// // Verify behavior
/// ```
pub fn with_threads(num_threads: usize) -> Self {
        Self { num_threads }
    }
    /// Execute notebook cells in parallel
/// # Examples
/// 
/// ```
/// use ruchy::notebook::testing::performance::ParallelTestExecutor;
/// 
/// let mut instance = ParallelTestExecutor::new();
/// let result = instance.execute_parallel();
/// // Verify behavior
/// ```
pub fn execute_parallel(&self, notebook: &Notebook, threads: usize) -> Vec<TestExecutionResult> {
        use std::thread;
        let num_threads = threads.min(self.num_threads);
        let cells = Arc::new(notebook.cells.clone());
        let results = Arc::new(Mutex::new(Vec::new()));
        let mut handles = Vec::new();
        // Split cells among threads
        let chunk_size = cells.len().div_ceil(num_threads);
        for chunk_idx in 0..num_threads {
            let start = chunk_idx * chunk_size;
            let end = ((chunk_idx + 1) * chunk_size).min(cells.len());
            if start >= cells.len() {
                break;
            }
            let cells = Arc::clone(&cells);
            let results = Arc::clone(&results);
            let handle = thread::spawn(move || {
                for i in start..end {
                    let cell = &cells[i];
                    let start_time = Instant::now();
                    // Simulate execution
                    let result = TestExecutionResult {
                        cell_id: cell.id.clone(),
                        success: true,
                        output: format!("Executed {}", cell.id),
                        duration_ms: start_time.elapsed().as_millis() as u64,
                    };
                    results.lock().expect("Failed to acquire lock").push(result);
                }
            });
            handles.push(handle);
        }
        // Wait for all threads
        for handle in handles {
            handle.join().expect("Thread failed to join");
        }
        Arc::try_unwrap(results).unwrap().into_inner().unwrap()
    }
}
/// Test result caching system
#[derive(Debug)]
pub struct TestCache {
    cache: HashMap<String, CachedResult>,
    hits: usize,
    misses: usize,
    max_size: usize,
}
#[derive(Debug, Clone)]
pub struct CachedResult {
    pub result: TestExecutionResult,
    pub timestamp: Instant,
    pub hash: u64,
}
#[derive(Debug, Clone)]
pub struct CacheStats {
    pub hits: usize,
    pub misses: usize,
    pub size: usize,
    pub hit_rate: f64,
}
impl Default for TestCache {
    fn default() -> Self {
        Self::new()
    }
}

impl TestCache {
    pub fn new() -> Self {
        Self {
            cache: HashMap::new(),
            hits: 0,
            misses: 0,
            max_size: 1000,
        }
    }
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::with_max_size;
/// 
/// let result = with_max_size(());
/// assert_eq!(result, Ok(()));
/// ```
pub fn with_max_size(max_size: usize) -> Self {
        Self {
            cache: HashMap::new(),
            hits: 0,
            misses: 0,
            max_size,
        }
    }
    /// Store a test result
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::store;
/// 
/// let result = store("example");
/// assert_eq!(result, Ok(()));
/// ```
pub fn store(&mut self, key: &str, result: &TestExecutionResult) {
        // Evict old entries if at capacity
        if self.cache.len() >= self.max_size {
            self.evict_oldest();
        }
        let cached = CachedResult {
            result: result.clone(),
            timestamp: Instant::now(),
            hash: self.calculate_hash(key),
        };
        self.cache.insert(key.to_string(), cached);
    }
    /// Get a cached result
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::get;
/// 
/// let result = get("example");
/// assert_eq!(result, Ok(()));
/// ```
pub fn get(&mut self, key: &str) -> Option<TestExecutionResult> {
        if let Some(cached) = self.cache.get(key) {
            self.hits += 1;
            Some(cached.result.clone())
        } else {
            self.misses += 1;
            None
        }
    }
    /// Get cache statistics
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::get_stats;
/// 
/// let result = get_stats(());
/// assert_eq!(result, Ok(()));
/// ```
pub fn get_stats(&self) -> CacheStats {
        let total = self.hits + self.misses;
        CacheStats {
            hits: self.hits,
            misses: self.misses,
            size: self.cache.len(),
            hit_rate: if total > 0 {
                self.hits as f64 / total as f64
            } else {
                0.0
            },
        }
    }
    fn evict_oldest(&mut self) {
        if let Some(oldest_key) = self.find_oldest_key() {
            self.cache.remove(&oldest_key);
        }
    }
    fn find_oldest_key(&self) -> Option<String> {
        self.cache.iter()
            .min_by_key(|(_, v)| v.timestamp)
            .map(|(k, _)| k.clone())
    }
    fn calculate_hash(&self, key: &str) -> u64 {
        use std::collections::hash_map::DefaultHasher;
        use std::hash::{Hash, Hasher};
        let mut hasher = DefaultHasher::new();
        key.hash(&mut hasher);
        hasher.finish()
    }
}
/// Resource monitoring
#[derive(Debug)]
pub struct ResourceMonitor {
    monitoring: Arc<Mutex<bool>>,
    start_time: Option<Instant>,
}
#[derive(Debug, Clone)]
pub struct ResourceUsage {
    pub memory_mb: f64,
    pub cpu_percent: f64,
    pub duration_ms: u64,
    pub peak_memory_mb: f64,
}
impl Default for ResourceMonitor {
    fn default() -> Self {
        Self::new()
    }
}

impl ResourceMonitor {
    pub fn new() -> Self {
        Self {
            monitoring: Arc::new(Mutex::new(false)),
            start_time: None,
        }
    }
    /// Start monitoring resources
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::start;
/// 
/// let result = start(());
/// assert_eq!(result, Ok(()));
/// ```
pub fn start(&mut self) {
        *self.monitoring.lock().expect("Failed to acquire lock") = true;
        self.start_time = Some(Instant::now());
    }
    /// Stop monitoring
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::stop;
/// 
/// let result = stop(());
/// assert_eq!(result, Ok(()));
/// ```
pub fn stop(&self) {
        *self.monitoring.lock().expect("Failed to acquire lock") = false;
    }
    /// Get current resource usage
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::get_usage;
/// 
/// let result = get_usage(());
/// assert_eq!(result, Ok(()));
/// ```
pub fn get_usage(&self) -> ResourceUsage {
        let duration_ms = self.start_time
            .map_or(0, |t| t.elapsed().as_millis() as u64);
        // Simulated values - real implementation would query system
        ResourceUsage {
            memory_mb: 100.0,
            cpu_percent: 25.0,
            duration_ms,
            peak_memory_mb: 150.0,
        }
    }
}
/// Test sharding for distributed execution
#[derive(Debug)]
pub struct TestSharder;
impl Default for TestSharder {
    fn default() -> Self {
        Self::new()
    }
}

impl TestSharder {
    pub fn new() -> Self {
        Self
    }
    /// Shard tests across multiple workers
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::shard;
/// 
/// let result = shard(());
/// assert_eq!(result, Ok(()));
/// ```
pub fn shard(&self, tests: &[String], num_shards: usize) -> Vec<Vec<String>> {
        if num_shards == 0 {
            return vec![];
        }
        let mut shards = vec![Vec::new(); num_shards];
        for (i, test) in tests.iter().enumerate() {
            shards[i % num_shards].push(test.clone());
        }
        shards
    }
    /// Shard by estimated duration for better balance
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::shard_by_duration;
/// 
/// let result = shard_by_duration(());
/// assert_eq!(result, Ok(()));
/// ```
pub fn shard_by_duration(&self, tests: &[(String, Duration)], num_shards: usize) -> Vec<Vec<String>> {
        if num_shards == 0 {
            return vec![];
        }
        let mut shards = vec![Vec::new(); num_shards];
        let mut shard_durations = vec![Duration::ZERO; num_shards];
        // Sort by duration (longest first)
        let mut sorted_tests = tests.to_vec();
        sorted_tests.sort_by_key(|(_, d)| std::cmp::Reverse(*d));
        // Assign to shard with smallest total duration
        for (test, duration) in sorted_tests {
            let min_shard = shard_durations.iter()
                .enumerate()
                .min_by_key(|(_, d)| **d)
                .map_or(0, |(i, _)| i);
            shards[min_shard].push(test);
            shard_durations[min_shard] += duration;
        }
        shards
    }
}
/// Regression detection
#[derive(Debug)]
pub struct RegressionDetector {
    baselines: HashMap<String, f64>,
    tolerance_percent: f64,
}
#[derive(Debug, Clone)]
pub struct RegressionResult {
    pub is_regression: bool,
    pub percent_change: f64,
    pub baseline: f64,
    pub current: f64,
}
impl Default for RegressionDetector {
    fn default() -> Self {
        Self::new()
    }
}

impl RegressionDetector {
    pub fn new() -> Self {
        Self {
            baselines: HashMap::new(),
            tolerance_percent: 5.0,
        }
    }
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::with_tolerance;
/// 
/// let result = with_tolerance(());
/// assert_eq!(result, Ok(()));
/// ```
pub fn with_tolerance(tolerance_percent: f64) -> Self {
        Self {
            baselines: HashMap::new(),
            tolerance_percent,
        }
    }
    /// Add a baseline measurement
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::add_baseline;
/// 
/// let result = add_baseline("example");
/// assert_eq!(result, Ok(()));
/// ```
pub fn add_baseline(&mut self, name: &str, time_ms: f64) {
        self.baselines.insert(name.to_string(), time_ms);
    }
    /// Check if current measurement is a regression
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::check_regression;
/// 
/// let result = check_regression("example");
/// assert_eq!(result, Ok(()));
/// ```
pub fn check_regression(&self, name: &str, time_ms: f64) -> RegressionResult {
        if let Some(&baseline) = self.baselines.get(name) {
            let percent_change = ((time_ms - baseline) / baseline) * 100.0;
            RegressionResult {
                is_regression: percent_change > self.tolerance_percent,
                percent_change,
                baseline,
                current: time_ms,
            }
        } else {
            RegressionResult {
                is_regression: false,
                percent_change: 0.0,
                baseline: 0.0,
                current: time_ms,
            }
        }
    }
}
/// Test prioritization based on history
#[derive(Debug)]
pub struct TestPrioritizer {
    history: HashMap<String, TestHistory>,
}
#[derive(Debug, Clone)]
pub struct TestHistory {
    pub failures: usize,
    pub successes: usize,
    pub avg_duration_ms: f64,
    pub last_run: Option<Instant>,
}
impl Default for TestPrioritizer {
    fn default() -> Self {
        Self::new()
    }
}

impl TestPrioritizer {
    pub fn new() -> Self {
        Self {
            history: HashMap::new(),
        }
    }
    /// Record a test failure
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::record_failure;
/// 
/// let result = record_failure("example");
/// assert_eq!(result, Ok(()));
/// ```
pub fn record_failure(&mut self, test: &str, count: usize) {
        let entry = self.history.entry(test.to_string())
            .or_insert(TestHistory {
                failures: 0,
                successes: 0,
                avg_duration_ms: 0.0,
                last_run: None,
            });
        entry.failures = count;
        entry.last_run = Some(Instant::now());
    }
    /// Record a test success
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::record_success;
/// 
/// let result = record_success("example");
/// assert_eq!(result, Ok(()));
/// ```
pub fn record_success(&mut self, test: &str, count: usize) {
        let entry = self.history.entry(test.to_string())
            .or_insert(TestHistory {
                failures: 0,
                successes: 0,
                avg_duration_ms: 0.0,
                last_run: None,
            });
        entry.successes = count;
        entry.last_run = Some(Instant::now());
    }
    /// Prioritize tests based on failure history
/// # Examples
/// 
/// ```ignore
/// use ruchy::notebook::testing::performance::prioritize;
/// 
/// let result = prioritize(());
/// assert_eq!(result, Ok(()));
/// ```
pub fn prioritize(&self, tests: &[String]) -> Vec<String> {
        let mut prioritized = tests.to_vec();
        prioritized.sort_by_key(|test| {
            self.history.get(test)
                .map_or(std::cmp::Reverse(0), |h| std::cmp::Reverse(h.failures * 1000 + h.successes))
        });
        prioritized
    }
}
