//! Navigation Tracking System
//!
//! This module provides comprehensive tracking of navigation events, route
//! resolution, and performance metrics to help debug routing issues and
//! optimize performance.

use std::collections::HashMap;
use std::sync::{Arc, Mutex, OnceLock};
#[cfg(not(target_arch = "wasm32"))]
use std::time::{SystemTime, UNIX_EPOCH};

use serde::{Deserialize, Serialize};

/// Global navigation tracker instance
static NAVIGATION_TRACKER: OnceLock<Arc<Mutex<NavigationTracker>>> = OnceLock::new();

/// Initialize the navigation tracker
pub fn init_navigation_tracker(config: NavigationTrackingConfig) {
    let tracker = NavigationTracker::new(config);
    let _ = NAVIGATION_TRACKER.set(Arc::new(Mutex::new(tracker)));
}

/// Get the global navigation tracker instance
pub fn get_tracker() -> Option<Arc<Mutex<NavigationTracker>>> {
    NAVIGATION_TRACKER.get().cloned()
}

/// Navigation tracking configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NavigationTrackingConfig {
    pub enabled: bool,
    pub log_path: Option<String>,
    pub max_events_in_memory: usize,
    pub slow_threshold_ms: u64,
    pub enable_console_debug: bool,
}

impl Default for NavigationTrackingConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            log_path: Some("logs/navigation.jsonl".to_string()),
            max_events_in_memory: 1000,
            slow_threshold_ms: 10,
            enable_console_debug: cfg!(debug_assertions),
        }
    }
}

/// Types of navigation events we track
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum NavigationEventType {
    /// Route resolution using get_localized_route
    RouteResolution,
    /// Language and path extraction
    LanguageExtraction,
    /// Category/tag URL generation
    CategoryUrlGeneration,
    /// Filter API URL construction
    FilterApiUrl,
    /// User navigation click
    NavigationClick,
    /// Language switch event
    LanguageSwitch,
    /// Cache hit/miss
    CacheEvent,
}

/// Individual navigation event
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NavigationEvent {
    pub session_id: String,
    pub timestamp: u64,
    pub event_type: NavigationEventType,
    pub source_path: String,
    pub target_path: Option<String>,
    pub current_lang: String,
    pub target_lang: Option<String>,
    pub resolution_time_ms: u64,
    pub cache_hit: bool,
    pub component: Option<String>, // Which component triggered this
    pub errors: Vec<String>,
    pub fallback_used: bool,
    pub additional_data: HashMap<String, String>,
}

/// Session statistics
#[derive(Debug, Default, Clone)]
pub struct SessionStats {
    pub total_events: u64,
    pub route_resolutions: u64,
    pub cache_hits: u64,
    pub cache_misses: u64,
    pub errors: u64,
    pub slow_resolutions: u64,
    pub avg_resolution_time_ms: f64,
}

/// Main navigation tracker
#[derive(Debug)]
pub struct NavigationTracker {
    pub config: NavigationTrackingConfig,
    pub session_id: String,
    pub events: Vec<NavigationEvent>,
    pub stats: SessionStats,
    #[cfg(not(target_arch = "wasm32"))]
    pub start_time: SystemTime,
}

impl NavigationTracker {
    pub fn new(config: NavigationTrackingConfig) -> Self {
        let session_id = generate_session_id();

        Self {
            config,
            session_id,
            events: Vec::new(),
            stats: SessionStats::default(),
            #[cfg(not(target_arch = "wasm32"))]
            start_time: SystemTime::now(),
        }
    }

    /// Track a navigation event
    pub fn track_event(&mut self, mut event: NavigationEvent) {
        if !self.config.enabled {
            return;
        }

        // Set session ID if not provided
        if event.session_id.is_empty() {
            event.session_id = self.session_id.clone();
        }

        // Update statistics
        self.update_stats(&event);

        // Console debug output
        if self.config.enable_console_debug {
            self.log_to_console(&event);
        }

        // Store in memory (with size limits)
        if self.events.len() >= self.config.max_events_in_memory {
            // Remove oldest events
            self.events.remove(0);
        }
        self.events.push(event.clone());

        // JSONL file logging (server-side only)
        #[cfg(not(target_arch = "wasm32"))]
        if let Some(ref log_path) = self.config.log_path {
            self.log_to_file(&event, log_path);
        }
    }

