//! Rust Panic Detection and Analysis
//!
//! Detects, analyzes, and reports Rust panics in server logs
//! to provide insights into application stability.

use super::super::{AnalyticsEvent, EventLevel, LogSource, generate_event_id};
use anyhow::Result;
use chrono::Utc;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use tokio::sync::mpsc;

/// Panic information extracted from logs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PanicInfo {
    /// Panic message
    pub message: String,
    /// File and line where panic occurred
    pub location: Option<String>,
    /// Stack trace if available
    pub stack_trace: Vec<String>,
    /// Thread information
    pub thread: Option<String>,
    /// Timestamp when panic occurred
    pub timestamp: chrono::DateTime<chrono::Utc>,
}

/// Panic statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PanicStats {
    /// Total panic count
    pub total_panics: u64,
    /// Recent panics (last 24 hours)
    pub recent_panics: u64,
    /// Most common panic locations
    pub common_locations: Vec<(String, u64)>,
    /// Most common panic messages
    pub common_messages: Vec<(String, u64)>,
    /// Panic frequency trend
    pub trend: PanicTrend,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PanicTrend {
    Increasing,
    Stable,
    Decreasing,
    NoData,
}

/// Panic detector
pub struct PanicDetector {
    panic_regex: Regex,
    location_regex: Regex,
    stack_trace_regex: Regex,
    recent_panics: Arc<Mutex<Vec<PanicInfo>>>,
}

impl PanicDetector {
    /// Create new panic detector
    pub fn new() -> Self {
        // Regex patterns for detecting panics in Rust logs
        let panic_regex = Regex::new(r"thread '.*?' panicked at '(.*?)'").unwrap();
        let location_regex = Regex::new(r"at (.*?):(\d+):(\d+)").unwrap();
        let stack_trace_regex = Regex::new(r"^\s+\d+:\s+(.*)$").unwrap();

        Self {
            panic_regex,
            location_regex,
            stack_trace_regex,
            recent_panics: Arc::new(Mutex::new(Vec::new())),
        }
    }

    /// Start panic monitoring
    pub async fn start_monitoring(&self, sender: mpsc::UnboundedSender<AnalyticsEvent>) -> Result<()> {
        tracing::info!("Starting panic detection...");

        // In a real implementation, this would:
        // 1. Hook into the Rust panic handler
        // 2. Monitor log files for panic patterns
        // 3. Parse stack traces and extract meaningful information

        let panics = Arc::clone(&self.recent_panics);

        tokio::spawn(async move {
            let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(60));

            loop {
                interval.tick().await;

                // Simulate panic detection (in real implementation, this would be event-driven)
                if rand::random::<f64>() < 0.05 { // 5% chance of detecting a panic
                    let panic_info = PanicInfo {
                        message: "index out of bounds: the len is 3 but the index is 5".to_string(),
                        location: Some("src/handlers/content.rs:142:25".to_string()),
                        stack_trace: vec![
                            "core::panicking::panic_fmt".to_string(),
                            "rust_begin_unwind".to_string(),
                            "core::panicking::panic_bounds_check".to_string(),
                            "rustelo_server::handlers::content::get_content".to_string(),
                        ],
                        thread: Some("tokio-runtime-worker".to_string()),
                        timestamp: Utc::now(),
                    };

                    // Store panic info
                    {
                        let mut panics_guard = panics.lock().unwrap();
                        panics_guard.push(panic_info.clone());

                        // Keep only recent panics (last 100)
                        if panics_guard.len() > 100 {
                            panics_guard.remove(0);
                        }
                    }

                    // Create analytics event
                    let event = Self::create_panic_event(panic_info);
                    if let Err(e) = sender.send(event) {
                        tracing::error!("Failed to send panic event: {}", e);
                        break;
                    }
                }
            }
        });

