//! Analytics Search and Query System
//!
//! Provides powerful search and filtering capabilities across all analytics data:
//! - Full-text search across log messages
//! - Time-range filtering
//! - Source-based filtering
//! - Event type and severity filtering
//! - Cross-log correlation and pattern matching

use super::{AnalyticsEvent, EventLevel, LogSource};
use anyhow::Result;
use chrono::{DateTime, Duration, Utc};
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};

/// Search query structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchQuery {
    /// Text to search for in messages
    pub text: Option<String>,
    /// Regex pattern for advanced matching
    pub regex: Option<String>,
    /// Filter by log sources
    pub sources: Option<Vec<LogSource>>,
    /// Filter by event types
    pub event_types: Option<Vec<String>>,
    /// Filter by severity levels
    pub levels: Option<Vec<EventLevel>>,
    /// Time range filter
    pub time_range: Option<TimeRange>,
    /// Filter by specific paths
    pub paths: Option<Vec<String>>,
    /// Filter by session IDs
    pub session_ids: Option<Vec<String>>,
    /// Include/exclude error events
    pub has_errors: Option<bool>,
    /// Minimum duration filter (milliseconds)
    pub min_duration_ms: Option<u64>,
    /// Maximum duration filter (milliseconds)
    pub max_duration_ms: Option<u64>,
    /// Limit number of results
    pub limit: Option<usize>,
    /// Sort options
    pub sort: Option<SortOptions>,
}

/// Time range for filtering
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeRange {
    pub start: DateTime<Utc>,
    pub end: DateTime<Utc>,
}

impl TimeRange {
    /// Create time range for last N hours
    pub fn last_hours(hours: i64) -> Self {
        let end = Utc::now();
        let start = end - Duration::hours(hours);
        Self { start, end }
    }

    /// Create time range for last N days
    pub fn last_days(days: i64) -> Self {
        let end = Utc::now();
        let start = end - Duration::days(days);
        Self { start, end }
    }

    /// Create time range for today
    pub fn today() -> Self {
        let now = Utc::now();
        let start = now.date_naive().and_hms_opt(0, 0, 0).unwrap().and_utc();
        let end = now.date_naive().and_hms_opt(23, 59, 59).unwrap().and_utc();
        Self { start, end }
    }
}