    /// Update session statistics
    fn update_stats(&mut self, event: &NavigationEvent) {
        self.stats.total_events += 1;

        match event.event_type {
            NavigationEventType::RouteResolution => {
                self.stats.route_resolutions += 1;
            }
            NavigationEventType::CacheEvent => {
                if event.cache_hit {
                    self.stats.cache_hits += 1;
                } else {
                    self.stats.cache_misses += 1;
                }
            }
            _ => {}
        }

        if !event.errors.is_empty() {
            self.stats.errors += 1;
        }

        if event.resolution_time_ms > self.config.slow_threshold_ms {
            self.stats.slow_resolutions += 1;
        }

        // Update average resolution time
        let total_time = self.stats.avg_resolution_time_ms * (self.stats.total_events - 1) as f64;
        self.stats.avg_resolution_time_ms =
            (total_time + event.resolution_time_ms as f64) / self.stats.total_events as f64;
    }

    /// Log event to console for debugging
    fn log_to_console(&self, event: &NavigationEvent) {
        let event_symbol = match event.event_type {
            NavigationEventType::RouteResolution => "🔄",
            NavigationEventType::LanguageExtraction => "🌐",
            NavigationEventType::CategoryUrlGeneration => "🏷️",
            NavigationEventType::FilterApiUrl => "📡",
            NavigationEventType::NavigationClick => "👆",
            NavigationEventType::LanguageSwitch => "🔀",
            NavigationEventType::CacheEvent => "💾",
        };

        let status_symbol = if !event.errors.is_empty() {
            "❌"
        } else if event.fallback_used {
            "⚠️"
        } else if event.cache_hit {
            "✅💾"
        } else {
            "✅"
        };

        #[cfg(target_arch = "wasm32")]
        {
            let message = if let Some(ref target) = event.target_path {
                format!(
                    "{} NAV TRACK {}: {} -> {} [{}ms] {}",
                    event_symbol,
                    format!("{:?}", event.event_type),
                    event.source_path,
                    target,
                    event.resolution_time_ms,
                    status_symbol
                )
            } else {
                format!(
                    "{} NAV TRACK {}: {} [{}ms] {}",
                    event_symbol,
                    format!("{:?}", event.event_type),
                    event.source_path,
                    event.resolution_time_ms,
                    status_symbol
                )
            };
            web_sys::console::log_1(&message.into());

            // Log errors separately
            for error in &event.errors {
                web_sys::console::log_1(&format!("   ❌ Error: {}", error).into());
            }
        }

        #[cfg(not(target_arch = "wasm32"))]
        {
            if let Some(ref target) = event.target_path {
                tracing::debug!(
                    "{} NAV TRACK {:?}: {} -> {} [{}ms] {} - lang: {} -> {:?}",
                    event_symbol,
                    event.event_type,
                    event.source_path,
                    target,
                    event.resolution_time_ms,
                    status_symbol,
                    event.current_lang,
                    event.target_lang
                );
            } else {
                tracing::debug!(
                    "{} NAV TRACK {:?}: {} [{}ms] {} - lang: {}",
                    event_symbol,
                    event.event_type,
                    event.source_path,
                    event.resolution_time_ms,
                    status_symbol,
                    event.current_lang
                );
            }

            // Log errors
            for error in &event.errors {
                tracing::error!("NAV TRACK Error: {}", error);
            }
        }
    }

    /// Log event to JSONL file (server-side only)
    #[cfg(not(target_arch = "wasm32"))]
    fn log_to_file(&self, event: &NavigationEvent, log_path: &str) {
        use std::fs::OpenOptions;
        use std::io::Write;

        // Ensure log directory exists
        if let Some(parent) = std::path::Path::new(log_path).parent() {
            let _ = std::fs::create_dir_all(parent);
        }

        // Serialize event to JSON and append to file
        if let Ok(json_line) = serde_json::to_string(event) {
            if let Ok(mut file) = OpenOptions::new().create(true).append(true).open(log_path) {
                if let Err(e) = writeln!(file, "{}", json_line) {
                    tracing::error!("Failed to write navigation event to log file: {}", e);
                }
            } else {
                tracing::error!("Failed to open navigation log file: {}", log_path);
            }
        } else {
            tracing::error!("Failed to serialize navigation event to JSON");
        }
    }

    /// Get session statistics
    pub fn get_stats(&self) -> SessionStats {
        self.stats.clone()
    }

    /// Get recent events (last N events)
    pub fn get_recent_events(&self, limit: usize) -> Vec<NavigationEvent> {
        let start = if self.events.len() > limit {
            self.events.len() - limit
        } else {
            0
        };
        self.events[start..].to_vec()
    }
}

