//! # Benzinga Streaming Provider
//!
//! Real-time WebSocket streaming provider for Benzinga Pro API, focusing on news, sentiment,
//! analyst ratings, and unusual options activity. This provider implements high-frequency
//! data streaming with automatic reconnection and event normalization.
//!
//! ## Features
//!
//! - **Real-time News**: Breaking financial news with impact scoring
//! - **Sentiment Analysis**: AI-powered sentiment scores for symbols
//! - **Analyst Ratings**: Upgrades, downgrades, and price target changes
//! - **Unusual Options**: Detection of unusual options flow and large trades
//! - **Auto Reconnection**: Exponential backoff with circuit breaker
//! - **Event Normalization**: Unified MarketDataEvent format for trading pipeline
//!
//! ## Usage
//!
//! ```rust,no_run
//! use data::providers::benzinga::streaming::BenzingaStreamingProvider;
//! use data::providers::traits::RealTimeProvider;
//! use common::Symbol;
//!
//! # async fn example() -> anyhow::Result<()> {
//! let config = BenzingaStreamingConfig {
//!     api_key: "your-api-key".to_string(),
//!     ..Default::default()
//! };
//!
//! let mut provider = BenzingaStreamingProvider::new(config)?;
//! provider.connect().await?;
//! provider.subscribe(vec![Symbol::from("AAPL"), Symbol::from("SPY")]).await?;
//!
//! let mut stream = provider.stream().await?;
//! while let Some(event) = stream.next().await {
//!     // Process news, sentiment, and ratings events
//!     println!("Received: {:?}", event);
//! }
//! # Ok(())
//! # }
//! ```

use crate::error::{DataError, Result};
use crate::providers::common::{
    AnalystRatingEvent, NewsEvent, NewsEventType, OptionsContract, OptionsSentiment, OptionsType,
    RatingAction, SentimentEvent, SentimentPeriod, UnusualOptionsEvent, UnusualOptionsType,
};
use crate::providers::traits::{
    ConnectionState as TraitConnectionState, ConnectionStatus, RealTimeProvider,
};
use crate::types::ExtendedMarketDataEvent;
use async_trait::async_trait;
use chrono::{DateTime, NaiveDate, NaiveDateTime, Utc};
use common::error::ErrorCategory;
use common::{
    ConnectionEvent, ConnectionStatus as EventConnectionStatus, MarketDataEvent, Price, Quantity,
    Symbol,
};
use futures_util::{SinkExt, StreamExt};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::pin::Pin;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::net::TcpStream;
use tokio::sync::{mpsc, Mutex, RwLock};
use tokio_stream::Stream;
use tokio_tungstenite::{connect_async, tungstenite::Message, MaybeTlsStream, WebSocketStream};
use tracing::{debug, error, info, warn};

/// Configuration for Benzinga streaming provider
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenzingaStreamingConfig {
    /// Benzinga Pro API key
    pub api_key: String,

    /// WebSocket endpoint URL
    pub websocket_url: String,

    /// Connection timeout in seconds
    pub connect_timeout_secs: u64,

    /// Ping interval in seconds
    pub ping_interval_secs: u64,

    /// Maximum reconnection attempts
    pub max_reconnect_attempts: u32,

    /// Initial reconnection delay in milliseconds
    pub initial_reconnect_delay_ms: u64,

    /// Maximum reconnection delay in milliseconds
    pub max_reconnect_delay_ms: u64,

    /// Reconnection backoff multiplier
    pub reconnect_backoff_multiplier: f64,

    /// Enable news alerts
    pub enable_news: bool,

    /// Enable sentiment updates
    pub enable_sentiment: bool,

    /// Enable analyst ratings
    pub enable_ratings: bool,

    /// Enable unusual options activity
    pub enable_options: bool,

    /// Buffer size for event channel
    pub event_buffer_size: usize,

    /// Heartbeat timeout in seconds
    pub heartbeat_timeout_secs: u64,
}

impl Default for BenzingaStreamingConfig {
    fn default() -> Self {
        let endpoints = config::BenzingaEndpoints::default();
        Self {
            api_key: std::env::var("BENZINGA_API_KEY").unwrap_or_default(),
            websocket_url: endpoints.websocket_url,
            connect_timeout_secs: 30,
            ping_interval_secs: 30,
            max_reconnect_attempts: 10,
            initial_reconnect_delay_ms: 1000,
            max_reconnect_delay_ms: 30000,
            reconnect_backoff_multiplier: 2.0,
            enable_news: true,
            enable_sentiment: true,
            enable_ratings: true,
            enable_options: true,
            event_buffer_size: 10000,
            heartbeat_timeout_secs: 60,
        }
    }
}

/// Benzinga WebSocket streaming provider
pub struct BenzingaStreamingProvider {
    /// Provider configuration
    config: BenzingaStreamingConfig,

    /// Current connection status
    connection_status: Arc<RwLock<ConnectionStatus>>,

    /// WebSocket connection
    websocket: Arc<Mutex<Option<WebSocketStream<MaybeTlsStream<TcpStream>>>>>,

    /// Event sender channel
    event_tx: Arc<Mutex<Option<mpsc::UnboundedSender<ExtendedMarketDataEvent>>>>,