        tracing::info!("Panic detection started");
        Ok(())
    }

    /// Create analytics event from panic info
    fn create_panic_event(panic_info: PanicInfo) -> AnalyticsEvent {
        let mut metadata = HashMap::new();

        if let Some(location) = &panic_info.location {
            metadata.insert("location".to_string(),
                serde_json::Value::String(location.clone()));
        }

        if let Some(thread) = &panic_info.thread {
            metadata.insert("thread".to_string(),
                serde_json::Value::String(thread.clone()));
        }

        metadata.insert("stack_trace".to_string(),
            serde_json::Value::Array(
                panic_info.stack_trace.iter()
                    .map(|s| serde_json::Value::String(s.clone()))
                    .collect()
            ));

        let message = format!(
            "Rust panic detected: {} at {}",
            panic_info.message,
            panic_info.location.as_deref().unwrap_or("unknown location")
        );

        AnalyticsEvent {
            id: generate_event_id(),
            timestamp: panic_info.timestamp,
            source: LogSource::Server,
            event_type: "panic".to_string(),
            session_id: None,
            path: panic_info.location.clone(),
            level: EventLevel::Critical,
            message,
            metadata,
            duration_ms: None,
            errors: vec![panic_info.message],
        }
    }

    /// Parse panic information from log line
    pub fn parse_panic_from_log(&self, log_line: &str) -> Option<PanicInfo> {
        if let Some(captures) = self.panic_regex.captures(log_line) {
            let message = captures.get(1)?.as_str().to_string();

            // Try to extract location
            let location = self.location_regex.captures(log_line)
                .map(|loc_captures| {
                    format!("{}:{}:{}",
                        loc_captures.get(1).unwrap().as_str(),
                        loc_captures.get(2).unwrap().as_str(),
                        loc_captures.get(3).unwrap().as_str()
                    )
                });

            Some(PanicInfo {
                message,
                location,
                stack_trace: Vec::new(), // Would be populated by parsing subsequent lines
                thread: None, // Would be extracted from thread info
                timestamp: Utc::now(),
            })
        } else {
            None
        }
    }

    /// Get panic statistics
    pub fn get_panic_stats(&self) -> PanicStats {
        let panics = self.recent_panics.lock().unwrap();
        let total_panics = panics.len() as u64;

        // Count recent panics (last 24 hours)
        let cutoff = Utc::now() - chrono::Duration::hours(24);
        let recent_panics = panics.iter()
            .filter(|p| p.timestamp > cutoff)
            .count() as u64;

        // Analyze common locations
        let mut location_counts: HashMap<String, u64> = HashMap::new();
        let mut message_counts: HashMap<String, u64> = HashMap::new();

        for panic in panics.iter() {
            if let Some(location) = &panic.location {
                *location_counts.entry(location.clone()).or_insert(0) += 1;
            }
            *message_counts.entry(panic.message.clone()).or_insert(0) += 1;
        }

        let mut common_locations: Vec<(String, u64)> = location_counts.into_iter().collect();
        common_locations.sort_by(|a, b| b.1.cmp(&a.1));
        common_locations.truncate(5);

        let mut common_messages: Vec<(String, u64)> = message_counts.into_iter().collect();
        common_messages.sort_by(|a, b| b.1.cmp(&a.1));
        common_messages.truncate(5);

        // Determine trend (simplified)
        let trend = if recent_panics > total_panics / 2 {
            PanicTrend::Increasing
        } else if recent_panics == 0 {
            PanicTrend::Decreasing
        } else {
            PanicTrend::Stable
        };

        PanicStats {
            total_panics,
            recent_panics,
            common_locations,
            common_messages,
            trend,
        }
    }

    /// Get recent panic details
    pub fn get_recent_panics(&self, limit: usize) -> Vec<PanicInfo> {
        let panics = self.recent_panics.lock().unwrap();
        panics.iter()
            .rev()
            .take(limit)
            .cloned()
            .collect()
    }

    /// Check if panic rate is concerning
    pub fn is_panic_rate_concerning(&self) -> bool {
        let stats = self.get_panic_stats();

        // Consider concerning if:
        // - More than 5 panics in last 24 hours
        // - Increasing trend with recent panics
        stats.recent_panics > 5 ||
        (matches!(stats.trend, PanicTrend::Increasing) && stats.recent_panics > 2)
    }

    /// Generate panic report
    pub fn generate_panic_report(&self) -> String {
        let stats = self.get_panic_stats();

        let mut report = format!(
            "🚨 Panic Analysis Report\n\
             Total Panics: {}\n\
             Recent Panics (24h): {}\n\
             Trend: {:?}\n\n",
            stats.total_panics,
            stats.recent_panics,
            stats.trend
        );

        if !stats.common_locations.is_empty() {
            report.push_str("Most Common Locations:\n");
            for (location, count) in stats.common_locations.iter().take(3) {
                report.push_str(&format!("  {} - {} occurrences\n", location, count));
            }
            report.push('\n');
        }

        if !stats.common_messages.is_empty() {
            report.push_str("Most Common Messages:\n");
            for (message, count) in stats.common_messages.iter().take(3) {
                let truncated = if message.len() > 60 {
                    format!("{}...", &message[..57])
                } else {
                    message.clone()
                };
                report.push_str(&format!("  {} - {} occurrences\n", truncated, count));
            }
        }

        report
    }
}
