//! Benzinga News Provider Streaming and Integration Tests
//!
//! Comprehensive tests for Benzinga news feed covering:
//! - News article processing and parsing
//! - Rate limiting and throttling
//! - Real-time streaming
//! - News sentiment analysis integration

#![allow(
    clippy::absurd_extreme_comparisons,
    clippy::assertions_on_constants,
    clippy::assertions_on_result_states,
    clippy::clone_on_copy,
    clippy::decimal_literal_representation,
    clippy::doc_lazy_continuation,
    clippy::doc_markdown,
    clippy::double_comparisons,
    clippy::else_if_without_else,
    clippy::empty_drop,
    clippy::expect_used,
    clippy::field_reassign_with_default,
    clippy::format_push_string,
    clippy::if_then_some_else_none,
    clippy::indexing_slicing,
    clippy::integer_division,
    clippy::len_zero,
    clippy::let_and_return,
    clippy::let_underscore_must_use,
    clippy::manual_let_else,
    clippy::manual_range_contains,
    clippy::map_err_ignore,
    clippy::missing_const_for_fn,
    clippy::modulo_arithmetic,
    clippy::needless_range_loop,
    clippy::new_without_default,
    clippy::non_ascii_literal,
    clippy::nonminimal_bool,
    clippy::octal_escapes,
    clippy::overly_complex_bool_expr,
    clippy::redundant_clone,
    clippy::shadow_reuse,
    clippy::shadow_unrelated,
    clippy::similar_names,
    clippy::single_component_path_imports,
    clippy::single_match_else,
    clippy::str_to_string,
    clippy::string_slice,
    clippy::tests_outside_test_module,
    clippy::unnecessary_cast,
    clippy::unnecessary_get_then_check,
    clippy::unnecessary_unwrap,
    clippy::unseparated_literal_suffix,
    clippy::unwrap_or_default,
    clippy::unwrap_used,
    clippy::use_debug,
    clippy::useless_vec,
    clippy::wildcard_enum_match_arm,
    dead_code,
    deprecated,
    unreachable_pub,
    unused_assignments,
    unused_comparisons,
    unused_imports,
    unused_variables
)]

use chrono::{Duration, Utc};
use common::types::Symbol;
use data::error::DataError;
use data::providers::common::{NewsEvent, NewsEventType};
use std::collections::HashMap;

// ============================================================================
// News Article Processing Tests
// ============================================================================

#[test]
fn test_benzinga_news_article_structure() {
    let article = NewsEvent {
        symbol: Some(Symbol::from("AAPL")),
        symbols: vec![Symbol::from("AAPL")],
        story_id: "BZ123456".to_string(),
        headline: "Apple Announces New Product".to_string(),
        content: "Apple Inc. announced...".to_string(),
        summary: "Apple announces new product".to_string(),
        category: "News".to_string(),
        tags: vec!["tech".to_string(), "apple".to_string()],
        impact_score: Some(0.8),
        importance: 0.9,
        author: "Benzinga Staff".to_string(),
        timestamp: Utc::now(),
        published_at: Utc::now(),
        source: "Benzinga".to_string(),
        url: "https://benzinga.com/news/123456".to_string(),
        sentiment_score: Some(0.6),
        #[allow(deprecated)]
        sentiment: Some(0.6),
        event_type: NewsEventType::News,
    };

    assert_eq!(article.source, "Benzinga");
    assert!(article.symbol.is_some());
    assert!(!article.headline.is_empty());
}

#[test]
fn test_benzinga_news_empty_fields() {
    let article = NewsEvent {
        symbol: None,
        symbols: vec![],
        story_id: "".to_string(),
        headline: "".to_string(),
        content: "".to_string(),
        summary: "".to_string(),
        category: "".to_string(),
        tags: vec![],
        impact_score: None,
        importance: 0.0,
        author: "".to_string(),
        timestamp: Utc::now(),
        published_at: Utc::now(),
        source: "Benzinga".to_string(),
        url: "".to_string(),
        sentiment_score: None,
        #[allow(deprecated)]
        sentiment: None,
        event_type: NewsEventType::News,
    };

    assert!(article.symbol.is_none());
    assert!(article.headline.is_empty());
    assert!(article.content.is_empty());
}