    /// Event receiver channel for streaming
    event_rx: Arc<Mutex<Option<mpsc::UnboundedReceiver<ExtendedMarketDataEvent>>>>,

    /// Subscribed symbols
    subscribed_symbols: Arc<RwLock<HashSet<Symbol>>>,

    /// Reconnection state
    reconnect_attempt: Arc<Mutex<u32>>,

    /// Last heartbeat time
    last_heartbeat: Arc<Mutex<Instant>>,

    /// Connection metrics
    metrics: Arc<RwLock<ConnectionMetrics>>,

    /// Shutdown signal
    shutdown_tx: Arc<Mutex<Option<mpsc::UnboundedSender<()>>>>,
}

/// Connection metrics for monitoring
#[derive(Debug, Default)]
struct ConnectionMetrics {
    /// Total messages received
    messages_received: u64,

    /// Messages received per second (rolling average)
    messages_per_second: f64,

    /// Connection start time
    connection_start: Option<Instant>,

    /// Last message timestamp
    last_message_time: Option<DateTime<Utc>>,

    /// Error count
    error_count: u32,

    /// Reconnection count
    reconnection_count: u32,
}

/// Benzinga WebSocket message types
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type")]
enum BenzingaMessage {
    #[serde(rename = "news")]
    News(BenzingaNewsMessage),

    #[serde(rename = "sentiment")]
    Sentiment(BenzingaSentimentMessage),

    #[serde(rename = "rating")]
    Rating(BenzingaRatingMessage),

    #[serde(rename = "options")]
    Options(BenzingaOptionsMessage),

    #[serde(rename = "heartbeat")]
    Heartbeat(BenzingaHeartbeatMessage),

    #[serde(rename = "error")]
    Error(BenzingaErrorMessage),

    #[serde(rename = "subscription_confirmation")]
    SubscriptionConfirmation(BenzingaSubscriptionMessage),
}

/// Benzinga news message
#[derive(Debug, Serialize, Deserialize)]
struct BenzingaNewsMessage {
    /// Story ID
    pub story_id: String,

    /// Headline
    pub headline: String,

    /// Summary
    pub summary: Option<String>,

    /// Symbols
    pub tickers: Vec<String>,

    /// Category
    pub category: String,

    /// Tags
    pub tags: Vec<String>,

    /// Impact score (-1.0 to 1.0)
    pub impact_score: Option<f64>,

    /// Author
    pub author: Option<String>,

    /// Source
    pub source: String,

    /// Publication timestamp (ISO 8601)
    pub published_at: String,

    /// URL
    pub url: Option<String>,
}

/// Benzinga sentiment message
#[derive(Debug, Serialize, Deserialize)]
struct BenzingaSentimentMessage {
    /// Symbol
    pub ticker: String,

    /// Sentiment score (-1.0 to 1.0)
    pub sentiment_score: f64,

    /// Bullish percentage (0.0 to 1.0)
    pub bullish_ratio: f64,

    /// Bearish percentage (0.0 to 1.0)
    pub bearish_ratio: f64,

    /// Sample size
    pub sample_size: u32,

    /// Period
    pub period: String,

    /// Sources
    pub sources: Vec<String>,

    /// Confidence
    pub confidence: Option<f64>,

    /// Timestamp
    pub timestamp: String,
}

/// Benzinga rating message
#[derive(Debug, Serialize, Deserialize)]
struct BenzingaRatingMessage {
    /// Symbol
    pub ticker: String,

    /// Analyst name
    pub analyst: String,

    /// Firm name
    pub firm: String,

    /// Action (upgrade, downgrade, etc.)
    pub action: String,

    /// Current rating
    pub current_rating: String,

    /// Previous rating
    pub previous_rating: Option<String>,

    /// Price target
    pub price_target: Option<f64>,

    /// Previous price target
    pub previous_price_target: Option<f64>,

    /// Comment
    pub comment: Option<String>,

    /// Rating date
    pub rating_date: String,

    /// Timestamp
    pub timestamp: String,
}

/// Benzinga options message
#[derive(Debug, Serialize, Deserialize)]
struct BenzingaOptionsMessage {
    /// Underlying symbol
    pub ticker: String,

    /// Strike price
    pub strike: f64,

    /// Expiration date
    pub expiration: String,

    /// Option type (call/put)
    pub option_type: String,

    /// Activity type
    pub activity_type: String,

    /// Volume
    pub volume: u32,

    /// Open interest
    pub open_interest: Option<u32>,

    /// Premium
    pub premium: Option<f64>,

    /// Implied volatility
    pub implied_volatility: Option<f64>,

    /// Sentiment
    pub sentiment: String,

    /// Confidence
    pub confidence: f64,

    /// Description
    pub description: String,

    /// Timestamp
    pub timestamp: String,
}

/// Benzinga heartbeat message
#[derive(Debug, Serialize, Deserialize)]
struct BenzingaHeartbeatMessage {
    /// Server timestamp
    pub timestamp: String,

    /// Connection ID
    pub connection_id: Option<String>,
}

/// Benzinga error message
#[derive(Debug, Serialize, Deserialize)]
struct BenzingaErrorMessage {
    /// Error code
    pub code: String,

    /// Error message
    pub message: String,

    /// Additional details
    pub details: Option<String>,
}

