//! Anomaly Detection Module
//!
//! Provides statistical analysis and anomaly detection for policy evaluation
//! patterns.

pub mod alerts;
pub mod detector;
pub mod rules;
pub mod statistical;

use std::collections::HashMap;
use std::sync::Arc;

use chrono::{DateTime, Datelike, Duration, Timelike, Utc};
use serde::{Deserialize, Serialize};

use crate::config::AnomalyConfig;
use crate::error::{ControlCenterError, Result};
use crate::storage::{PolicyEvaluationEvent, PolicyStorage};

/// Anomaly detection result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnomalyResult {
    pub id: String,
    pub anomaly_type: AnomalyType,
    pub severity: AnomalySeverity,
    pub confidence: f64, // 0.0 to 1.0
    pub description: String,
    pub detected_at: DateTime<Utc>,
    pub affected_entities: Vec<String>,
    pub baseline_period: BaselinePeriod,
    pub statistical_evidence: StatisticalEvidence,
    pub recommended_actions: Vec<String>,
}

/// Types of anomalies detected
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AnomalyType {
    UnusualAccessPattern,
    PolicyViolationSpike,
    PermissionEscalation,
    TimeBasedAnomaly,
    GeographicAnomaly,
    VolumeAnomaly,
    FrequencyAnomaly,
    BehavioralAnomaly,
}

/// Severity levels for anomalies
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AnomalySeverity {
    Low,
    Medium,
    High,
    Critical,
}

/// Baseline period for comparison
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BaselinePeriod {
    pub start: DateTime<Utc>,
    pub end: DateTime<Utc>,
    pub sample_size: usize,
    pub period_type: BaselinePeriodType,
}

/// Types of baseline periods
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BaselinePeriodType {
    Rolling,  // Rolling window
    Fixed,    // Fixed historical period
    Adaptive, // Adaptive based on data patterns
}

/// Statistical evidence supporting the anomaly
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatisticalEvidence {
    pub method: StatisticalMethod,
    pub score: f64,
    pub threshold: f64,
    pub p_value: Option<f64>,
    pub z_score: Option<f64>,
    pub iqr_factor: Option<f64>,
    pub isolation_score: Option<f64>,
    pub baseline_stats: BaselineStatistics,
    pub observed_stats: ObservedStatistics,
}

/// Statistical methods used for detection
#[derive(Debug, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum StatisticalMethod {
    ZScore,
    InterquartileRange,
    IsolationForest,
    MovingAverage,
    SeasonalDecomposition,
    KolmogorovSmirnov,
    Ensemble, // Combination of multiple methods
}

/// Baseline statistical measurements
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BaselineStatistics {
    pub mean: f64,
    pub median: f64,
    pub std_deviation: f64,
    pub percentile_95: f64,
    pub percentile_99: f64,
    pub min: f64,
    pub max: f64,
    pub count: usize,
}

/// Observed statistical measurements
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ObservedStatistics {
    pub value: f64,
    pub count: usize,
    pub time_window: Duration,
    pub percentile_rank: f64,
}

/// Anomaly detection configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetectionConfig {
    pub enabled_methods: Vec<StatisticalMethod>,
    pub thresholds: HashMap<StatisticalMethod, f64>,
    pub baseline_window_hours: u64,
    pub detection_window_minutes: u64,
    pub minimum_samples: usize,
    pub confidence_threshold: f64,
}

impl Default for DetectionConfig {
    fn default() -> Self {
        let mut thresholds = HashMap::new();
        thresholds.insert(StatisticalMethod::ZScore, 3.0);
        thresholds.insert(StatisticalMethod::InterquartileRange, 1.5);
        thresholds.insert(StatisticalMethod::IsolationForest, 0.6);

        Self {
            enabled_methods: vec![
                StatisticalMethod::ZScore,
                StatisticalMethod::InterquartileRange,
                StatisticalMethod::IsolationForest,
            ],
            thresholds,
            baseline_window_hours: 24,
            detection_window_minutes: 60,
            minimum_samples: 30,
            confidence_threshold: 0.8,
        }
    }
}

