//! Performance Regression Detection
//!
//! Automatically detects performance regressions by comparing
//! test results against established baselines.

use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::Path;
use std::time::Duration;

/// Performance baseline metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BaselineMetrics {
    pub version: String,
    pub description: String,
    pub baselines: Baselines,
    pub system_requirements: SystemRequirements,
    pub regression_thresholds: RegressionThresholds,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Baselines {
    pub batch_creation: BatchCreationBaseline,
    pub streaming: StreamingBaseline,
    pub state_transitions: StateTransitionBaseline,
    pub file_discovery: FileDiscoveryBaseline,
    pub memory_leak: MemoryLeakBaseline,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchCreationBaseline {
    pub description: String,
    pub p95_latency_ms: f64,
    pub throughput_ops_per_sec: f64,
    pub max_concurrent: u64,
    pub acceptable_failure_rate_percent: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StreamingBaseline {
    pub description: String,
    pub throughput_msg_per_sec_per_stream: f64,
    pub max_concurrent_streams: u64,
    pub backpressure_handling: bool,
    pub message_loss_rate_percent: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StateTransitionBaseline {
    pub description: String,
    pub p95_latency_ms: f64,
    pub throughput_updates_per_sec: f64,
    pub deadlock_free: bool,
    pub rollback_correctness: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileDiscoveryBaseline {
    pub description: String,
    pub discovery_time_100_files_ms: f64,
    pub discovery_time_10k_files_ms: f64,
    pub concurrent_requests: u64,
    pub cache_invalidation: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryLeakBaseline {
    pub description: String,
    pub max_rss_growth_percent_per_hour: f64,
    pub max_leak_after_cleanup_percent: f64,
    pub stream_cleanup_leak_percent: f64,
    pub connection_leak_percent: f64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemRequirements {
    pub min_cpu_cores: u32,
    pub min_ram_gb: u32,
    pub min_disk_gb: u32,
    pub postgres_connections: u32,
    pub recommended_environment: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RegressionThresholds {
    pub latency_p95_degradation_percent: f64,
    pub throughput_degradation_percent: f64,
    pub memory_growth_degradation_percent: f64,
    pub failure_rate_increase_percent: f64,
}

/// Regression detection result
#[derive(Debug, Clone)]
pub struct RegressionResult {
    pub test_name: String,
    pub has_regression: bool,
    pub violations: Vec<RegressionViolation>,
}

#[derive(Debug, Clone)]
pub struct RegressionViolation {
    pub metric: String,
    pub baseline_value: f64,
    pub actual_value: f64,
    pub degradation_percent: f64,
    pub threshold_percent: f64,
}

/// Regression detector
pub struct RegressionDetector {
    baselines: BaselineMetrics,
}

impl RegressionDetector {
    /// Load baselines from file
    pub fn load_from_file(path: &Path) -> Result<Self> {
        let content = fs::read_to_string(path)
            .context("Failed to read baseline metrics file")?;
        let baselines: BaselineMetrics = serde_json::from_str(&content)
            .context("Failed to parse baseline metrics JSON")?;

        Ok(Self { baselines })
    }

    /// Check batch creation performance
    pub fn check_batch_creation(
        &self,
        p95_latency: Duration,
        throughput: f64,
        failure_rate_percent: f64,
    ) -> RegressionResult {
        let mut violations = Vec::new();

        let p95_ms = p95_latency.as_secs_f64() * 1000.0;
        let baseline = &self.baselines.baselines.batch_creation;

        // Check P95 latency
        if p95_ms > baseline.p95_latency_ms {
            let degradation = ((p95_ms - baseline.p95_latency_ms) / baseline.p95_latency_ms) * 100.0;
            if degradation > self.baselines.regression_thresholds.latency_p95_degradation_percent {
                violations.push(RegressionViolation {
                    metric: "p95_latency_ms".to_string(),
                    baseline_value: baseline.p95_latency_ms,
                    actual_value: p95_ms,
                    degradation_percent: degradation,
                    threshold_percent: self.baselines.regression_thresholds.latency_p95_degradation_percent,
                });
            }
        }

        // Check throughput
        if throughput < baseline.throughput_ops_per_sec {
            let degradation = ((baseline.throughput_ops_per_sec - throughput) / baseline.throughput_ops_per_sec) * 100.0;
            if degradation > self.baselines.regression_thresholds.throughput_degradation_percent {
                violations.push(RegressionViolation {
                    metric: "throughput_ops_per_sec".to_string(),
                    baseline_value: baseline.throughput_ops_per_sec,
                    actual_value: throughput,
                    degradation_percent: degradation,
                    threshold_percent: self.baselines.regression_thresholds.throughput_degradation_percent,
                });
            }
        }

        // Check failure rate
        if failure_rate_percent > baseline.acceptable_failure_rate_percent {
            let degradation = ((failure_rate_percent - baseline.acceptable_failure_rate_percent)
                / baseline.acceptable_failure_rate_percent) * 100.0;
            if degradation > self.baselines.regression_thresholds.failure_rate_increase_percent {
                violations.push(RegressionViolation {
                    metric: "failure_rate_percent".to_string(),
                    baseline_value: baseline.acceptable_failure_rate_percent,
                    actual_value: failure_rate_percent,
                    degradation_percent: degradation,
                    threshold_percent: self.baselines.regression_thresholds.failure_rate_increase_percent,
                });
            }
        }

        RegressionResult {
            test_name: "batch_creation".to_string(),
            has_regression: !violations.is_empty(),
            violations,
        }
    }

    /// Check memory leak performance
    pub fn check_memory_leak(
        &self,
        rss_growth_percent: f64,
        cleanup_leak_percent: f64,
    ) -> RegressionResult {
        let mut violations = Vec::new();
        let baseline = &self.baselines.baselines.memory_leak;

        // Check RSS growth
        if rss_growth_percent > baseline.max_rss_growth_percent_per_hour {
            let degradation = ((rss_growth_percent - baseline.max_rss_growth_percent_per_hour)
                / baseline.max_rss_growth_percent_per_hour) * 100.0;
            if degradation > self.baselines.regression_thresholds.memory_growth_degradation_percent {
                violations.push(RegressionViolation {
                    metric: "rss_growth_percent_per_hour".to_string(),
                    baseline_value: baseline.max_rss_growth_percent_per_hour,
                    actual_value: rss_growth_percent,
                    degradation_percent: degradation,
                    threshold_percent: self.baselines.regression_thresholds.memory_growth_degradation_percent,
                });
            }
        }

        // Check cleanup leak
        if cleanup_leak_percent > baseline.max_leak_after_cleanup_percent {
            let degradation = ((cleanup_leak_percent - baseline.max_leak_after_cleanup_percent)
                / baseline.max_leak_after_cleanup_percent) * 100.0;
            if degradation > self.baselines.regression_thresholds.memory_growth_degradation_percent {
                violations.push(RegressionViolation {
                    metric: "cleanup_leak_percent".to_string(),
                    baseline_value: baseline.max_leak_after_cleanup_percent,
                    actual_value: cleanup_leak_percent,
                    degradation_percent: degradation,
                    threshold_percent: self.baselines.regression_thresholds.memory_growth_degradation_percent,
                });
            }
        }

        RegressionResult {
            test_name: "memory_leak".to_string(),
            has_regression: !violations.is_empty(),
            violations,
        }
    }

    /// Print regression report
    pub fn print_report(&self, result: &RegressionResult) {
        println!("\n=== Regression Detection Report: {} ===", result.test_name);

        if !result.has_regression {
            println!("✓ No performance regressions detected");
            return;
        }

        println!("✗ Performance regressions detected:");
        for violation in &result.violations {
            println!("\n  Metric: {}", violation.metric);
            println!("    Baseline:    {:.2}", violation.baseline_value);
            println!("    Actual:      {:.2}", violation.actual_value);
            println!("    Degradation: {:.2}% (threshold: {:.2}%)",
                violation.degradation_percent, violation.threshold_percent);
        }
    }
}

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

    #[test]
    fn test_load_baselines() {
        let baseline_path = Path::new(env!("CARGO_MANIFEST_DIR"))
            .join("tests/baselines/baseline_metrics.json");

        let detector = RegressionDetector::load_from_file(&baseline_path);
        assert!(detector.is_ok(), "Failed to load baseline metrics");
    }

    #[test]
    fn test_batch_creation_no_regression() {
        let baseline_path = Path::new(env!("CARGO_MANIFEST_DIR"))
            .join("tests/baselines/baseline_metrics.json");
        let detector = RegressionDetector::load_from_file(&baseline_path).unwrap();

        let result = detector.check_batch_creation(
            Duration::from_millis(8),  // Better than baseline (10ms)
            1200.0,                      // Better than baseline (1000 ops/sec)
            0.5,                         // Better than baseline (1%)
        );

        assert!(!result.has_regression);
        assert!(result.violations.is_empty());
    }

    #[test]
    fn test_batch_creation_with_regression() {
        let baseline_path = Path::new(env!("CARGO_MANIFEST_DIR"))
            .join("tests/baselines/baseline_metrics.json");
        let detector = RegressionDetector::load_from_file(&baseline_path).unwrap();

        let result = detector.check_batch_creation(
            Duration::from_millis(15),  // 50% worse than baseline (10ms)
            500.0,                       // 50% worse than baseline (1000 ops/sec)
            2.0,                         // 100% worse than baseline (1%)
        );

        assert!(result.has_regression);
        assert!(!result.violations.is_empty());
    }
}
