//! # Event Filters Module
//!
//! Provides filtering capabilities for trading events, allowing subscribers
//! to receive only the events they are interested in.

use super::events::{EventCategory, EventSeverity, TradingEvent, TradingEventType};
use chrono::{DateTime, TimeDelta, Utc};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};

/// Event filter for controlling which events are delivered to subscribers
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EventFilter {
    /// Filter by event types (if empty, all types are allowed)
    pub event_types: HashSet<TradingEventType>,
    /// Filter by event categories
    pub categories: HashSet<EventCategory>,
    /// Minimum severity level
    pub min_severity: Option<EventSeverity>,
    /// Filter by source services
    pub sources: HashSet<String>,
    /// Filter by correlation IDs
    pub correlation_ids: HashSet<String>,
    /// Metadata filters (key-value pairs that must match)
    pub metadata_filters: HashMap<String, String>,
    /// Time range filter
    pub time_range: Option<TimeRange>,
    /// Custom filter function name (for advanced filtering)
    pub custom_filter: Option<String>,
}

impl EventFilter {
    /// Create a new empty filter (matches all events)
    pub fn new() -> Self {
        Self {
            event_types: HashSet::new(),
            categories: HashSet::new(),
            min_severity: None,
            sources: HashSet::new(),
            correlation_ids: HashSet::new(),
            metadata_filters: HashMap::new(),
            time_range: None,
            custom_filter: None,
        }
    }

    /// Create a filter for specific event types
    pub fn for_event_types(types: Vec<TradingEventType>) -> Self {
        Self {
            event_types: types.into_iter().collect(),
            ..Self::new()
        }
    }

    /// Create a filter for specific event categories
    pub fn for_categories(categories: Vec<EventCategory>) -> Self {
        Self {
            categories: categories.into_iter().collect(),
            ..Self::new()
        }
    }

    /// Create a filter with minimum severity
    pub fn with_min_severity(severity: EventSeverity) -> Self {
        Self {
            min_severity: Some(severity),
            ..Self::new()
        }
    }

    /// Create a filter for specific sources
    pub fn for_sources(sources: Vec<String>) -> Self {
        Self {
            sources: sources.into_iter().collect(),
            ..Self::new()
        }
    }

    /// Create a filter for specific correlation IDs
    pub fn for_correlation_ids(ids: Vec<String>) -> Self {
        Self {
            correlation_ids: ids.into_iter().collect(),
            ..Self::new()
        }
    }

    /// Add event type to filter
    pub fn add_event_type(&mut self, event_type: TradingEventType) {
        self.event_types.insert(event_type);
    }

    /// Add category to filter
    pub fn add_category(&mut self, category: EventCategory) {
        self.categories.insert(category);
    }

    /// Set minimum severity
    pub fn set_min_severity(&mut self, severity: EventSeverity) {
        self.min_severity = Some(severity);
    }

    /// Add source to filter
    pub fn add_source(&mut self, source: String) {
        self.sources.insert(source);
    }

    /// Add correlation ID to filter
    pub fn add_correlation_id(&mut self, id: String) {
        self.correlation_ids.insert(id);
    }

    /// Add metadata filter
    pub fn add_metadata_filter(&mut self, key: String, value: String) {
        self.metadata_filters.insert(key, value);
    }

    /// Set time range filter
    pub fn set_time_range(&mut self, time_range: TimeRange) {
        self.time_range = Some(time_range);
    }

    /// Check if an event matches this filter
    pub fn matches(&self, event: &TradingEvent) -> bool {
        // Check event types
        if !self.event_types.is_empty() && !self.event_types.contains(&event.event_type) {
            return false;
        }

        // Check categories
        if !self.categories.is_empty() && !self.categories.contains(&event.event_type.category()) {
            return false;
        }

        // Check minimum severity
        if let Some(min_severity) = self.min_severity {
            if event.severity < min_severity {
                return false;
            }
        }

        // Check sources
        if !self.sources.is_empty() && !self.sources.contains(&event.source) {
            return false;
        }

        // Check correlation IDs
        if !self.correlation_ids.is_empty() {
            if let Some(ref correlation_id) = event.correlation_id {
                if !self.correlation_ids.contains(correlation_id) {
                    return false;
                }
            } else {
                return false;
            }
        }

        // Check metadata filters
        for (key, expected_value) in &self.metadata_filters {
            if let Some(actual_value) = event.metadata.get(key) {
                if actual_value != expected_value {
                    return false;
                }
            } else {
                return false;
            }
        }

        // Check time range
        if let Some(ref time_range) = self.time_range {
            if !time_range.contains(event.timestamp) {
                return false;
            }
        }

        true
    }