/// Main anomaly detector
pub struct AnomalyDetector {
    config: AnomalyConfig,
    detection_config: DetectionConfig,
    storage: Arc<dyn PolicyStorage>,
    statistical_detector: statistical::StatisticalDetector,
    rules_engine: rules::RulesEngine,
    alert_manager: alerts::AlertManager,
}

impl AnomalyDetector {
    /// Create new anomaly detector
    pub fn new(config: AnomalyConfig, storage: Arc<dyn PolicyStorage>) -> Self {
        let detection_config = DetectionConfig::default();
        let statistical_detector = statistical::StatisticalDetector::new(&detection_config);
        let rules_engine = rules::RulesEngine::new();
        let alert_manager = alerts::AlertManager::new(&config.alert_channels);

        Self {
            config,
            detection_config,
            storage,
            statistical_detector,
            rules_engine,
            alert_manager,
        }
    }

    /// Detect anomalies in policy evaluations
    pub async fn detect_anomalies(&self) -> Result<Vec<AnomalyResult>> {
        if !self.config.enabled {
            return Ok(Vec::new());
        }

        let mut all_anomalies = Vec::new();

        // Get recent policy evaluations
        let recent_evaluations = self.get_recent_evaluations().await?;
        if recent_evaluations.len() < self.detection_config.minimum_samples {
            tracing::info!(
                "Insufficient data for anomaly detection: {} samples",
                recent_evaluations.len()
            );
            return Ok(Vec::new());
        }

        // Get baseline data for comparison
        let baseline_evaluations = self.get_baseline_evaluations().await?;

        // Run different types of anomaly detection
        all_anomalies.extend(
            self.detect_access_pattern_anomalies(&recent_evaluations, &baseline_evaluations)
                .await?,
        );
        all_anomalies.extend(
            self.detect_volume_anomalies(&recent_evaluations, &baseline_evaluations)
                .await?,
        );
        all_anomalies.extend(
            self.detect_temporal_anomalies(&recent_evaluations, &baseline_evaluations)
                .await?,
        );
        all_anomalies.extend(
            self.detect_policy_violation_anomalies(&recent_evaluations, &baseline_evaluations)
                .await?,
        );
        all_anomalies.extend(
            self.detect_geographic_anomalies(&recent_evaluations, &baseline_evaluations)
                .await?,
        );

        // Apply rule-based filtering and enhancement
        let filtered_anomalies = self.rules_engine.apply_rules(&all_anomalies);

        // Send alerts for high-severity anomalies
        self.send_alerts(&filtered_anomalies).await?;

        Ok(filtered_anomalies)
    }

    /// Get recent policy evaluations for analysis
    async fn get_recent_evaluations(&self) -> Result<Vec<PolicyEvaluationEvent>> {
        let limit = 1000; // Adjust based on needs
        self.storage.get_policy_evaluations(None, limit).await
    }

    /// Get baseline evaluations for comparison
    async fn get_baseline_evaluations(&self) -> Result<Vec<PolicyEvaluationEvent>> {
        // For now, use the same recent evaluations as baseline
        // In a real implementation, this would get historical data
        let limit = 5000;
        self.storage.get_policy_evaluations(None, limit).await
    }