#[test]
fn test_benzinga_news_with_symbol() {
    let article = NewsEvent {
        symbol: Some(Symbol::from("AAPL")),
        symbols: vec![Symbol::from("AAPL")],
        story_id: "BZ123457".to_string(),
        headline: "Tech Giants Partnership".to_string(),
        content: "Major tech companies announce partnership...".to_string(),
        summary: "Partnership announced".to_string(),
        category: "News".to_string(),
        tags: vec![],
        impact_score: None,
        importance: 0.5,
        author: "Benzinga Staff".to_string(),
        timestamp: Utc::now(),
        published_at: Utc::now(),
        source: "Benzinga".to_string(),
        url: "https://benzinga.com/news/123457".to_string(),
        sentiment_score: None,
        #[allow(deprecated)]
        sentiment: None,
        event_type: NewsEventType::News,
    };

    assert!(article.symbol.is_some());
    assert_eq!(article.symbol.unwrap().as_str(), "AAPL");
}

// ============================================================================
// Earnings Event Tests
// ============================================================================

#[test]
fn test_benzinga_earnings_event() {
    let earnings = NewsEvent {
        symbol: Some(Symbol::from("AAPL")),
        symbols: vec![Symbol::from("AAPL")],
        story_id: "BZ123458".to_string(),
        headline: "Apple Q4 Earnings Beat Estimates".to_string(),
        content: "Apple reports strong Q4... EPS: $2.75 (est: $2.50)".to_string(),
        summary: "Apple beats earnings estimates".to_string(),
        category: "Earnings".to_string(),
        tags: vec!["earnings".to_string()],
        impact_score: Some(0.9),
        importance: 0.95,
        author: "Benzinga Staff".to_string(),
        timestamp: Utc::now(),
        published_at: Utc::now(),
        source: "Benzinga".to_string(),
        url: "https://benzinga.com/news/123458".to_string(),
        sentiment_score: Some(0.75),
        #[allow(deprecated)]
        sentiment: Some(0.75),
        event_type: NewsEventType::Earnings,
    };

    assert!(earnings.content.contains("EPS"));
    assert!(earnings.content.contains("$2.75"));
}

#[test]
fn test_benzinga_earnings_surprise() {
    // Test calculation logic (standalone, not tied to NewsEvent structure)
    let eps_estimate = 1.50;
    let eps_actual = 2.00;
    let surprise: f64 = ((eps_actual - eps_estimate) / eps_estimate) * 100.0;

    assert!(surprise > 0.0); // Positive surprise
    assert_eq!(surprise.round(), 33.0); // 33% surprise
}

// ============================================================================
// Analyst Rating Tests
// ============================================================================

#[test]
fn test_benzinga_analyst_rating_upgrade() {
    let rating = NewsEvent {
        symbol: Some(Symbol::from("TSLA")),
        symbols: vec![Symbol::from("TSLA")],
        story_id: "BZ123459".to_string(),
        headline: "Goldman Sachs Upgrades Tesla".to_string(),
        content: "Goldman Sachs upgraded Tesla from hold to buy".to_string(),
        summary: "Tesla upgraded to buy".to_string(),
        category: "Analyst Rating".to_string(),
        tags: vec!["upgrade".to_string()],
        impact_score: Some(0.85),
        importance: 0.9,
        author: "Benzinga Staff".to_string(),
        timestamp: Utc::now(),
        published_at: Utc::now(),
        source: "Benzinga".to_string(),
        url: "https://benzinga.com/news/123459".to_string(),
        sentiment_score: Some(0.8),
        #[allow(deprecated)]
        sentiment: Some(0.8),
        event_type: NewsEventType::Rating,
    };

    assert!(rating.content.contains("upgraded"));
    assert!(rating.content.contains("buy"));
}

#[test]
fn test_benzinga_analyst_rating_downgrade() {
    let rating = NewsEvent {
        symbol: Some(Symbol::from("NFLX")),
        symbols: vec![Symbol::from("NFLX")],
        story_id: "BZ123460".to_string(),
        headline: "Netflix Downgraded on Subscriber Concerns".to_string(),
        content: "Downgraded from buy to sell".to_string(),
        summary: "Netflix downgraded to sell".to_string(),
        category: "Analyst Rating".to_string(),
        tags: vec!["downgrade".to_string()],
        impact_score: Some(0.85),
        importance: 0.9,
        author: "Benzinga Staff".to_string(),
        timestamp: Utc::now(),
        published_at: Utc::now(),
        source: "Benzinga".to_string(),
        url: "https://benzinga.com/news/123460".to_string(),
        sentiment_score: Some(0.2),
        #[allow(deprecated)]
        sentiment: Some(0.2),
        event_type: NewsEventType::Rating,
    };

    assert!(rating.content.contains("Downgraded"));
    assert!(rating.content.contains("sell"));
}