    /// Check if this filter is empty (matches all events)
    pub fn is_empty(&self) -> bool {
        self.event_types.is_empty()
            && self.categories.is_empty()
            && self.min_severity.is_none()
            && self.sources.is_empty()
            && self.correlation_ids.is_empty()
            && self.metadata_filters.is_empty()
            && self.time_range.is_none()
            && self.custom_filter.is_none()
    }

    /// Get a description of what this filter matches
    pub fn description(&self) -> String {
        let mut parts = Vec::new();

        if !self.event_types.is_empty() {
            let types: Vec<_> = self.event_types.iter().map(|t| t.as_str()).collect();
            parts.push(format!("types: [{}]", types.join(", ")));
        }

        if !self.categories.is_empty() {
            let categories: Vec<_> = self.categories.iter().map(|c| c.as_str()).collect();
            parts.push(format!("categories: [{}]", categories.join(", ")));
        }

        if let Some(min_severity) = self.min_severity {
            parts.push(format!("min_severity: {}", min_severity.as_str()));
        }

        if !self.sources.is_empty() {
            parts.push(format!(
                "sources: [{}]",
                self.sources.iter().cloned().collect::<Vec<_>>().join(", ")
            ));
        }

        if !self.correlation_ids.is_empty() {
            parts.push(format!(
                "correlation_ids: {} items",
                self.correlation_ids.len()
            ));
        }

        if !self.metadata_filters.is_empty() {
            parts.push(format!("metadata: {} filters", self.metadata_filters.len()));
        }

        if let Some(ref time_range) = self.time_range {
            parts.push(format!(
                "time_range: {} to {}",
                time_range.start.to_rfc3339(),
                time_range.end.to_rfc3339()
            ));
        }

        if parts.is_empty() {
            "all events".to_string()
        } else {
            parts.join(", ")
        }
    }

    /// Combine this filter with another using AND logic
    pub fn and(&self, other: &EventFilter) -> EventFilter {
        let mut combined = self.clone();

        // Combine event types (intersection)
        if !other.event_types.is_empty() {
            if combined.event_types.is_empty() {
                combined.event_types = other.event_types.clone();
            } else {
                combined.event_types = combined
                    .event_types
                    .intersection(&other.event_types)
                    .cloned()
                    .collect();
            }
        }

        // Combine categories (intersection)
        if !other.categories.is_empty() {
            if combined.categories.is_empty() {
                combined.categories = other.categories.clone();
            } else {
                combined.categories = combined
                    .categories
                    .intersection(&other.categories)
                    .cloned()
                    .collect();
            }
        }

        // Take the higher minimum severity
        if let Some(other_severity) = other.min_severity {
            combined.min_severity = Some(
                combined
                    .min_severity
                    .map(|s| s.max(other_severity))
                    .unwrap_or(other_severity),
            );
        }

        // Combine sources (intersection)
        if !other.sources.is_empty() {
            if combined.sources.is_empty() {
                combined.sources = other.sources.clone();
            } else {
                combined.sources = combined
                    .sources
                    .intersection(&other.sources)
                    .cloned()
                    .collect();
            }
        }

        // Combine correlation IDs (intersection)
        if !other.correlation_ids.is_empty() {
            if combined.correlation_ids.is_empty() {
                combined.correlation_ids = other.correlation_ids.clone();
            } else {
                combined.correlation_ids = combined
                    .correlation_ids
                    .intersection(&other.correlation_ids)
                    .cloned()
                    .collect();
            }
        }

        // Combine metadata filters (both must match)
        for (key, value) in &other.metadata_filters {
            combined.metadata_filters.insert(key.clone(), value.clone());
        }

        // Combine time ranges (intersection)
        if let Some(ref other_range) = other.time_range {
            combined.time_range = match combined.time_range {
                Some(ref current_range) => Some(current_range.intersection(other_range)),
                None => Some(other_range.clone()),
            };
        }

        combined
    }

    /// Combine this filter with another using OR logic
    pub fn or(&self, other: &EventFilter) -> EventFilter {
        let mut combined = self.clone();

        // Combine event types (union)
        combined.event_types.extend(other.event_types.iter());

        // Combine categories (union)
        combined.categories.extend(other.categories.iter());

        // Take the lower minimum severity
        if let Some(other_severity) = other.min_severity {
            combined.min_severity = Some(
                combined
                    .min_severity
                    .map(|s| s.min(other_severity))
                    .unwrap_or(other_severity),
            );
        }

        // Combine sources (union)
        combined.sources.extend(other.sources.iter().cloned());

        // Combine correlation IDs (union)
        combined
            .correlation_ids
            .extend(other.correlation_ids.iter().cloned());

        // Note: Metadata filters and time ranges are more complex for OR logic
        // For simplicity, we'll keep the current filter's values
        // In practice, OR logic for these might require a more complex structure

        combined
    }
}