    /// Detect unusual access patterns
    async fn detect_access_pattern_anomalies(
        &self,
        recent: &[PolicyEvaluationEvent],
        baseline: &[PolicyEvaluationEvent],
    ) -> Result<Vec<AnomalyResult>> {
        let mut anomalies = Vec::new();

        // Group by principal (user/service)
        let mut principal_counts: HashMap<String, usize> = HashMap::new();
        for event in recent {
            *principal_counts
                .entry(event.principal_id.clone())
                .or_insert(0) += 1;
        }

        // Get baseline principal access patterns
        let mut baseline_counts: HashMap<String, usize> = HashMap::new();
        for event in baseline {
            *baseline_counts
                .entry(event.principal_id.clone())
                .or_insert(0) += 1;
        }

        // Calculate baseline statistics
        let baseline_values: Vec<f64> = baseline_counts.values().map(|&v| v as f64).collect();
        if baseline_values.len() < self.detection_config.minimum_samples {
            return Ok(anomalies);
        }

        let baseline_stats = self
            .statistical_detector
            .calculate_baseline_stats(&baseline_values);

        // Check each principal for anomalies
        for (principal_id, &current_count) in &principal_counts {
            let baseline_count = baseline_counts.get(principal_id).copied().unwrap_or(0) as f64;
            let current_value = current_count as f64;

            // Apply statistical detection methods
            if let Some(evidence) = self.statistical_detector.detect_anomaly(
                current_value,
                &baseline_stats,
                &StatisticalMethod::ZScore,
            ) {
                if evidence.score >= self.config.detection_threshold {
                    anomalies.push(AnomalyResult {
                        id: uuid::Uuid::new_v4().to_string(),
                        anomaly_type: AnomalyType::UnusualAccessPattern,
                        severity: self.calculate_severity(evidence.score),
                        confidence: evidence.score,
                        description: format!(
                            "Unusual access pattern detected for principal '{}': {} accesses \
                             (baseline avg: {:.1})",
                            principal_id, current_count, baseline_count
                        ),
                        detected_at: Utc::now(),
                        affected_entities: vec![principal_id.clone()],
                        baseline_period: BaselinePeriod {
                            start: Utc::now()
                                - Duration::hours(self.config.baseline_window_hours as i64),
                            end: Utc::now(),
                            sample_size: baseline_values.len(),
                            period_type: BaselinePeriodType::Rolling,
                        },
                        statistical_evidence: evidence,
                        recommended_actions: vec![
                            "Review principal access logs".to_string(),
                            "Verify principal authorization".to_string(),
                            "Check for compromised credentials".to_string(),
                        ],
                    });
                }
            }
        }

        Ok(anomalies)
    }

    /// Detect volume-based anomalies
    async fn detect_volume_anomalies(
        &self,
        recent: &[PolicyEvaluationEvent],
        baseline: &[PolicyEvaluationEvent],
    ) -> Result<Vec<AnomalyResult>> {
        let mut anomalies = Vec::new();

        let recent_count = recent.len() as f64;
        let baseline_count = baseline.len() as f64;

        // Simple volume comparison
        if baseline_count > 0.0 {
            let volume_ratio = recent_count / baseline_count;

            // Check for significant volume changes
            if !(0.5..=2.0).contains(&volume_ratio) {
                let severity = if !(0.3..=3.0).contains(&volume_ratio) {
                    AnomalySeverity::High
                } else {
                    AnomalySeverity::Medium
                };

                anomalies.push(AnomalyResult {
                    id: uuid::Uuid::new_v4().to_string(),
                    anomaly_type: AnomalyType::VolumeAnomaly,
                    severity,
                    confidence: (volume_ratio - 1.0).abs().min(1.0),
                    description: format!(
                        "Significant volume change detected: {} recent evaluations vs {} baseline \
                         (ratio: {:.2})",
                        recent_count, baseline_count, volume_ratio
                    ),
                    detected_at: Utc::now(),
                    affected_entities: vec!["system".to_string()],
                    baseline_period: BaselinePeriod {
                        start: Utc::now()
                            - Duration::hours(self.config.baseline_window_hours as i64),
                        end: Utc::now(),
                        sample_size: baseline.len(),
                        period_type: BaselinePeriodType::Rolling,
                    },
                    statistical_evidence: StatisticalEvidence {
                        method: StatisticalMethod::MovingAverage,
                        score: volume_ratio,
                        threshold: 2.0,
                        p_value: None,
                        z_score: None,
                        iqr_factor: None,
                        isolation_score: None,
                        baseline_stats: BaselineStatistics {
                            mean: baseline_count,
                            median: baseline_count,
                            std_deviation: 0.0,
                            percentile_95: baseline_count * 1.2,
                            percentile_99: baseline_count * 1.5,
                            min: 0.0,
                            max: baseline_count * 2.0,
                            count: 1,
                        },
                        observed_stats: ObservedStatistics {
                            value: recent_count,
                            count: 1,
                            time_window: Duration::hours(1),
                            percentile_rank: if volume_ratio > 1.0 { 95.0 } else { 5.0 },
                        },
                    },
                    recommended_actions: if volume_ratio > 1.0 {
                        vec![
                            "Investigate cause of increased activity".to_string(),
                            "Check for potential security incidents".to_string(),
                            "Review system capacity".to_string(),
                        ]
                    } else {
                        vec![
                            "Investigate cause of decreased activity".to_string(),
                            "Check for system issues".to_string(),
                            "Verify monitoring systems".to_string(),
                        ]
                    },
                });
            }
        }

        Ok(anomalies)
    }