/// Benzinga subscription message
#[derive(Debug, Serialize, Deserialize)]
struct BenzingaSubscriptionMessage {
    /// Subscription type
    pub subscription_type: String,

    /// Symbols
    pub tickers: Vec<String>,

    /// Status
    pub status: String,
}

/// WebSocket subscription request
#[derive(Debug, Serialize)]
struct SubscriptionRequest {
    /// Request type
    #[serde(rename = "type")]
    pub request_type: String,

    /// API key
    pub api_key: String,

    /// Symbols to subscribe to
    pub tickers: Vec<String>,

    /// Event types to subscribe to
    pub events: Vec<String>,
}

impl BenzingaStreamingProvider {
    /// Create a new Benzinga streaming provider
    pub fn new(config: BenzingaStreamingConfig) -> Result<Self> {
        if config.api_key.is_empty() {
            return Err(DataError::Configuration {
                field: "api_key".to_string(),
                message: "Benzinga API key is required".to_string(),
            });
        }

        let (event_tx, event_rx) = mpsc::unbounded_channel();

        Ok(Self {
            config,
            connection_status: Arc::new(RwLock::new(ConnectionStatus {
                state: TraitConnectionState::Disconnected,
                active_subscriptions: 0,
                events_per_second: 0.0,
                latency_micros: None,
                recent_error_count: 0,
                last_message_time: None,
                last_connection_attempt: None,
            })),
            websocket: Arc::new(Mutex::new(None)),
            event_tx: Arc::new(Mutex::new(Some(event_tx))),
            event_rx: Arc::new(Mutex::new(Some(event_rx))),
            subscribed_symbols: Arc::new(RwLock::new(HashSet::new())),
            reconnect_attempt: Arc::new(Mutex::new(0)),
            last_heartbeat: Arc::new(Mutex::new(Instant::now())),
            metrics: Arc::new(RwLock::new(ConnectionMetrics::default())),
            shutdown_tx: Arc::new(Mutex::new(None)),
        })
    }

    /// Establish WebSocket connection
    async fn connect_websocket(&self) -> Result<()> {
        let url = &self.config.websocket_url;

        info!("Connecting to Benzinga WebSocket: {}", url);

        let (ws_stream, response) = tokio::time::timeout(
            Duration::from_secs(self.config.connect_timeout_secs),
            connect_async(url),
        )
        .await
        .map_err(|_| DataError::timeout("WebSocket connection timeout"))?
        .map_err(|e| DataError::WebSocket(e))?;

        info!(
            "Connected to Benzinga WebSocket, response: {}",
            response.status()
        );

        // Store the WebSocket connection
        {
            let mut websocket = self.websocket.lock().await;
            *websocket = Some(ws_stream);
        }

        // Update connection status
        {
            let mut status = self.connection_status.write().await;
            status.state = TraitConnectionState::Connected;
            status.last_connection_attempt = Some(Utc::now());
        }

        // Update metrics
        {
            let mut metrics = self.metrics.write().await;
            metrics.connection_start = Some(Instant::now());
            metrics.reconnection_count += 1;
        }

        // Reset reconnection attempt counter
        {
            let mut attempt = self.reconnect_attempt.lock().await;
            *attempt = 0;
        }

        Ok(())
    }

    /// Send subscription request
    async fn send_subscription(&self, symbols: Vec<Symbol>) -> Result<()> {
        let mut events = Vec::new();

        if self.config.enable_news {
            events.push("news".to_string());
        }
        if self.config.enable_sentiment {
            events.push("sentiment".to_string());
        }
        if self.config.enable_ratings {
            events.push("ratings".to_string());
        }
        if self.config.enable_options {
            events.push("options".to_string());
        }

        let subscription = SubscriptionRequest {
            request_type: "subscribe".to_string(),
            api_key: self.config.api_key.clone(),
            tickers: symbols.iter().map(|s| s.to_string()).collect(),
            events,
        };

        let message =
            serde_json::to_string(&subscription).map_err(|e| DataError::Serialization {
                message: format!("Failed to serialize subscription: {}", e),
            })?;

        // Send subscription message
        {
            let mut websocket_guard = self.websocket.lock().await;
            if let Some(websocket) = websocket_guard.as_mut() {
                websocket
                    .send(Message::Text(message))
                    .await
                    .map_err(|e| DataError::WebSocket(e))?;

                debug!("Sent subscription for symbols: {:?}", symbols);
            } else {
                return Err(DataError::Connection("No WebSocket connection".to_string()));
            }
        }

        Ok(())
    }