// ============================================================================
// Economic Event Tests
// ============================================================================

#[test]
fn test_benzinga_economic_calendar_event() {
    let economic = NewsEvent {
        symbol: None,
        symbols: vec![],
        story_id: "BZ123461".to_string(),
        headline: "Federal Reserve FOMC Meeting".to_string(),
        content:
            "Federal Reserve to announce interest rate decision... High importance event for USA"
                .to_string(),
        summary: "FOMC meeting scheduled".to_string(),
        category: "Economic".to_string(),
        tags: vec!["fed".to_string(), "fomc".to_string()],
        impact_score: Some(0.95),
        importance: 1.0,
        author: "Benzinga Staff".to_string(),
        timestamp: Utc::now(),
        published_at: Utc::now(),
        source: "Benzinga".to_string(),
        url: "https://benzinga.com/news/123461".to_string(),
        sentiment_score: None,
        #[allow(deprecated)]
        sentiment: None,
        event_type: NewsEventType::Economic,
    };

    assert!(economic.content.contains("Federal Reserve"));
    assert!(economic.content.contains("High importance"));
}

#[test]
fn test_benzinga_economic_data_release() {
    let data = NewsEvent {
        symbol: None,
        symbols: vec![],
        story_id: "BZ123462".to_string(),
        headline: "Consumer Price Index Exceeds Expectations".to_string(),
        content: "CPI: Expected 3.5%, Actual 3.8%".to_string(),
        summary: "CPI exceeds expectations".to_string(),
        category: "Economic".to_string(),
        tags: vec!["cpi".to_string(), "inflation".to_string()],
        impact_score: Some(0.9),
        importance: 0.95,
        author: "Benzinga Staff".to_string(),
        timestamp: Utc::now(),
        published_at: Utc::now(),
        source: "Benzinga".to_string(),
        url: "https://benzinga.com/news/123462".to_string(),
        sentiment_score: Some(0.3),
        #[allow(deprecated)]
        sentiment: Some(0.3),
        event_type: NewsEventType::Economic,
    };

    assert!(data.content.contains("CPI"));
    assert!(data.content.contains("3.8%"));
}

// ============================================================================
// Rate Limiting Tests
// ============================================================================

#[test]
fn test_benzinga_rate_limit_error() {
    let rate_limit_err = DataError::RateLimit;
    assert!(rate_limit_err.is_retryable());
    assert_eq!(rate_limit_err.category(), "RATE_LIMIT");
}

#[test]
fn test_benzinga_rate_limit_throttling() {
    let requests_per_minute = 60;
    let current_requests = 55;

    let remaining = requests_per_minute - current_requests;
    let should_throttle = remaining <= 5;

    assert!(should_throttle);
}

#[test]
fn test_benzinga_rate_limit_backoff() {
    let base_delay_ms = 1000;
    let attempts = vec![0, 1, 2, 3, 4];

    for attempt in attempts {
        let delay = base_delay_ms * 2_u64.pow(attempt);
        let capped_delay = delay.min(60_000);

        assert!(capped_delay >= base_delay_ms);
        assert!(capped_delay <= 60_000);
    }
}

// ============================================================================
// API Error Handling Tests
// ============================================================================

#[test]
fn test_benzinga_api_error_responses() {
    let api_errors = vec![
        ("400", "Bad Request"),
        ("401", "Unauthorized"),
        ("403", "Forbidden"),
        ("404", "Not Found"),
        ("429", "Too Many Requests"),
        ("500", "Internal Server Error"),
        ("503", "Service Unavailable"),
    ];

    for (status, message) in api_errors {
        let err = DataError::api(message, Some(status));
        assert!(matches!(err, DataError::Api { .. }));
        assert_eq!(err.category(), "API");
    }
}

#[test]
fn test_benzinga_authentication_error() {
    let auth_err = DataError::authentication("Invalid API token");
    assert!(matches!(auth_err, DataError::Authentication { .. }));
    assert_eq!(auth_err.severity(), data::error::ErrorSeverity::Critical);
}

// ============================================================================
// Symbol Validation Tests
// ============================================================================