impl Default for EventFilter {
    fn default() -> Self {
        Self::new()
    }
}

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

impl TimeRange {
    /// Create a new time range
    pub fn new(start: DateTime<Utc>, end: DateTime<Utc>) -> Self {
        Self { start, end }
    }

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

    /// Create a time range for the last N minutes
    pub fn last_minutes(minutes: i64) -> Self {
        let end = Utc::now();
        let start = end - TimeDelta::minutes(minutes);
        Self { start, end }
    }

    /// Create a time range for today
    pub fn today() -> Self {
        let now = Utc::now();
        // Midnight is always valid, fallback to current time if somehow invalid
        let start = now
            .date_naive()
            .and_hms_opt(0, 0, 0)
            .map(|t| t.and_utc())
            .unwrap_or(now);
        let end = start + TimeDelta::days(1);
        Self { start, end }
    }

    /// Check if a timestamp is within this range
    pub fn contains(&self, timestamp: DateTime<Utc>) -> bool {
        timestamp >= self.start && timestamp <= self.end
    }

    /// Get the intersection of two time ranges
    pub fn intersection(&self, other: &TimeRange) -> TimeRange {
        let start = self.start.max(other.start);
        let end = self.end.min(other.end);

        // If the intersection is invalid, return an empty range
        if start > end {
            TimeRange::new(start, start)
        } else {
            TimeRange::new(start, end)
        }
    }

    /// Get the duration of this time range
    pub fn duration(&self) -> TimeDelta {
        self.end - self.start
    }

    /// Check if this time range is valid (start <= end)
    pub fn is_valid(&self) -> bool {
        self.start <= self.end
    }
}

/// Predefined filter builders for common use cases
pub struct FilterBuilder;

impl FilterBuilder {
    /// Filter for order events only
    pub fn orders() -> EventFilter {
        EventFilter::for_categories(vec![EventCategory::Order])
    }

    /// Filter for critical events only
    pub fn critical() -> EventFilter {
        EventFilter::with_min_severity(EventSeverity::Critical)
    }

    /// Filter for warnings and errors
    pub fn warnings_and_errors() -> EventFilter {
        EventFilter::with_min_severity(EventSeverity::Warning)
    }

    /// Filter for risk events
    pub fn risk() -> EventFilter {
        EventFilter::for_categories(vec![EventCategory::Risk])
    }

    /// Filter for market data events
    pub fn market_data() -> EventFilter {
        EventFilter::for_categories(vec![EventCategory::MarketData])
    }

    /// Filter for system events
    pub fn system() -> EventFilter {
        EventFilter::for_categories(vec![EventCategory::System])
    }

    /// Filter for ML events
    pub fn ml() -> EventFilter {
        EventFilter::for_categories(vec![EventCategory::ML])
    }

    /// Filter for recent events (last hour)
    pub fn recent() -> EventFilter {
        let mut filter = EventFilter::new();
        filter.set_time_range(TimeRange::last_hours(1));
        filter
    }

    /// Filter for events from trading engine
    pub fn from_trading_engine() -> EventFilter {
        EventFilter::for_sources(vec!["trading_engine".to_string()])
    }

    /// Filter for events from risk management
    pub fn from_risk_management() -> EventFilter {
        EventFilter::for_sources(vec!["risk_management".to_string()])
    }

    /// Filter for specific symbol
    pub fn for_symbol(symbol: String) -> EventFilter {
        let mut filter = EventFilter::new();
        filter.add_metadata_filter("symbol".to_string(), symbol);
        filter
    }