    /// Detect temporal (time-based) anomalies
    async fn detect_temporal_anomalies(
        &self,
        recent: &[PolicyEvaluationEvent],
        _baseline: &[PolicyEvaluationEvent],
    ) -> Result<Vec<AnomalyResult>> {
        let mut anomalies = Vec::new();

        // Group by hour of day
        let mut hourly_counts = [0usize; 24];
        for event in recent {
            let hour = event.timestamp.hour() as usize;
            if hour < 24 {
                hourly_counts[hour] += 1;
            }
        }

        // Detect unusual activity outside business hours (assuming 9 AM - 5 PM)
        let business_hours_total: usize = hourly_counts[9..17].iter().sum();
        let after_hours_total: usize = hourly_counts.iter().sum::<usize>() - business_hours_total;

        if business_hours_total > 0 {
            let after_hours_ratio = after_hours_total as f64 / business_hours_total as f64;

            // If after-hours activity is more than 50% of business hours activity
            if after_hours_ratio > 0.5 {
                anomalies.push(AnomalyResult {
                    id: uuid::Uuid::new_v4().to_string(),
                    anomaly_type: AnomalyType::TimeBasedAnomaly,
                    severity: if after_hours_ratio > 1.0 {
                        AnomalySeverity::Medium
                    } else {
                        AnomalySeverity::Low
                    },
                    confidence: (after_hours_ratio / 2.0).min(1.0),
                    description: format!(
                        "Unusual after-hours activity detected: {} after-hours vs {} \
                         business-hours evaluations (ratio: {:.2})",
                        after_hours_total, business_hours_total, after_hours_ratio
                    ),
                    detected_at: Utc::now(),
                    affected_entities: vec!["temporal_pattern".to_string()],
                    baseline_period: BaselinePeriod {
                        start: Utc::now() - Duration::hours(24),
                        end: Utc::now(),
                        sample_size: recent.len(),
                        period_type: BaselinePeriodType::Rolling,
                    },
                    statistical_evidence: StatisticalEvidence {
                        method: StatisticalMethod::MovingAverage,
                        score: after_hours_ratio,
                        threshold: 0.5,
                        p_value: None,
                        z_score: None,
                        iqr_factor: None,
                        isolation_score: None,
                        baseline_stats: BaselineStatistics {
                            mean: 0.3, // Expected ratio
                            median: 0.3,
                            std_deviation: 0.2,
                            percentile_95: 0.5,
                            percentile_99: 0.8,
                            min: 0.0,
                            max: 2.0,
                            count: 1,
                        },
                        observed_stats: ObservedStatistics {
                            value: after_hours_ratio,
                            count: 1,
                            time_window: Duration::hours(24),
                            percentile_rank: if after_hours_ratio > 0.5 { 90.0 } else { 10.0 },
                        },
                    },
                    recommended_actions: vec![
                        "Review after-hours access logs".to_string(),
                        "Check for automated processes".to_string(),
                        "Verify legitimate business need".to_string(),
                    ],
                });
            }
        }

        Ok(anomalies)
    }