#[test]
fn test_benzinga_symbol_validation() {
    let too_long = "TOOLONG".repeat(100);
    let invalid_symbols = vec!["", " ", "\n", &too_long, "!@#$%", "symbol with spaces"];

    for symbol in invalid_symbols {
        let is_valid = !symbol.is_empty()
            && symbol.len() <= 20
            && symbol.trim() == symbol
            && symbol
                .chars()
                .all(|c| c.is_alphanumeric() || c == '.' || c == '-');

        assert!(!is_valid);
    }
}

#[test]
fn test_benzinga_symbol_normalization() {
    let symbols = vec![("aapl", "AAPL"), ("tsla", "TSLA"), ("brk.b", "BRK.B")];

    for (input, expected) in symbols {
        let normalized = input.to_uppercase();
        assert_eq!(normalized, expected);
    }
}

// ============================================================================
// News Filtering Tests
// ============================================================================

#[test]
fn test_benzinga_news_category_filtering() {
    let categories = vec!["earnings", "analyst_rating", "merger", "ipo"];

    let article = NewsEvent {
        symbol: Some(Symbol::from("AAPL")),
        symbols: vec![Symbol::from("AAPL")],
        story_id: "BZ123463".to_string(),
        headline: "Test Earnings Release".to_string(),
        content: "earnings release content".to_string(),
        summary: "Earnings release".to_string(),
        category: "Earnings".to_string(),
        tags: vec!["earnings".to_string()],
        impact_score: Some(0.7),
        importance: 0.8,
        author: "Benzinga Staff".to_string(),
        timestamp: Utc::now(),
        published_at: Utc::now(),
        source: "Benzinga".to_string(),
        url: "https://benzinga.com/news/123463".to_string(),
        sentiment_score: Some(0.5),
        #[allow(deprecated)]
        sentiment: Some(0.5),
        event_type: NewsEventType::Earnings,
    };

    // Simulate category filtering based on content
    let should_include = categories.iter().any(|cat| article.content.contains(cat));
    assert!(should_include);
}

#[test]
fn test_benzinga_news_importance_filtering() {
    let min_importance = 0.7;

    // Simulate importance score (would come from content analysis in real implementation)
    let article_importance = 0.8;
    let should_include = article_importance >= min_importance;

    assert!(should_include);
}

// ============================================================================
// News Deduplication Tests
// ============================================================================

#[test]
fn test_benzinga_news_deduplication() {
    let mut seen_ids: std::collections::HashSet<String> = std::collections::HashSet::new();

    let event_ids = vec!["news_1", "news_2", "news_3", "news_2", "news_4", "news_3"];

    let mut unique_count = 0;
    for id in event_ids {
        if seen_ids.insert(id.to_string()) {
            unique_count += 1;
        }
    }

    assert_eq!(unique_count, 4); // Only 4 unique IDs
}

// ============================================================================
// Timestamp Validation Tests
// ============================================================================

#[test]
fn test_benzinga_news_timestamp_validation() {
    let now = Utc::now();
    let old_news = now - Duration::hours(24);
    let future_news = now + Duration::hours(1);

    let max_age = Duration::hours(6);

    let is_old = (now - old_news) > max_age;
    let is_future = future_news > now;

    assert!(is_old);
    assert!(is_future);
}

#[test]
fn test_benzinga_news_timestamp_ordering() {
    let events = vec![
        Utc::now() - Duration::hours(3),
        Utc::now() - Duration::hours(2),
        Utc::now() - Duration::hours(1),
        Utc::now(),
    ];

    for i in 1..events.len() {
        assert!(events[i] > events[i - 1]);
    }
}

// ============================================================================
// Content Processing Tests
// ============================================================================

#[test]
fn test_benzinga_news_content_sanitization() {
    let raw_content = "Apple <b>announces</b> new product!\n\nStock up 5%";
    let sanitized = raw_content.replace("<b>", "").replace("</b>", "");

    assert!(!sanitized.contains("<b>"));
    assert!(!sanitized.contains("</b>"));
}

#[test]
fn test_benzinga_news_content_truncation() {
    let long_content = "a".repeat(10000);
    let max_length = 5000;

    let truncated = if long_content.len() > max_length {
        &long_content[..max_length]
    } else {
        &long_content
    };

    assert_eq!(truncated.len(), max_length);
}

// ============================================================================
// Streaming Integration Tests
// ============================================================================