    /// Start the message processing loop
    async fn start_message_loop(&self) -> Result<()> {
        let (shutdown_tx, mut shutdown_rx) = mpsc::unbounded_channel();

        // Store shutdown sender
        {
            let mut tx = self.shutdown_tx.lock().await;
            *tx = Some(shutdown_tx);
        }

        let websocket = self.websocket.clone();
        let event_tx = self.event_tx.clone();
        let connection_status = self.connection_status.clone();
        let metrics = self.metrics.clone();
        let last_heartbeat = self.last_heartbeat.clone();
        let heartbeat_timeout = Duration::from_secs(self.config.heartbeat_timeout_secs);

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

            loop {
                tokio::select! {
                    // Check for shutdown signal
                    _ = shutdown_rx.recv() => {
                        debug!("Received shutdown signal");
                        break;
                    }

                    // Handle heartbeat timeout
                    _ = heartbeat_interval.tick() => {
                        let last_beat = {
                            let guard = last_heartbeat.lock().await;
                            *guard
                        };

                        if last_beat.elapsed() > heartbeat_timeout {
                            error!("Heartbeat timeout detected");

                            // Update connection status
                            {
                                let mut status = connection_status.write().await;
                                status.state = TraitConnectionState::Failed;
                            }

                            // Send error event
                            if let Some(tx) = event_tx.lock().await.as_ref() {
                                let error_event = ExtendedMarketDataEvent::Core(MarketDataEvent::Error(common::ErrorEvent {
                                    provider: "benzinga".to_string(),
                                    message: "Heartbeat timeout".to_string(),
                                    category: ErrorCategory::Connection,
                                    timestamp: Utc::now(),
                                }));

                                let _ = tx.send(error_event);
                            }
                            break;
                        }
                    }

                    // Process WebSocket messages
                    message_result = async {
                        let mut websocket_guard = websocket.lock().await;
                        if let Some(ws) = websocket_guard.as_mut() {
                            ws.next().await
                        } else {
                            None
                        }
                    } => {
                        if let Some(message_result) = message_result {
                            match message_result {
                                Ok(message) => {
                                    if let Err(e) = Self::process_message(
                                        message,
                                        &event_tx,
                                        &metrics,
                                        &last_heartbeat
                                    ).await {
                                        error!("Failed to process message: {}", e);
                                    }
                                }
                                Err(e) => {
                                    error!("WebSocket error: {}", e);

                                    // Update connection status
                                    {
                                        let mut status = connection_status.write().await;
                                        status.state = TraitConnectionState::Failed;
                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
            }

            debug!("Message processing loop ended");
        });

        Ok(())
    }

    /// Process a WebSocket message
    async fn process_message(
        message: Message,
        event_tx: &Arc<Mutex<Option<mpsc::UnboundedSender<ExtendedMarketDataEvent>>>>,
        metrics: &Arc<RwLock<ConnectionMetrics>>,
        last_heartbeat: &Arc<Mutex<Instant>>,
    ) -> Result<()> {
        match message {
            Message::Text(text) => {
                debug!("Received text message: {}", text);

                // Update metrics
                {
                    let mut m = metrics.write().await;
                    m.messages_received += 1;
                    m.last_message_time = Some(Utc::now());

                    // Calculate messages per second (simple moving average)
                    if let Some(start_time) = m.connection_start {
                        let elapsed_secs = start_time.elapsed().as_secs_f64();
                        if elapsed_secs > 0.0 {
                            m.messages_per_second = m.messages_received as f64 / elapsed_secs;
                        }
                    }
                }

                // Parse and process the message
                match serde_json::from_str::<BenzingaMessage>(&text) {
                    Ok(benzinga_msg) => {
                        if let Some(market_event) =
                            Self::convert_benzinga_message(benzinga_msg).await?
                        {
                            // Send event to stream
                            if let Some(tx) = event_tx.lock().await.as_ref() {
                                if let Err(e) = tx.send(market_event) {
                                    error!("Failed to send market event: {}", e);
                                }
                            }
                        }
                    },
                    Err(e) => {
                        warn!(
                            "Failed to parse Benzinga message: {}. Raw message: {}",
                            e, text
                        );

                        // Update error metrics
                        {
                            let mut m = metrics.write().await;
                            m.error_count += 1;
                        }
                    },
                }
            },
            Message::Binary(_) => {
                warn!("Received unexpected binary message");
            },
            Message::Ping(_payload) => {
                debug!("Received ping, will send pong");
                // WebSocket library handles pong automatically
            },
            Message::Pong(_) => {
                debug!("Received pong");

                // Update heartbeat
                {
                    let mut heartbeat = last_heartbeat.lock().await;
                    *heartbeat = Instant::now();
                }
            },
            Message::Close(_) => {
                info!("Received close message");
                return Err(DataError::Connection(
                    "WebSocket closed by server".to_string(),
                ));
            },
            Message::Frame(_) => {
                // Internal frame, ignore
            },
        }

        Ok(())
    }

    /// Convert Benzinga message to ExtendedMarketDataEvent
    #[allow(deprecated)] // Needed for backward compatibility with deprecated fields
    async fn convert_benzinga_message(
        message: BenzingaMessage,
    ) -> Result<Option<ExtendedMarketDataEvent>> {
        match message {
            BenzingaMessage::News(news) => {
                let event = NewsEvent {
                    story_id: news.story_id,
                    headline: news.headline.clone(),
                    content: news.headline.clone(), // Use headline as content if not available
                    summary: news.summary.unwrap_or_default(),
                    symbol: news.tickers.first().map(|t| Symbol::from(t.clone())),
                    symbols: news.tickers.into_iter().map(Symbol::from).collect(),
                    category: news.category,
                    tags: news.tags,
                    impact_score: news.impact_score,
                    importance: news.impact_score.unwrap_or(0.5), // Default importance based on impact score
                    author: news.author.unwrap_or_default(),
                    source: news.source,
                    published_at: Self::parse_timestamp(&news.published_at)?,
                    timestamp: Utc::now(),
                    url: news.url.unwrap_or_default(),
                    sentiment_score: None,
                    sentiment: None, // No sentiment data available in basic streaming
                    event_type: NewsEventType::News, // Default to general news
                };

                Ok(Some(ExtendedMarketDataEvent::NewsAlert(event)))
            },

            BenzingaMessage::Sentiment(sentiment) => {
                let period = match sentiment.period.as_str() {
                    "realtime" | "real_time" => SentimentPeriod::RealTime,
                    "hourly" => SentimentPeriod::Hourly,
                    "daily" => SentimentPeriod::Daily,
                    "weekly" => SentimentPeriod::Weekly,
                    _ => SentimentPeriod::RealTime,
                };

                let event = SentimentEvent {
                    symbol: Symbol::from(sentiment.ticker),
                    sentiment_score: sentiment.sentiment_score,
                    bullish_ratio: sentiment.bullish_ratio,
                    bearish_ratio: sentiment.bearish_ratio,
                    sample_size: sentiment.sample_size,
                    period,
                    sources: sentiment.sources,
                    confidence: sentiment.confidence.unwrap_or(0.0),
                    source: "Benzinga".to_string(),
                    timestamp: Self::parse_timestamp(&sentiment.timestamp)?,
                };

                Ok(Some(ExtendedMarketDataEvent::SentimentUpdate(event)))
            },

            BenzingaMessage::Rating(rating) => {
                let action = match rating.action.as_str() {
                    "Upgrades" => RatingAction::Upgrade,
                    "Downgrades" => RatingAction::Downgrade,
                    "Initiates" => RatingAction::Initiate,
                    "Maintains" => RatingAction::Maintain,
                    "Discontinues" => RatingAction::Discontinue,
                    _ => RatingAction::Maintain,
                };

                let event = AnalystRatingEvent {
                    symbol: Symbol::from(rating.ticker),
                    analyst: rating.analyst,
                    firm: rating.firm,
                    action,
                    rating: rating.current_rating.clone(),
                    current_rating: rating.current_rating,
                    previous_rating: rating.previous_rating.unwrap_or_default(),
                    price_target: rating
                        .price_target
                        .and_then(|p| Decimal::from_f64_retain(p).map(Price::from)),
                    previous_price_target: rating
                        .previous_price_target
                        .and_then(|p| Decimal::from_f64_retain(p).map(Price::from)),
                    comment: rating.comment,
                    rating_date: Self::parse_timestamp(&rating.rating_date)?,
                    timestamp: Self::parse_timestamp(&rating.timestamp)?,
                };

                Ok(Some(ExtendedMarketDataEvent::AnalystRating(event)))
            },

            BenzingaMessage::Options(options) => {
                let option_type = match options.option_type.as_str() {
                    "call" | "Call" | "CALL" => OptionsType::Call,
                    "put" | "Put" | "PUT" => OptionsType::Put,
                    _ => OptionsType::Call,
                };

                let activity_type = match options.activity_type.as_str() {
                    "block" | "Block" => UnusualOptionsType::BlockTrade,
                    "sweep" | "Sweep" => UnusualOptionsType::Sweep,
                    "volume" | "Volume" => UnusualOptionsType::VolumeSpike,
                    "oi" | "open_interest" => UnusualOptionsType::OpenInterestSpike,
                    "iv" | "volatility" => UnusualOptionsType::VolatilitySpike,
                    _ => UnusualOptionsType::BlockTrade,
                };

                let sentiment = match options.sentiment.as_str() {
                    "bullish" | "Bullish" => OptionsSentiment::Bullish,
                    "bearish" | "Bearish" => OptionsSentiment::Bearish,
                    _ => OptionsSentiment::Neutral,
                };

                let expiration_date = NaiveDate::parse_from_str(&options.expiration, "%Y-%m-%d")
                    .map_err(|e| DataError::parse(format!("Invalid expiration date: {}", e)))?;
                let expiry = expiration_date.and_hms_opt(0, 0, 0)
                    .ok_or_else(|| DataError::parse("Failed to construct expiration time".to_string()))?
                    .and_utc();
                let expiration = expiry; // Deprecated: kept for compatibility

                let contract = OptionsContract {
                    symbol: Symbol::from(options.ticker.clone()),
                    expiry,
                    expiration,
                    strike: Price::from_decimal(
                        Decimal::from_f64_retain(options.strike).unwrap_or_default(),
                    ),
                    option_type,
                    multiplier: 100, // Standard equity options multiplier
                };

                let event = UnusualOptionsEvent {
                    symbol: Symbol::from(options.ticker),
                    contract,
                    unusual_type: activity_type,
                    activity_type,
                    volume: Quantity::new(options.volume as f64).unwrap_or(Quantity::zero()),
                    open_interest: Quantity::new(options.open_interest.unwrap_or(0) as f64)
                        .unwrap_or(Quantity::zero()),
                    premium: options.premium.map(|p| {
                        Price::from_decimal(Decimal::from_f64_retain(p).unwrap_or_default())
                    }),
                    implied_volatility: options.implied_volatility,
                    sentiment,
                    confidence: options.confidence,
                    description: options.description,
                    timestamp: Self::parse_timestamp(&options.timestamp)?,
                };

                Ok(Some(ExtendedMarketDataEvent::UnusualOptions(event)))
            },

            BenzingaMessage::Heartbeat(_) => {
                // Update heartbeat time - this is handled in the message processing loop
                Ok(None)
            },

            BenzingaMessage::Error(error) => {
                let category = match error.code.as_str() {
                    "AUTH_ERROR" => ErrorCategory::Authentication,
                    "RATE_LIMIT" => ErrorCategory::RateLimit,
                    "PARSE_ERROR" => ErrorCategory::Parse,
                    "SUBSCRIPTION_ERROR" => ErrorCategory::Subscription,
                    _ => ErrorCategory::Other,
                };

                let error_event = common::ErrorEvent {
                    provider: "benzinga".to_string(),
                    message: error.message,
                    category,
                    timestamp: Utc::now(),
                };

                Ok(Some(ExtendedMarketDataEvent::Core(MarketDataEvent::Error(
                    error_event,
                ))))
            },

            BenzingaMessage::SubscriptionConfirmation(_) => {
                // Log subscription confirmation but don't emit event
                debug!("Subscription confirmed");
                Ok(None)
            },
        }
    }

    /// Parse timestamp string to DateTime<Utc>
    fn parse_timestamp(timestamp_str: &str) -> Result<DateTime<Utc>> {
        // Try parsing with timezone information first
        let with_tz_formats = [
            "%Y-%m-%dT%H:%M:%S%z",    // 2024-01-15T10:30:00+00:00
            "%Y-%m-%dT%H:%M:%S%.f%z", // 2024-01-15T10:30:00.123+00:00
        ];

        for format in &with_tz_formats {
            if let Ok(dt) = DateTime::parse_from_str(timestamp_str, format) {
                return Ok(dt.with_timezone(&Utc));
            }
        }

        // Try parsing Z suffix timestamps (assume UTC)
        let z_suffix_formats = [
            "%Y-%m-%dT%H:%M:%SZ",    // 2024-01-15T10:30:00Z
            "%Y-%m-%dT%H:%M:%S%.fZ", // 2024-01-15T10:30:00.123Z
        ];

        for format in &z_suffix_formats {
            if let Ok(naive_dt) = NaiveDateTime::parse_from_str(
                timestamp_str.trim_end_matches('Z'),
                &format[..format.len() - 1], // Remove the 'Z' from format
            ) {
                return Ok(DateTime::from_naive_utc_and_offset(naive_dt, Utc));
            }
        }

        // Try parsing as naive datetime without timezone (assume UTC)
        let naive_formats = [
            "%Y-%m-%d %H:%M:%S",    // 2024-01-15 10:30:00
            "%Y-%m-%d %H:%M:%S%.f", // 2024-01-15 10:30:00.123
        ];

        for format in &naive_formats {
            if let Ok(naive_dt) = NaiveDateTime::parse_from_str(timestamp_str, format) {
                return Ok(DateTime::from_naive_utc_and_offset(naive_dt, Utc));
            }
        }

        Err(DataError::parse(format!(
            "Unable to parse timestamp: {}",
            timestamp_str
        )))
    }

    /// Handle reconnection with exponential backoff
    async fn reconnect(&self) -> Result<()> {
        let attempt = {
            let mut guard = self.reconnect_attempt.lock().await;
            *guard += 1;
            *guard
        };

        if attempt > self.config.max_reconnect_attempts {
            error!(
                "Maximum reconnection attempts ({}) exceeded",
                self.config.max_reconnect_attempts
            );

            // Update connection status to failed
            {
                let mut status = self.connection_status.write().await;
                status.state = TraitConnectionState::Failed;
            }

            return Err(DataError::Connection(
                "Max reconnection attempts exceeded".to_string(),
            ));
        }

        info!("Attempting reconnection #{}", attempt);

        // Update connection status to reconnecting
        {
            let mut status = self.connection_status.write().await;
            status.state = TraitConnectionState::Reconnecting;
            status.last_connection_attempt = Some(Utc::now());
        }

        // Calculate backoff delay
        let delay_ms = self.config.initial_reconnect_delay_ms as f64
            * self
                .config
                .reconnect_backoff_multiplier
                .powi((attempt - 1) as i32);
        let delay_ms = delay_ms.min(self.config.max_reconnect_delay_ms as f64) as u64;

        info!("Waiting {}ms before reconnection", delay_ms);
        tokio::time::sleep(Duration::from_millis(delay_ms)).await;

        // Attempt to reconnect
        match self.connect_websocket().await {
            Ok(()) => {
                info!("Reconnection successful");

                // Re-subscribe to existing symbols
                let symbols: Vec<Symbol> = {
                    let subscribed = self.subscribed_symbols.read().await;
                    subscribed.iter().cloned().collect()
                };

                if !symbols.is_empty() {
                    if let Err(e) = self.send_subscription(symbols).await {
                        error!("Failed to re-subscribe after reconnection: {}", e);
                    }
                }

                // Restart message loop
                self.start_message_loop().await?;

                Ok(())
            },
            Err(e) => {
                error!("Reconnection failed: {}", e);

                // Schedule another reconnection attempt (without tokio::spawn to avoid Send issues)
                let provider: Arc<Self> = Arc::new(self.clone());
                tokio::task::spawn_local(async move {
                    let _ = provider.reconnect().await;
                });

                Err(e)
            },
        }
    }
}

// Implement Clone for BenzingaStreamingProvider (for reconnection spawning)
impl Clone for BenzingaStreamingProvider {
    fn clone(&self) -> Self {
        Self {
            config: self.config.clone(),
            connection_status: self.connection_status.clone(),
            websocket: self.websocket.clone(),
            event_tx: self.event_tx.clone(),
            event_rx: self.event_rx.clone(),
            subscribed_symbols: self.subscribed_symbols.clone(),
            reconnect_attempt: self.reconnect_attempt.clone(),
            last_heartbeat: self.last_heartbeat.clone(),
            metrics: self.metrics.clone(),
            shutdown_tx: self.shutdown_tx.clone(),
        }
    }
}

#[async_trait]
impl RealTimeProvider for BenzingaStreamingProvider {
    async fn connect(&mut self) -> Result<()> {
        info!("Connecting to Benzinga streaming API");

        // Update connection status to connecting
        {
            let mut status = self.connection_status.write().await;
            status.state = TraitConnectionState::Connecting;
            status.last_connection_attempt = Some(Utc::now());
        }

        // Establish WebSocket connection
        self.connect_websocket().await?;

        // Start message processing loop
        self.start_message_loop().await?;

        // Send connection status event
        if let Some(tx) = self.event_tx.lock().await.as_ref() {
            let status_event =
                ExtendedMarketDataEvent::Core(MarketDataEvent::ConnectionStatus(ConnectionEvent {
                    provider: "benzinga".to_string(),
                    status: EventConnectionStatus::Connected,
                    message: Some("Connected to Benzinga streaming API".to_string()),
                    timestamp: Utc::now(),
                }));

            let _ = tx.send(status_event);
        }

        info!("Successfully connected to Benzinga streaming API");
        Ok(())
    }

    async fn disconnect(&mut self) -> Result<()> {
        info!("Disconnecting from Benzinga streaming API");

        // Send shutdown signal
        if let Some(tx) = self.shutdown_tx.lock().await.take() {
            let _ = tx.send(());
        }

        // Close WebSocket connection
        {
            let mut websocket = self.websocket.lock().await;
            if let Some(mut ws) = websocket.take() {
                let _ = ws.close(None).await;
            }
        }

        // Update connection status
        {
            let mut status = self.connection_status.write().await;
            status.state = TraitConnectionState::Disconnected;
        }

        // Clear subscriptions
        {
            let mut subscriptions = self.subscribed_symbols.write().await;
            subscriptions.clear();
        }

        // Send connection status event
        if let Some(tx) = self.event_tx.lock().await.as_ref() {
            let status_event =
                ExtendedMarketDataEvent::Core(MarketDataEvent::ConnectionStatus(ConnectionEvent {
                    provider: "benzinga".to_string(),
                    status: EventConnectionStatus::Disconnected,
                    message: Some("Disconnected from Benzinga streaming API".to_string()),
                    timestamp: Utc::now(),
                }));

            let _ = tx.send(status_event);
        }

        info!("Successfully disconnected from Benzinga streaming API");
        Ok(())
    }

    async fn subscribe(&mut self, symbols: Vec<Symbol>) -> Result<()> {
        if symbols.is_empty() {
            return Ok(());
        }

        info!("Subscribing to symbols: {:?}", symbols);

        // Send subscription request
        self.send_subscription(symbols.clone()).await?;

        // Update subscribed symbols
        {
            let mut subscriptions = self.subscribed_symbols.write().await;
            for symbol in &symbols {
                subscriptions.insert(symbol.clone());
            }
        }

        // Update connection status
        {
            let mut status = self.connection_status.write().await;
            status.active_subscriptions = {
                let subscriptions = self.subscribed_symbols.read().await;
                subscriptions.len()
            };
        }

        info!("Successfully subscribed to {} symbols", symbols.len());
        Ok(())
    }

    async fn unsubscribe(&mut self, symbols: Vec<Symbol>) -> Result<()> {
        if symbols.is_empty() {
            return Ok(());
        }

        info!("Unsubscribing from symbols: {:?}", symbols);

        // Remove from subscribed symbols
        {
            let mut subscriptions = self.subscribed_symbols.write().await;
            for symbol in &symbols {
                subscriptions.remove(symbol);
            }
        }

        // Send unsubscription request (similar to subscription but with "unsubscribe" type)
        let unsubscription = SubscriptionRequest {
            request_type: "unsubscribe".to_string(),
            api_key: self.config.api_key.clone(),
            tickers: symbols.iter().map(|s| s.to_string()).collect(),
            events: vec![
                "news".to_string(),
                "sentiment".to_string(),
                "ratings".to_string(),
                "options".to_string(),
            ],
        };

        let message =
            serde_json::to_string(&unsubscription).map_err(|e| DataError::Serialization {
                message: format!("Failed to serialize unsubscription: {}", e),
            })?;

        // Send unsubscription message
        {
            let mut websocket_guard = self.websocket.lock().await;
            if let Some(websocket) = websocket_guard.as_mut() {
                websocket
                    .send(Message::Text(message))
                    .await
                    .map_err(|e| DataError::WebSocket(e))?;

                debug!("Sent unsubscription for symbols: {:?}", symbols);
            }
        }

        // Update connection status
        {
            let mut status = self.connection_status.write().await;
            status.active_subscriptions = {
                let subscriptions = self.subscribed_symbols.read().await;
                subscriptions.len()
            };
        }

        info!("Successfully unsubscribed from {} symbols", symbols.len());
        Ok(())
    }

    async fn stream(&mut self) -> Result<Pin<Box<dyn Stream<Item = MarketDataEvent> + Send>>> {
        // Take the receiver from the arc mutex
        let receiver = {
            let mut rx_guard = self.event_rx.lock().await;
            rx_guard.take()
        };

        match receiver {
            Some(rx) => {
                let stream = tokio_stream::wrappers::UnboundedReceiverStream::new(rx).filter_map(
                    |extended_event| async move {
                        match extended_event {
                            ExtendedMarketDataEvent::Core(core_event) => Some(core_event),
                            // Provider-specific events are filtered out for the standard trait
                            _ => None,
                        }
                    },
                );
                Ok(Box::pin(stream))
            },
            None => Err(DataError::internal(
                "Event receiver already taken or not initialized",
            )),
        }
    }

    fn get_connection_status(&self) -> ConnectionStatus {
        // This is a blocking operation but should be fast
        tokio::task::block_in_place(|| {
            tokio::runtime::Handle::current().block_on(async {
                let status = self.connection_status.read().await;
                let metrics = self.metrics.read().await;

                ConnectionStatus {
                    state: status.state,
                    active_subscriptions: status.active_subscriptions,
                    events_per_second: metrics.messages_per_second,
                    latency_micros: None, // Not measurable for WebSocket
                    recent_error_count: metrics.error_count,
                    last_message_time: metrics.last_message_time,
                    last_connection_attempt: status.last_connection_attempt,
                }
            })
        })
    }

    fn get_provider_name(&self) -> &'static str {
        "benzinga"
    }
}

#[cfg(test)]
#[allow(clippy::assertions_on_result_states)]
mod tests {
    use super::*;

    #[test]
    fn test_config_creation() {
        let config = BenzingaStreamingConfig::default();
        assert!(!config.websocket_url.is_empty());
        assert!(config.connect_timeout_secs > 0);
        assert!(config.event_buffer_size > 0);
    }

    #[test]
    fn test_provider_creation() {
        let config = BenzingaStreamingConfig {
            api_key: "test-key".to_string(),
            ..Default::default()
        };

        let provider = BenzingaStreamingProvider::new(config);
        assert!(provider.is_ok());
    }

    #[test]
    fn test_provider_creation_without_api_key() {
        let config = BenzingaStreamingConfig {
            api_key: "".to_string(),
            ..Default::default()
        };

        let provider = BenzingaStreamingProvider::new(config);
        assert!(provider.is_err());
    }

    #[test]
    fn test_timestamp_parsing() {
        let timestamps = [
            "2024-01-15T10:30:00Z",
            "2024-01-15T10:30:00.123Z",
            "2024-01-15T10:30:00+00:00",
            "2024-01-15T10:30:00.123+00:00",
            "2024-01-15 10:30:00",
            "2024-01-15 10:30:00.123",
        ];

        for timestamp_str in &timestamps {
            let result = BenzingaStreamingProvider::parse_timestamp(timestamp_str);
            assert!(
                result.is_ok(),
                "Failed to parse timestamp: {}",
                timestamp_str
            );
        }
    }

    #[test]
    fn test_subscription_request_serialization() {
        let request = SubscriptionRequest {
            request_type: "subscribe".to_string(),
            api_key: "test-key".to_string(),
            tickers: vec!["AAPL".to_string(), "SPY".to_string()],
            events: vec!["news".to_string(), "sentiment".to_string()],
        };

        let json = serde_json::to_string(&request);
        assert!(json.is_ok());

        let json_str = json.unwrap();
        assert!(json_str.contains("subscribe"));
        assert!(json_str.contains("AAPL"));
        assert!(json_str.contains("news"));
    }

    #[tokio::test(flavor = "multi_thread")]
    async fn test_connection_status_tracking() {
        let config = BenzingaStreamingConfig {
            api_key: "test-key".to_string(),
            ..Default::default()
        };

        let provider = BenzingaStreamingProvider::new(config).unwrap();

        // Initial status should be disconnected
        let status = provider.get_connection_status();
        assert_eq!(status.state, TraitConnectionState::Disconnected);
        assert_eq!(status.active_subscriptions, 0);
    }

    #[test]
    fn test_benzinga_message_deserialization() {
        let news_json = r#"
        {
            "type": "news",
            "story_id": "12345",
            "headline": "Test Headline",
            "summary": "Test summary",
            "tickers": ["AAPL"],
            "category": "earnings",
            "tags": ["tech"],
            "impact_score": 0.75,
            "author": "Test Author",
            "source": "Benzinga",
            "published_at": "2024-01-15T10:30:00Z",
            "url": "https://example.com"
        }
        "#;

        let result: std::result::Result<BenzingaMessage, _> = serde_json::from_str(news_json);
        assert!(result.is_ok());

        if let Ok(BenzingaMessage::News(news)) = result {
            assert_eq!(news.story_id, "12345");
            assert_eq!(news.headline, "Test Headline");
            assert_eq!(news.tickers, vec!["AAPL"]);
        } else {
            panic!("Expected news message");
        }
    }
}