/// Sort options for search results
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SortOptions {
    pub field: SortField,
    pub direction: SortDirection,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SortField {
    Timestamp,
    Level,
    Source,
    Duration,
    EventType,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SortDirection {
    Ascending,
    Descending,
}

/// Search results
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResults {
    pub events: Vec<AnalyticsEvent>,
    pub total_count: usize,
    pub query_time_ms: u64,
    pub aggregations: SearchAggregations,
}

/// Aggregated statistics from search results
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchAggregations {
    pub sources: HashMap<LogSource, usize>,
    pub event_types: HashMap<String, usize>,
    pub levels: HashMap<EventLevel, usize>,
    pub hourly_distribution: HashMap<u8, usize>, // hour -> count
    pub top_paths: Vec<(String, usize)>,
    pub error_summary: ErrorSummary,
}

/// Error-specific aggregation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorSummary {
    pub total_errors: usize,
    pub unique_error_messages: usize,
    pub top_error_messages: Vec<(String, usize)>,
    pub error_sources: HashMap<LogSource, usize>,
}

/// Analytics search engine
pub struct AnalyticsSearch {
    /// In-memory event storage (for real implementation, this would be a database)
    events: Vec<AnalyticsEvent>,
    /// Compiled regex cache for performance
    regex_cache: HashMap<String, Regex>,
}

impl AnalyticsSearch {
    /// Create new search engine
    pub fn new() -> Self {
        Self {
            events: Vec::new(),
            regex_cache: HashMap::new(),
        }
    }

    /// Add events to search index
    pub fn index_events(&mut self, events: Vec<AnalyticsEvent>) {
        self.events.extend(events);

        // Keep only recent events to prevent memory growth
        // In production, this would be handled by a proper database
        const MAX_EVENTS: usize = 100_000;
        if self.events.len() > MAX_EVENTS {
            let keep_from = self.events.len() - MAX_EVENTS + 1000;
            self.events.drain(0..keep_from);
        }
    }

    /// Execute search query
    pub async fn search(&mut self, query: SearchQuery) -> Result<SearchResults> {
        let start_time = std::time::Instant::now();

        let mut matching_events = Vec::new();

        // Compile regex if provided
        let regex_pattern = if let Some(ref pattern) = query.regex {
            Some(self.get_or_compile_regex(pattern)?)
        } else {
            None
        };

        // Filter events based on query criteria
        for event in &self.events {
            if self.event_matches_query(event, &query, &regex_pattern)? {
                matching_events.push(event.clone());
            }
        }

        // Apply sorting
        if let Some(ref sort) = query.sort {
            self.sort_events(&mut matching_events, sort);
        } else {
            // Default sort by timestamp descending
            matching_events.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
        }

        // Apply limit
        let total_count = matching_events.len();
        if let Some(limit) = query.limit {
            matching_events.truncate(limit);
        }

        // Generate aggregations
        let aggregations = self.generate_aggregations(&matching_events);

        let query_time_ms = start_time.elapsed().as_millis() as u64;

        Ok(SearchResults {
            events: matching_events,
            total_count,
            query_time_ms,
            aggregations,
        })
    }

    /// Check if event matches query criteria
    fn event_matches_query(
        &self,
        event: &AnalyticsEvent,
        query: &SearchQuery,
        regex_pattern: &Option<&Regex>,
    ) -> Result<bool> {
        // Text search
        if let Some(ref text) = query.text {
            if !event.message.to_lowercase().contains(&text.to_lowercase()) {
                return Ok(false);
            }
        }

        // Regex search
        if let Some(regex) = regex_pattern {
            if !regex.is_match(&event.message) {
                return Ok(false);
            }
        }

        // Source filter
        if let Some(ref sources) = query.sources {
            if !sources.contains(&event.source) {
                return Ok(false);
            }
        }

        // Event type filter
        if let Some(ref event_types) = query.event_types {
            if !event_types.contains(&event.event_type) {
                return Ok(false);
            }
        }

        // Level filter
        if let Some(ref levels) = query.levels {
            if !levels.contains(&event.level) {
                return Ok(false);
            }
        }

        // Time range filter
        if let Some(ref time_range) = query.time_range {
            if event.timestamp < time_range.start || event.timestamp > time_range.end {
                return Ok(false);
            }
        }

        // Path filter
        if let Some(ref paths) = query.paths {
            if let Some(ref event_path) = event.path {
                if !paths.iter().any(|p| event_path.contains(p)) {
                    return Ok(false);
                }
            } else {
                return Ok(false);
            }
        }

        // Session ID filter
        if let Some(ref session_ids) = query.session_ids {
            if let Some(ref event_session) = event.session_id {
                if !session_ids.contains(event_session) {
                    return Ok(false);
                }
            } else {
                return Ok(false);
            }
        }

        // Error filter
        if let Some(has_errors) = query.has_errors {
            let event_has_errors = !event.errors.is_empty();
            if has_errors != event_has_errors {
                return Ok(false);
            }
        }

        // Duration filters
        if let Some(duration) = event.duration_ms {
            if let Some(min_duration) = query.min_duration_ms {
                if duration < min_duration {
                    return Ok(false);
                }
            }
            if let Some(max_duration) = query.max_duration_ms {
                if duration > max_duration {
                    return Ok(false);
                }
            }
        }

        Ok(true)
    }

    /// Get or compile regex pattern
    fn get_or_compile_regex(&mut self, pattern: &str) -> Result<&Regex> {
        if !self.regex_cache.contains_key(pattern) {
            let regex = Regex::new(pattern)
                .with_context(|| format!("Invalid regex pattern: {}", pattern))?;
            self.regex_cache.insert(pattern.to_string(), regex);
        }
        Ok(self.regex_cache.get(pattern).unwrap())
    }

    /// Sort events based on sort options
    fn sort_events(&self, events: &mut Vec<AnalyticsEvent>, sort: &SortOptions) {
        match (&sort.field, &sort.direction) {
            (SortField::Timestamp, SortDirection::Ascending) => {
                events.sort_by(|a, b| a.timestamp.cmp(&b.timestamp));
            }
            (SortField::Timestamp, SortDirection::Descending) => {
                events.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
            }
            (SortField::Level, SortDirection::Ascending) => {
                events.sort_by(|a, b| a.level.cmp(&b.level));
            }
            (SortField::Level, SortDirection::Descending) => {
                events.sort_by(|a, b| b.level.cmp(&a.level));
            }
            (SortField::Source, SortDirection::Ascending) => {
                events.sort_by(|a, b| a.source.as_str().cmp(b.source.as_str()));
            }
            (SortField::Source, SortDirection::Descending) => {
                events.sort_by(|a, b| b.source.as_str().cmp(a.source.as_str()));
            }
            (SortField::Duration, SortDirection::Ascending) => {
                events.sort_by(|a, b| a.duration_ms.cmp(&b.duration_ms));
            }
            (SortField::Duration, SortDirection::Descending) => {
                events.sort_by(|a, b| b.duration_ms.cmp(&a.duration_ms));
            }
            (SortField::EventType, SortDirection::Ascending) => {
                events.sort_by(|a, b| a.event_type.cmp(&b.event_type));
            }
            (SortField::EventType, SortDirection::Descending) => {
                events.sort_by(|a, b| b.event_type.cmp(&a.event_type));
            }
        }
    }

    /// Generate aggregations from matching events
    fn generate_aggregations(&self, events: &[AnalyticsEvent]) -> SearchAggregations {
        let mut sources: HashMap<LogSource, usize> = HashMap::new();
        let mut event_types: HashMap<String, usize> = HashMap::new();
        let mut levels: HashMap<EventLevel, usize> = HashMap::new();
        let mut hourly_distribution: HashMap<u8, usize> = HashMap::new();
        let mut path_counts: HashMap<String, usize> = HashMap::new();
        let mut error_messages: HashMap<String, usize> = HashMap::new();
        let mut error_sources: HashMap<LogSource, usize> = HashMap::new();

        for event in events {
            // Count by source
            *sources.entry(event.source.clone()).or_insert(0) += 1;

            // Count by event type
            *event_types.entry(event.event_type.clone()).or_insert(0) += 1;

            // Count by level
            *levels.entry(event.level.clone()).or_insert(0) += 1;

            // Count by hour
            let hour = event.timestamp.time().hour() as u8;
            *hourly_distribution.entry(hour).or_insert(0) += 1;

            // Count by path
            if let Some(ref path) = event.path {
                *path_counts.entry(path.clone()).or_insert(0) += 1;
            }

            // Count errors
            if !event.errors.is_empty() {
                *error_sources.entry(event.source.clone()).or_insert(0) += 1;
                for error in &event.errors {
                    *error_messages.entry(error.clone()).or_insert(0) += 1;
                }
            }
        }

        // Convert to sorted vectors
        let mut top_paths: Vec<(String, usize)> = path_counts.into_iter().collect();
        top_paths.sort_by(|a, b| b.1.cmp(&a.1));
        top_paths.truncate(10);

        let mut top_error_messages: Vec<(String, usize)> = error_messages.iter()
            .map(|(k, v)| (k.clone(), *v))
            .collect();
        top_error_messages.sort_by(|a, b| b.1.cmp(&a.1));
        top_error_messages.truncate(10);

        let total_errors: usize = error_messages.values().sum();
        let unique_error_messages = error_messages.len();

        SearchAggregations {
            sources,
            event_types,
            levels,
            hourly_distribution,
            top_paths,
            error_summary: ErrorSummary {
                total_errors,
                unique_error_messages,
                top_error_messages,
                error_sources,
            },
        }
    }

    /// Create predefined queries for common searches
    pub fn create_error_query(last_hours: i64) -> SearchQuery {
        SearchQuery {
            text: None,
            regex: None,
            sources: None,
            event_types: None,
            levels: Some(vec![EventLevel::Error, EventLevel::Critical]),
            time_range: Some(TimeRange::last_hours(last_hours)),
            paths: None,
            session_ids: None,
            has_errors: Some(true),
            min_duration_ms: None,
            max_duration_ms: None,
            limit: Some(100),
            sort: Some(SortOptions {
                field: SortField::Timestamp,
                direction: SortDirection::Descending,
            }),
        }
    }

    /// Create query for slow requests
    pub fn create_slow_requests_query(min_duration_ms: u64) -> SearchQuery {
        SearchQuery {
            text: None,
            regex: None,
            sources: None,
            event_types: None,
            levels: None,
            time_range: Some(TimeRange::today()),
            paths: None,
            session_ids: None,
            has_errors: None,
            min_duration_ms: Some(min_duration_ms),
            max_duration_ms: None,
            limit: Some(50),
            sort: Some(SortOptions {
                field: SortField::Duration,
                direction: SortDirection::Descending,
            }),
        }
    }

    /// Create query for specific route analysis
    pub fn create_route_query(route_path: &str, hours: i64) -> SearchQuery {
        SearchQuery {
            text: None,
            regex: None,
            sources: Some(vec![LogSource::Navigation, LogSource::RouteCache]),
            event_types: None,
            levels: None,
            time_range: Some(TimeRange::last_hours(hours)),
            paths: Some(vec![route_path.to_string()]),
            session_ids: None,
            has_errors: None,
            min_duration_ms: None,
            max_duration_ms: None,
            limit: Some(200),
            sort: Some(SortOptions {
                field: SortField::Timestamp,
                direction: SortDirection::Descending,
            }),
        }
    }

    /// Get current statistics
    pub fn get_stats(&self) -> SearchStats {
        let total_events = self.events.len();
        let mut sources: HashMap<LogSource, usize> = HashMap::new();
        let mut oldest_event: Option<DateTime<Utc>> = None;
        let mut newest_event: Option<DateTime<Utc>> = None;

        for event in &self.events {
            *sources.entry(event.source.clone()).or_insert(0) += 1;

            if oldest_event.is_none() || event.timestamp < oldest_event.unwrap() {
                oldest_event = Some(event.timestamp);
            }

            if newest_event.is_none() || event.timestamp > newest_event.unwrap() {
                newest_event = Some(event.timestamp);
            }
        }

        SearchStats {
            total_events,
            sources,
            oldest_event,
            newest_event,
            regex_cache_size: self.regex_cache.len(),
        }
    }

    /// Clear old events to free memory
    pub fn cleanup_old_events(&mut self, older_than_hours: i64) {
        let cutoff = Utc::now() - Duration::hours(older_than_hours);
        self.events.retain(|event| event.timestamp > cutoff);
    }
}

/// Search engine statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchStats {
    pub total_events: usize,
    pub sources: HashMap<LogSource, usize>,
    pub oldest_event: Option<DateTime<Utc>>,
    pub newest_event: Option<DateTime<Utc>>,
    pub regex_cache_size: usize,
}