#[tokio::test]
async fn test_benzinga_streaming_event_processing() {
    use tokio::task;

    let handles: Vec<_> = (0..10)
        .map(|i| {
            task::spawn(async move {
                let event = NewsEvent {
                    symbol: Some(Symbol::from("AAPL")),
                    symbols: vec![Symbol::from("AAPL")],
                    story_id: format!("BZ12346{}", i),
                    headline: format!("Test Article {}", i),
                    content: format!("Content for article {}", i),
                    summary: format!("Article {}", i),
                    category: "News".to_string(),
                    tags: vec![],
                    impact_score: None,
                    importance: 0.5,
                    author: "Benzinga Staff".to_string(),
                    timestamp: Utc::now(),
                    published_at: Utc::now(),
                    source: "Benzinga".to_string(),
                    url: format!("https://benzinga.com/news/12346{}", i),
                    sentiment_score: None,
                    #[allow(deprecated)]
                    sentiment: None,
                    event_type: NewsEventType::News,
                };
                event
            })
        })
        .collect();

    for handle in handles {
        let event = handle.await.unwrap();
        assert!(!event.headline.is_empty());
    }
}

// ============================================================================
// Content Analysis Tests
// ============================================================================

#[test]
fn test_benzinga_sentiment_analysis() {
    // Simulate sentiment score extraction from content
    let content = "sentiment_score: 0.75, impact_score: 0.85";

    let sentiment = 0.75; // Would be extracted from content in real implementation
    let impact = 0.85;

    assert!(sentiment > 0.0 && sentiment <= 1.0);
    assert!(impact > 0.0 && impact <= 1.0);
}

#[test]
fn test_benzinga_content_missing_metadata() {
    let content = ""; // No metadata in content

    let sentiment = 0.0; // Default value when not found
    assert_eq!(sentiment, 0.0);
}

// ============================================================================
// Tag Processing Tests
// ============================================================================

#[test]
fn test_benzinga_tag_extraction() {
    let tags = vec![
        "earnings",
        "technology",
        "ai",
        "quarterly_results",
        "revenue_growth",
    ];

    let relevant_tags: Vec<&str> = tags
        .iter()
        .filter(|t| t.contains("earnings") || t.contains("revenue") || t.contains("growth"))
        .copied()
        .collect();

    assert_eq!(relevant_tags.len(), 2); // earnings and revenue_growth
}

// ============================================================================
// Error Recovery Tests
// ============================================================================

#[test]
fn test_benzinga_error_recovery() {
    let mut attempt = 0;
    let max_attempts = 3;

    let result = loop {
        attempt += 1;
        let err = DataError::network("Connection timeout");

        if err.is_retryable() && attempt < max_attempts {
            continue;
        }

        break if attempt < max_attempts {
            Ok(())
        } else {
            Err(err)
        };
    };

    assert!(result.is_err());
}

// ============================================================================
// Configuration Tests
// ============================================================================

#[test]
fn test_benzinga_config_validation() {
    let config = HashMap::from([
        ("api_token", "valid_token_12345"),
        ("max_events_per_request", "100"),
        ("update_interval_seconds", "60"),
    ]);

    assert!(!config["api_token"].is_empty());
    assert!(config["max_events_per_request"].parse::<u32>().is_ok());
}

// ============================================================================
// Serialization Tests
// ============================================================================

#[test]
fn test_benzinga_news_event_serialization() {
    use serde_json;

    let event = NewsEvent {
        symbol: Some(Symbol::from("AAPL")),
        symbols: vec![Symbol::from("AAPL")],
        story_id: "BZ123499".to_string(),
        headline: "Test".to_string(),
        content: "Test content".to_string(),
        summary: "Test summary".to_string(),
        category: "News".to_string(),
        tags: vec![],
        impact_score: None,
        importance: 0.5,
        author: "Benzinga Staff".to_string(),
        timestamp: Utc::now(),
        published_at: Utc::now(),
        source: "Benzinga".to_string(),
        url: "https://benzinga.com/news/123499".to_string(),
        sentiment_score: None,
        #[allow(deprecated)]
        sentiment: None,
        event_type: NewsEventType::News,
    };

    let json = serde_json::to_string(&event).unwrap();
    let deserialized: NewsEvent = serde_json::from_str(&json).unwrap();

    assert_eq!(event.headline, deserialized.headline);
    assert_eq!(event.content, deserialized.content);
    assert_eq!(event.source, deserialized.source);
}