    /// Detect policy violation spikes
    async fn detect_policy_violation_anomalies(
        &self,
        recent: &[PolicyEvaluationEvent],
        baseline: &[PolicyEvaluationEvent],
    ) -> Result<Vec<AnomalyResult>> {
        let mut anomalies = Vec::new();

        let recent_denials = recent.iter().filter(|e| e.decision == "Deny").count();
        let baseline_denials = baseline.iter().filter(|e| e.decision == "Deny").count();

        let recent_total = recent.len();
        let baseline_total = baseline.len();

        if recent_total > 0 && baseline_total > 0 {
            let recent_denial_rate = recent_denials as f64 / recent_total as f64;
            let baseline_denial_rate = baseline_denials as f64 / baseline_total as f64;

            // Check for significant increase in denial rate
            if recent_denial_rate > baseline_denial_rate * 2.0 && recent_denial_rate > 0.1 {
                anomalies.push(AnomalyResult {
                    id: uuid::Uuid::new_v4().to_string(),
                    anomaly_type: AnomalyType::PolicyViolationSpike,
                    severity: if recent_denial_rate > 0.3 {
                        AnomalySeverity::High
                    } else if recent_denial_rate > 0.2 {
                        AnomalySeverity::Medium
                    } else {
                        AnomalySeverity::Low
                    },
                    confidence: ((recent_denial_rate / baseline_denial_rate - 1.0) / 2.0).min(1.0),
                    description: format!(
                        "Policy violation spike detected: {:.1}% denial rate (baseline: {:.1}%)",
                        recent_denial_rate * 100.0,
                        baseline_denial_rate * 100.0
                    ),
                    detected_at: Utc::now(),
                    affected_entities: vec!["policy_violations".to_string()],
                    baseline_period: BaselinePeriod {
                        start: Utc::now()
                            - Duration::hours(self.config.baseline_window_hours as i64),
                        end: Utc::now(),
                        sample_size: baseline_total,
                        period_type: BaselinePeriodType::Rolling,
                    },
                    statistical_evidence: StatisticalEvidence {
                        method: StatisticalMethod::MovingAverage,
                        score: recent_denial_rate / baseline_denial_rate,
                        threshold: 2.0,
                        p_value: None,
                        z_score: None,
                        iqr_factor: None,
                        isolation_score: None,
                        baseline_stats: BaselineStatistics {
                            mean: baseline_denial_rate,
                            median: baseline_denial_rate,
                            std_deviation: 0.05,
                            percentile_95: baseline_denial_rate * 1.5,
                            percentile_99: baseline_denial_rate * 2.0,
                            min: 0.0,
                            max: 1.0,
                            count: 1,
                        },
                        observed_stats: ObservedStatistics {
                            value: recent_denial_rate,
                            count: recent_denials,
                            time_window: Duration::hours(1),
                            percentile_rank: 95.0,
                        },
                    },
                    recommended_actions: vec![
                        "Investigate cause of policy violations".to_string(),
                        "Review recent policy changes".to_string(),
                        "Check for security incidents".to_string(),
                        "Analyze violation patterns".to_string(),
                    ],
                });
            }
        }

        Ok(anomalies)
    }