    /// Filter for specific order ID
    pub fn for_order(order_id: String) -> EventFilter {
        EventFilter::for_correlation_ids(vec![order_id])
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::event_streaming::events::TradingEvent;

    #[test]
    fn test_event_filter_creation() {
        let filter = EventFilter::for_event_types(vec![TradingEventType::OrderSubmitted]);
        assert_eq!(filter.event_types.len(), 1);
        assert!(filter
            .event_types
            .contains(&TradingEventType::OrderSubmitted));
    }

    #[test]
    fn test_event_type_filtering() {
        let filter = EventFilter::for_event_types(vec![TradingEventType::OrderSubmitted]);

        let matching_event = TradingEvent::new(
            TradingEventType::OrderSubmitted,
            "order123".to_string(),
            "test".to_string(),
        );

        let non_matching_event = TradingEvent::new(
            TradingEventType::OrderFilled,
            "order123".to_string(),
            "test".to_string(),
        );

        assert!(filter.matches(&matching_event));
        assert!(!filter.matches(&non_matching_event));
    }

    #[test]
    fn test_severity_filtering() {
        let filter = EventFilter::with_min_severity(EventSeverity::Warning);

        let warning_event = TradingEvent::new(
            TradingEventType::RiskWarning,
            "risk1".to_string(),
            "test".to_string(),
        )
        .with_severity(EventSeverity::Warning);

        let info_event = TradingEvent::new(
            TradingEventType::OrderSubmitted,
            "order1".to_string(),
            "test".to_string(),
        )
        .with_severity(EventSeverity::Info);

        assert!(filter.matches(&warning_event));
        assert!(!filter.matches(&info_event));
    }

    #[test]
    fn test_source_filtering() {
        let filter = EventFilter::for_sources(vec!["trading_engine".to_string()]);

        let matching_event = TradingEvent::with_source(
            TradingEventType::OrderSubmitted,
            "trading_engine".to_string(),
            "order123".to_string(),
            "test".to_string(),
        );

        let non_matching_event = TradingEvent::with_source(
            TradingEventType::OrderSubmitted,
            "risk_management".to_string(),
            "order123".to_string(),
            "test".to_string(),
        );

        assert!(filter.matches(&matching_event));
        assert!(!filter.matches(&non_matching_event));
    }

    #[test]
    fn test_metadata_filtering() {
        use crate::test_utils::TestConfig;

        let test_config = TestConfig::new();
        let primary_symbol = test_config.primary_symbol();
        let secondary_symbol = test_config.secondary_symbol();

        let mut filter = EventFilter::new();
        filter.add_metadata_filter("symbol".to_string(), primary_symbol.to_string());

        let mut matching_event = TradingEvent::new(
            TradingEventType::OrderSubmitted,
            "order123".to_string(),
            "test".to_string(),
        );
        matching_event.add_metadata("symbol".to_string(), primary_symbol.to_string());

        let mut non_matching_event = TradingEvent::new(
            TradingEventType::OrderSubmitted,
            "order123".to_string(),
            "test".to_string(),
        );
        non_matching_event.add_metadata("symbol".to_string(), secondary_symbol.to_string());

        assert!(filter.matches(&matching_event));
        assert!(!filter.matches(&non_matching_event));
    }

    #[test]
    fn test_time_range() {
        let now = Utc::now();
        let range = TimeRange::new(now - TimeDelta::hours(1), now + TimeDelta::hours(1));

        assert!(range.contains(now));
        assert!(!range.contains(now - TimeDelta::hours(2)));
        assert!(!range.contains(now + TimeDelta::hours(2)));
    }

    #[test]
    fn test_filter_combination_and() {
        let filter1 = EventFilter::for_event_types(vec![TradingEventType::OrderSubmitted]);
        let filter2 = EventFilter::with_min_severity(EventSeverity::Warning);

        let combined = filter1.and(&filter2);

        assert!(combined
            .event_types
            .contains(&TradingEventType::OrderSubmitted));
        assert_eq!(combined.min_severity, Some(EventSeverity::Warning));
    }

    #[test]
    fn test_filter_combination_or() {
        let filter1 = EventFilter::for_event_types(vec![TradingEventType::OrderSubmitted]);
        let filter2 = EventFilter::for_event_types(vec![TradingEventType::OrderFilled]);

        let combined = filter1.or(&filter2);

        assert!(combined
            .event_types
            .contains(&TradingEventType::OrderSubmitted));
        assert!(combined
            .event_types
            .contains(&TradingEventType::OrderFilled));
    }

    #[test]
    fn test_filter_builders() {
        let orders_filter = FilterBuilder::orders();
        assert!(orders_filter.categories.contains(&EventCategory::Order));

        let critical_filter = FilterBuilder::critical();
        assert_eq!(critical_filter.min_severity, Some(EventSeverity::Critical));

        let recent_filter = FilterBuilder::recent();
        assert!(recent_filter.time_range.is_some());
    }

    #[test]
    fn test_empty_filter() {
        let filter = EventFilter::new();
        assert!(filter.is_empty());

        let event = TradingEvent::new(
            TradingEventType::OrderSubmitted,
            "order123".to_string(),
            "test".to_string(),
        );

        // Empty filter should match all events
        assert!(filter.matches(&event));
    }

    #[test]
    fn test_filter_description() {
        let filter = EventFilter::for_event_types(vec![TradingEventType::OrderSubmitted]);
        let description = filter.description();
        assert!(description.contains("order_submitted"));

        let empty_filter = EventFilter::new();
        assert_eq!(empty_filter.description(), "all events");
    }
}