/// Generate a unique session ID
fn generate_session_id() -> String {
    #[cfg(target_arch = "wasm32")]
    {
        // In WASM, use a simple counter-based ID
        use std::sync::atomic::{AtomicU64, Ordering};
        static COUNTER: AtomicU64 = AtomicU64::new(0);
        let count = COUNTER.fetch_add(1, Ordering::SeqCst);
        format!("nav_wasm_{}", count)
    }

    #[cfg(not(target_arch = "wasm32"))]
    {
        let timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap_or_default()
            .as_millis();

        use std::process;
        format!("nav_{}_{}", timestamp, process::id())
    }
}

/// Convenience macro for tracking navigation events
#[macro_export]
macro_rules! track_navigation {
    ($event_type:expr, $source:expr, $current_lang:expr) => {
        $crate::routing::tracker::track_navigation_event(
            $event_type,
            $source.to_string(),
            None,
            $current_lang.to_string(),
            None,
            None,
            std::collections::HashMap::new(),
        )
    };
    ($event_type:expr, $source:expr, $target:expr, $current_lang:expr, $target_lang:expr) => {
        $crate::routing::tracker::track_navigation_event(
            $event_type,
            $source.to_string(),
            Some($target.to_string()),
            $current_lang.to_string(),
            Some($target_lang.to_string()),
            None,
            std::collections::HashMap::new(),
        )
    };
}

/// Helper function to track navigation events
pub fn track_navigation_event(
    event_type: NavigationEventType,
    source_path: String,
    target_path: Option<String>,
    current_lang: String,
    target_lang: Option<String>,
    component: Option<String>,
    additional_data: HashMap<String, String>,
) {
    if let Some(tracker) = get_tracker() {
        if let Ok(mut tracker) = tracker.lock() {
            #[cfg(not(target_arch = "wasm32"))]
            let start_time = SystemTime::now();

            let event = NavigationEvent {
                session_id: String::new(), // Will be set by tracker
                timestamp: {
                    #[cfg(not(target_arch = "wasm32"))]
                    {
                        start_time
                            .duration_since(UNIX_EPOCH)
                            .unwrap_or_default()
                            .as_millis() as u64
                    }
                    #[cfg(target_arch = "wasm32")]
                    {
                        // Use a simple counter for WASM
                        use std::sync::atomic::{AtomicU64, Ordering};
                        static COUNTER: AtomicU64 = AtomicU64::new(0);
                        COUNTER.fetch_add(1, Ordering::SeqCst)
                    }
                },
                event_type,
                source_path,
                target_path,
                current_lang,
                target_lang,
                resolution_time_ms: 0, // Will be updated if we track timing
                cache_hit: false,
                component,
                errors: Vec::new(),
                fallback_used: false,
                additional_data,
            };

            tracker.track_event(event);
        }
    }
}

/// Track route resolution with timing
pub fn track_route_resolution<F, R>(
    source_path: &str,
    current_lang: &str,
    target_lang: Option<&str>,
    component: Option<&str>,
    operation: F,
) -> R
where
    F: FnOnce() -> R,
{
    #[cfg(not(target_arch = "wasm32"))]
    let start_time = SystemTime::now();
    let result = operation();
    #[cfg(not(target_arch = "wasm32"))]
    let elapsed = start_time.elapsed().unwrap_or_default();
    #[cfg(target_arch = "wasm32")]
    let elapsed = std::time::Duration::from_millis(0);

    if let Some(tracker) = get_tracker() {
        if let Ok(mut tracker) = tracker.lock() {
            let event = NavigationEvent {
                session_id: String::new(),
                timestamp: {
                    #[cfg(not(target_arch = "wasm32"))]
                    {
                        start_time
                            .duration_since(UNIX_EPOCH)
                            .unwrap_or_default()
                            .as_millis() as u64
                    }
                    #[cfg(target_arch = "wasm32")]
                    {
                        // Use a simple counter for WASM
                        use std::sync::atomic::{AtomicU64, Ordering};
                        static COUNTER: AtomicU64 = AtomicU64::new(0);
                        COUNTER.fetch_add(1, Ordering::SeqCst)
                    }
                },
                event_type: NavigationEventType::RouteResolution,
                source_path: source_path.to_string(),
                target_path: None, // Will be filled by caller
                current_lang: current_lang.to_string(),
                target_lang: target_lang.map(|s| s.to_string()),
                resolution_time_ms: elapsed.as_millis() as u64,
                cache_hit: false,
                component: component.map(|s| s.to_string()),
                errors: Vec::new(),
                fallback_used: false,
                additional_data: HashMap::new(),
            };

            tracker.track_event(event);
        }
    }

    result
}