    /// Detect geographic anomalies
    async fn detect_geographic_anomalies(
        &self,
        recent: &[PolicyEvaluationEvent],
        baseline: &[PolicyEvaluationEvent],
    ) -> Result<Vec<AnomalyResult>> {
        let mut anomalies = Vec::new();

        // Extract geographic information from context
        let mut recent_countries: HashMap<String, usize> = HashMap::new();
        let mut baseline_countries: HashMap<String, usize> = HashMap::new();

        for event in recent {
            let Ok(context) = serde_json::from_value::<serde_json::Value>(event.context.clone())
            else {
                continue;
            };
            let Some(geo) = context.get("geo") else {
                continue;
            };
            let Some(country) = geo.get("country").and_then(|c| c.as_str()) else {
                continue;
            };
            *recent_countries.entry(country.to_string()).or_insert(0) += 1;
        }

        for event in baseline {
            let Ok(context) = serde_json::from_value::<serde_json::Value>(event.context.clone())
            else {
                continue;
            };
            let Some(geo) = context.get("geo") else {
                continue;
            };
            let Some(country) = geo.get("country").and_then(|c| c.as_str()) else {
                continue;
            };
            *baseline_countries.entry(country.to_string()).or_insert(0) += 1;
        }

        // Look for new countries not seen in baseline
        for (country, &count) in &recent_countries {
            if !baseline_countries.contains_key(country) && count > 5 {
                anomalies.push(AnomalyResult {
                    id: uuid::Uuid::new_v4().to_string(),
                    anomaly_type: AnomalyType::GeographicAnomaly,
                    severity: AnomalySeverity::Medium,
                    confidence: 0.8,
                    description: format!(
                        "New geographic location detected: {} accesses from {}",
                        count, country
                    ),
                    detected_at: Utc::now(),
                    affected_entities: vec![country.clone()],
                    baseline_period: BaselinePeriod {
                        start: Utc::now()
                            - Duration::hours(self.config.baseline_window_hours as i64),
                        end: Utc::now(),
                        sample_size: baseline.len(),
                        period_type: BaselinePeriodType::Rolling,
                    },
                    statistical_evidence: StatisticalEvidence {
                        method: StatisticalMethod::MovingAverage,
                        score: count as f64,
                        threshold: 5.0,
                        p_value: None,
                        z_score: None,
                        iqr_factor: None,
                        isolation_score: None,
                        baseline_stats: BaselineStatistics {
                            mean: 0.0,
                            median: 0.0,
                            std_deviation: 0.0,
                            percentile_95: 0.0,
                            percentile_99: 0.0,
                            min: 0.0,
                            max: 0.0,
                            count: 0,
                        },
                        observed_stats: ObservedStatistics {
                            value: count as f64,
                            count,
                            time_window: Duration::hours(1),
                            percentile_rank: 100.0,
                        },
                    },
                    recommended_actions: vec![
                        "Verify legitimate business need for new geographic access".to_string(),
                        "Review VPN and travel policies".to_string(),
                        "Check for potential account compromise".to_string(),
                    ],
                });
            }
        }

        Ok(anomalies)
    }

    /// Calculate severity based on confidence score
    fn calculate_severity(&self, confidence: f64) -> AnomalySeverity {
        match confidence {
            c if c >= 0.9 => AnomalySeverity::Critical,
            c if c >= 0.8 => AnomalySeverity::High,
            c if c >= 0.6 => AnomalySeverity::Medium,
            _ => AnomalySeverity::Low,
        }
    }

    /// Send alerts for detected anomalies
    async fn send_alerts(&self, anomalies: &[AnomalyResult]) -> Result<()> {
        for anomaly in anomalies {
            if matches!(
                anomaly.severity,
                AnomalySeverity::High | AnomalySeverity::Critical
            ) {
                self.alert_manager.send_alert(anomaly).await?;
            }
        }
        Ok(())
    }

    /// Get anomaly detection statistics
    pub async fn get_detection_statistics(&self) -> Result<DetectionStatistics> {
        // This would typically query historical anomaly data
        Ok(DetectionStatistics {
            total_detections: 0,
            false_positives: 0,
            true_positives: 0,
            precision: 0.0,
            recall: 0.0,
            f1_score: 0.0,
        })
    }
}

/// Detection performance statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetectionStatistics {
    pub total_detections: usize,
    pub false_positives: usize,
    pub true_positives: usize,
    pub precision: f64,
    pub recall: f64,
    pub f1_score: f64,
}
