//! Dual Provider Mock Infrastructure
//!
//! Provides comprehensive mocking for Databento and Benzinga data providers
//! to enable reliable E2E testing without external API dependencies.

use anyhow::Result;
use async_trait::async_trait;
use serde_json::{json, Value};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tokio::sync::{RwLock, Mutex};
use tokio::time::{interval, sleep, Interval};
use tracing::{debug, info, warn};
use uuid::Uuid;
use wiremock::{
    Mock, MockServer, ResponseTemplate, Request, Respond, matchers::{method, path, query_param}
};

/// Mock data provider trait
#[async_trait]
pub trait MockDataProvider: Send + Sync {
    async fn start(&self) -> Result<()>;
    async fn stop(&self) -> Result<()>;
    async fn is_healthy(&self) -> bool;
    async fn simulate_failure(&self) -> Result<()>;
    async fn restore(&self) -> Result<()>;
    fn get_base_url(&self) -> String;
    fn get_provider_name(&self) -> &str;
}

/// Mock Databento provider
pub struct MockDatabentoProvider {
    server: Arc<Mutex<Option<MockServer>>>,
    is_running: Arc<RwLock<bool>>,
    is_failed: Arc<RwLock<bool>>,
    market_data_generator: Arc<Mutex<MarketDataGenerator>>,
}

impl MockDatabentoProvider {
    pub fn new() -> Self {
        Self {
            server: Arc::new(Mutex::new(None)),
            is_running: Arc::new(RwLock::new(false)),
            is_failed: Arc::new(RwLock::new(false)),
            market_data_generator: Arc::new(Mutex::new(MarketDataGenerator::new("databento"))),
        }
    }
    
    async fn setup_mocks(&self, server: &MockServer) -> Result<()> {
        // Mock health check endpoint
        Mock::given(method("GET"))
            .and(path("/health"))
            .respond_with(ResponseTemplate::new(200).set_body_json(json!({
                "status": "healthy",
                "provider": "databento",
                "timestamp": SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs()
            })))
            .mount(server)
            .await;
        
        // Mock market data streaming endpoint
        let market_generator = Arc::clone(&self.market_data_generator);
        let is_failed = Arc::clone(&self.is_failed);
        
        Mock::given(method("GET"))
            .and(path("/v0/timeseries.get_range"))
            .respond_with(DatabentoPriceDataResponder::new(market_generator, is_failed))
            .mount(server)
            .await;
        
        // Mock real-time subscription endpoint
        Mock::given(method("POST"))
            .and(path("/v0/timeseries.subscribe"))
            .respond_with(DatabentoBatchResponder::new(Arc::clone(&self.market_data_generator), Arc::clone(&self.is_failed)))
            .mount(server)
            .await;
        
        // Mock historical data endpoint
        Mock::given(method("GET"))
            .and(path("/v0/timeseries.get_range"))
            .and(query_param("start", wiremock::matchers::any()))
            .respond_with(DatabentoPriceDataResponder::new(Arc::clone(&self.market_data_generator), Arc::clone(&self.is_failed)))
            .mount(server)
            .await;
        
        info!("Databento mock endpoints configured");
        Ok(())
    }
}

#[async_trait]
impl MockDataProvider for MockDatabentoProvider {
    async fn start(&self) -> Result<()> {
        let server = MockServer::start().await;
        self.setup_mocks(&server).await?;
        
        let mut server_guard = self.server.lock().await;
        *server_guard = Some(server);
        
        let mut running = self.is_running.write().await;
        *running = true;
        
        info!("Mock Databento provider started on {}", self.get_base_url());
        Ok(())
    }
    
    async fn stop(&self) -> Result<()> {
        let mut server_guard = self.server.lock().await;
        *server_guard = None;
        
        let mut running = self.is_running.write().await;
        *running = false;
        
        info!("Mock Databento provider stopped");
        Ok(())
    }
    
    async fn is_healthy(&self) -> bool {
        *self.is_running.read().await && !*self.is_failed.read().await
    }
    
    async fn simulate_failure(&self) -> Result<()> {
        let mut failed = self.is_failed.write().await;
        *failed = true;
        warn!("Databento provider failure simulated");
        Ok(())
    }
    
    async fn restore(&self) -> Result<()> {
        let mut failed = self.is_failed.write().await;
        *failed = false;
        info!("Databento provider restored");
        Ok(())
    }
    
    fn get_base_url(&self) -> String {
        "http://127.0.0.1:3001".to_string()
    }
    
    fn get_provider_name(&self) -> &str {
        "databento"
    }
}

/// Mock Benzinga provider
pub struct MockBenzingaProvider {
    server: Arc<Mutex<Option<MockServer>>>,
    is_running: Arc<RwLock<bool>>,
    is_failed: Arc<RwLock<bool>>,
    news_generator: Arc<Mutex<NewsGenerator>>,
    market_data_generator: Arc<Mutex<MarketDataGenerator>>,
}

impl MockBenzingaProvider {
    pub fn new() -> Self {
        Self {
            server: Arc::new(Mutex::new(None)),
            is_running: Arc::new(RwLock::new(false)),
            is_failed: Arc::new(RwLock::new(false)),
            news_generator: Arc::new(Mutex::new(NewsGenerator::new())),
            market_data_generator: Arc::new(Mutex::new(MarketDataGenerator::new("benzinga"))),
        }
    }
    
    async fn setup_mocks(&self, server: &MockServer) -> Result<()> {
        // Mock health check endpoint
        Mock::given(method("GET"))
            .and(path("/health"))
            .respond_with(ResponseTemplate::new(200).set_body_json(json!({
                "status": "healthy",
                "provider": "benzinga",
                "timestamp": SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs()
            })))
            .mount(server)
            .await;
        
        // Mock news endpoint
        let news_generator = Arc::clone(&self.news_generator);
        let is_failed = Arc::clone(&self.is_failed);
        
        Mock::given(method("GET"))
            .and(path("/api/v2/news"))
            .respond_with(BenzingaNewsResponder::new(news_generator, is_failed))
            .mount(server)
            .await;
        
        // Mock quote endpoint
        Mock::given(method("GET"))
            .and(path("/api/v1/quoteDelayed"))
            .respond_with(BenzingaQuoteResponder::new(Arc::clone(&self.market_data_generator), Arc::clone(&self.is_failed)))
            .mount(server)
            .await;
        
        // Mock real-time quotes WebSocket simulation
        Mock::given(method("GET"))
            .and(path("/api/v1/quotes/stream"))
            .respond_with(BenzingaStreamResponder::new(Arc::clone(&self.market_data_generator), Arc::clone(&self.is_failed)))
            .mount(server)
            .await;
        
        info!("Benzinga mock endpoints configured");
        Ok(())
    }
}

#[async_trait]
impl MockDataProvider for MockBenzingaProvider {
    async fn start(&self) -> Result<()> {
        let server = MockServer::start().await;
        self.setup_mocks(&server).await?;
        
        let mut server_guard = self.server.lock().await;
        *server_guard = Some(server);
        
        let mut running = self.is_running.write().await;
        *running = true;
        
        info!("Mock Benzinga provider started on {}", self.get_base_url());
        Ok(())
    }
    
    async fn stop(&self) -> Result<()> {
        let mut server_guard = self.server.lock().await;
        *server_guard = None;
        
        let mut running = self.is_running.write().await;
        *running = false;
        
        info!("Mock Benzinga provider stopped");
        Ok(())
    }
    
    async fn is_healthy(&self) -> bool {
        *self.is_running.read().await && !*self.is_failed.read().await
    }
    
    async fn simulate_failure(&self) -> Result<()> {
        let mut failed = self.is_failed.write().await;
        *failed = true;
        warn!("Benzinga provider failure simulated");
        Ok(())
    }
    
    async fn restore(&self) -> Result<()> {
        let mut failed = self.is_failed.write().await;
        *failed = false;
        info!("Benzinga provider restored");
        Ok(())
    }
    
    fn get_base_url(&self) -> String {
        "http://127.0.0.1:3002".to_string()
    }
    
    fn get_provider_name(&self) -> &str {
        "benzinga"
    }
}

/// Market data generator for mock providers
pub struct MarketDataGenerator {
    provider_name: String,
    base_prices: HashMap<String, f64>,
    price_trends: HashMap<String, f64>,
    last_update: HashMap<String, SystemTime>,
}

impl MarketDataGenerator {
    pub fn new(provider_name: &str) -> Self {
        let mut base_prices = HashMap::new();
        base_prices.insert("AAPL".to_string(), 150.0);
        base_prices.insert("GOOGL".to_string(), 2500.0);
        base_prices.insert("MSFT".to_string(), 300.0);
        base_prices.insert("TSLA".to_string(), 800.0);
        base_prices.insert("EURUSD".to_string(), 1.0800);
        base_prices.insert("GBPUSD".to_string(), 1.2500);
        
        Self {
            provider_name: provider_name.to_string(),
            base_prices,
            price_trends: HashMap::new(),
            last_update: HashMap::new(),
        }
    }
    
    pub fn generate_market_data(&mut self, symbol: &str) -> Value {
        let base_price = self.base_prices.get(symbol).copied().unwrap_or(100.0);
        
        // Apply random walk with mean reversion
        let trend = self.price_trends.get(symbol).copied().unwrap_or(0.0);
        let random_change = (rand::random::<f64>() - 0.5) * 0.01; // ±0.5% random change
        let mean_reversion = -trend * 0.1; // 10% mean reversion
        let total_change = random_change + mean_reversion;
        
        let new_price = base_price * (1.0 + total_change);
        let spread = base_price * 0.0001; // 1bp spread
        
        let bid = new_price - spread / 2.0;
        let ask = new_price + spread / 2.0;
        let volume = (rand::random::<f64>() * 10000.0) as u64 + 1000;
        
        // Update internal state
        self.base_prices.insert(symbol.to_string(), new_price);
        self.price_trends.insert(symbol.to_string(), total_change);
        self.last_update.insert(symbol.to_string(), SystemTime::now());
        
        let timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_nanos() as u64;
        
        json!({
            "symbol": symbol,
            "provider": self.provider_name,
            "bid": format!("{:.4}", bid),
            "ask": format!("{:.4}", ask),
            "last": format!("{:.4}", new_price),
            "volume": volume,
            "timestamp": timestamp,
            "provider_timestamp": timestamp,
            "exchange": match symbol {
                s if s.contains("USD") => "FX",
                _ => "NASDAQ"
            }
        })
    }
    
    pub fn generate_historical_batch(&mut self, symbol: &str, count: usize) -> Vec<Value> {
        (0..count)
            .map(|_| {
                sleep(Duration::from_millis(1)); // Small delay for timestamp variation
                self.generate_market_data(symbol)
            })
            .collect()
    }
}

/// News generator for Benzinga mock
pub struct NewsGenerator {
    headlines: Vec<String>,
    symbols: Vec<String>,
}

impl NewsGenerator {
    pub fn new() -> Self {
        let headlines = vec![
            "Company reports strong quarterly earnings".to_string(),
            "New product launch announced".to_string(),
            "Major partnership deal signed".to_string(),
            "FDA approval received for new drug".to_string(),
            "Stock buyback program initiated".to_string(),
            "Dividend increased by 10%".to_string(),
            "Analyst upgrades price target".to_string(),
            "New factory construction begins".to_string(),
            "CEO announces expansion plans".to_string(),
            "Acquisition talks confirmed".to_string(),
        ];
        
        let symbols = vec![
            "AAPL".to_string(), "GOOGL".to_string(), "MSFT".to_string(),
            "TSLA".to_string(), "AMZN".to_string(), "META".to_string(),
        ];
        
        Self { headlines, symbols }
    }
    
    pub fn generate_news(&self) -> Value {
        let headline = &self.headlines[rand::random::<usize>() % self.headlines.len()];
        let symbol = &self.symbols[rand::random::<usize>() % self.symbols.len()];
        let sentiment = (rand::random::<f64>() - 0.5) * 2.0; // -1.0 to 1.0
        
        let timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();
        
        json!({
            "id": Uuid::new_v4().to_string(),
            "headline": format!("{} - {}", symbol, headline),
            "summary": format!("News about {} regarding {}", symbol, headline.to_lowercase()),
            "symbols": [symbol],
            "sentiment_score": sentiment,
            "timestamp": timestamp,
            "provider": "benzinga",
            "category": "earnings",
            "importance": rand::random::<u8>() % 5 + 1,
            "url": format!("https://mock-benzinga.com/news/{}", Uuid::new_v4())
        })
    }
    
    pub fn generate_news_batch(&self, count: usize) -> Vec<Value> {
        (0..count).map(|_| self.generate_news()).collect()
    }
}

/// Custom responder for Databento price data
struct DatabentoPriceDataResponder {
    market_generator: Arc<Mutex<MarketDataGenerator>>,
    is_failed: Arc<RwLock<bool>>,
}

impl DatabentoPriceDataResponder {
    fn new(market_generator: Arc<Mutex<MarketDataGenerator>>, is_failed: Arc<RwLock<bool>>) -> Self {
        Self { market_generator, is_failed }
    }
}

impl Respond for DatabentoPriceDataResponder {
    fn respond(&self, request: &Request) -> ResponseTemplate {
        let rt = tokio::runtime::Handle::current();
        
        rt.block_on(async {
            let is_failed = *self.is_failed.read().await;
            if is_failed {
                return ResponseTemplate::new(503).set_body_json(json!({
                    "error": "Service temporarily unavailable"
                }));
            }
            
            let symbol = request.url.query_pairs()
                .find(|(key, _)| key == "symbols")
                .map(|(_, value)| value.to_string())
                .unwrap_or_else(|| "AAPL".to_string());
            
            let mut generator = self.market_generator.lock().await;
            let data = generator.generate_market_data(&symbol);
            
            ResponseTemplate::new(200).set_body_json(json!({
                "data": [data],
                "metadata": {
                    "provider": "databento",
                    "timestamp": SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs()
                }
            }))
        })
    }
}

/// Custom responder for Databento batch data
struct DatabentoBatchResponder {
    market_generator: Arc<Mutex<MarketDataGenerator>>,
    is_failed: Arc<RwLock<bool>>,
}

impl DatabentoBatchResponder {
    fn new(market_generator: Arc<Mutex<MarketDataGenerator>>, is_failed: Arc<RwLock<bool>>) -> Self {
        Self { market_generator, is_failed }
    }
}

impl Respond for DatabentoBatchResponder {
    fn respond(&self, _request: &Request) -> ResponseTemplate {
        let rt = tokio::runtime::Handle::current();
        
        rt.block_on(async {
            let is_failed = *self.is_failed.read().await;
            if is_failed {
                return ResponseTemplate::new(503).set_body_json(json!({
                    "error": "Service temporarily unavailable"
                }));
            }
            
            let mut generator = self.market_generator.lock().await;
            let batch_data = generator.generate_historical_batch("AAPL", 10);
            
            ResponseTemplate::new(200).set_body_json(json!({
                "data": batch_data,
                "metadata": {
                    "provider": "databento",
                    "batch_size": batch_data.len()
                }
            }))
        })
    }
}

/// Custom responder for Benzinga news
struct BenzingaNewsResponder {
    news_generator: Arc<Mutex<NewsGenerator>>,
    is_failed: Arc<RwLock<bool>>,
}

impl BenzingaNewsResponder {
    fn new(news_generator: Arc<Mutex<NewsGenerator>>, is_failed: Arc<RwLock<bool>>) -> Self {
        Self { news_generator, is_failed }
    }
}

impl Respond for BenzingaNewsResponder {
    fn respond(&self, _request: &Request) -> ResponseTemplate {
        let rt = tokio::runtime::Handle::current();
        
        rt.block_on(async {
            let is_failed = *self.is_failed.read().await;
            if is_failed {
                return ResponseTemplate::new(503).set_body_json(json!({
                    "error": "Service temporarily unavailable"
                }));
            }
            
            let generator = self.news_generator.lock().await;
            let news_batch = generator.generate_news_batch(5);
            
            ResponseTemplate::new(200).set_body_json(json!({
                "data": news_batch,
                "metadata": {
                    "provider": "benzinga",
                    "count": news_batch.len()
                }
            }))
        })
    }
}

/// Custom responder for Benzinga quotes
struct BenzingaQuoteResponder {
    market_generator: Arc<Mutex<MarketDataGenerator>>,
    is_failed: Arc<RwLock<bool>>,
}

impl BenzingaQuoteResponder {
    fn new(market_generator: Arc<Mutex<MarketDataGenerator>>, is_failed: Arc<RwLock<bool>>) -> Self {
        Self { market_generator, is_failed }
    }
}

impl Respond for BenzingaQuoteResponder {
    fn respond(&self, request: &Request) -> ResponseTemplate {
        let rt = tokio::runtime::Handle::current();
        
        rt.block_on(async {
            let is_failed = *self.is_failed.read().await;
            if is_failed {
                return ResponseTemplate::new(503).set_body_json(json!({
                    "error": "Service temporarily unavailable"
                }));
            }
            
            let symbol = request.url.query_pairs()
                .find(|(key, _)| key == "symbols")
                .map(|(_, value)| value.to_string())
                .unwrap_or_else(|| "AAPL".to_string());
            
            let mut generator = self.market_generator.lock().await;
            let data = generator.generate_market_data(&symbol);
            
            ResponseTemplate::new(200).set_body_json(json!({
                "data": [data],
                "provider": "benzinga"
            }))
        })
    }
}

/// Custom responder for Benzinga streaming quotes
struct BenzingaStreamResponder {
    market_generator: Arc<Mutex<MarketDataGenerator>>,
    is_failed: Arc<RwLock<bool>>,
}

impl BenzingaStreamResponder {
    fn new(market_generator: Arc<Mutex<MarketDataGenerator>>, is_failed: Arc<RwLock<bool>>) -> Self {
        Self { market_generator, is_failed }
    }
}

impl Respond for BenzingaStreamResponder {
    fn respond(&self, _request: &Request) -> ResponseTemplate {
        let rt = tokio::runtime::Handle::current();
        
        rt.block_on(async {
            let is_failed = *self.is_failed.read().await;
            if is_failed {
                return ResponseTemplate::new(503).set_body_json(json!({
                    "error": "Stream temporarily unavailable"
                }));
            }
            
            // Simulate WebSocket-like streaming response
            let mut generator = self.market_generator.lock().await;
            let stream_data = generator.generate_historical_batch("AAPL", 3);
            
            ResponseTemplate::new(200)
                .set_body_json(json!({
                    "stream_id": Uuid::new_v4().to_string(),
                    "data": stream_data,
                    "provider": "benzinga"
                }))
                .insert_header("content-type", "application/x-ndjson")
        })
    }
}

/// Main dual-provider mock orchestrator
pub struct DualProviderMockOrchestrator {
    databento: Arc<MockDatabentoProvider>,
    benzinga: Arc<MockBenzingaProvider>,
    is_running: Arc<RwLock<bool>>,
}

impl DualProviderMockOrchestrator {
    pub fn new() -> Self {
        Self {
            databento: Arc::new(MockDatabentoProvider::new()),
            benzinga: Arc::new(MockBenzingaProvider::new()),
            is_running: Arc::new(RwLock::new(false)),
        }
    }
    
    pub async fn start_all(&self) -> Result<()> {
        info!("Starting dual-provider mock orchestrator");
        
        self.databento.start().await?;
        self.benzinga.start().await?;
        
        let mut running = self.is_running.write().await;
        *running = true;
        
        info!("✅ Dual-provider mock orchestrator started");
        info!("   Databento: {}", self.databento.get_base_url());
        info!("   Benzinga: {}", self.benzinga.get_base_url());
        
        Ok(())
    }
    
    pub async fn stop_all(&self) -> Result<()> {
        info!("Stopping dual-provider mock orchestrator");
        
        self.databento.stop().await?;
        self.benzinga.stop().await?;
        
        let mut running = self.is_running.write().await;
        *running = false;
        
        info!("✅ Dual-provider mock orchestrator stopped");
        Ok(())
    }
    
    pub async fn get_provider_status(&self) -> HashMap<String, bool> {
        let mut status = HashMap::new();
        status.insert("databento".to_string(), self.databento.is_healthy().await);
        status.insert("benzinga".to_string(), self.benzinga.is_healthy().await);
        status
    }
    
    pub async fn simulate_provider_failure(&self, provider: &str) -> Result<()> {
        match provider {
            "databento" => self.databento.simulate_failure().await?,
            "benzinga" => self.benzinga.simulate_failure().await?,
            _ => anyhow::bail!("Unknown provider: {}", provider),
        }
        Ok(())
    }
    
    pub async fn restore_provider(&self, provider: &str) -> Result<()> {
        match provider {
            "databento" => self.databento.restore().await?,
            "benzinga" => self.benzinga.restore().await?,
            _ => anyhow::bail!("Unknown provider: {}", provider),
        }
        Ok(())
    }
    
    pub fn get_provider_urls(&self) -> HashMap<String, String> {
        let mut urls = HashMap::new();
        urls.insert("databento".to_string(), self.databento.get_base_url());
        urls.insert("benzinga".to_string(), self.benzinga.get_base_url());
        urls
    }
}

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

#[cfg(test)]
mod tests {
    use super::*;
    
    #[tokio::test]
    async fn test_mock_orchestrator() {
        let orchestrator = DualProviderMockOrchestrator::new();
        
        // Test startup
        orchestrator.start_all().await.unwrap();
        
        let status = orchestrator.get_provider_status().await;
        assert!(status["databento"]);
        assert!(status["benzinga"]);
        
        // Test failure simulation
        orchestrator.simulate_provider_failure("databento").await.unwrap();
        let status = orchestrator.get_provider_status().await;
        assert!(!status["databento"]);
        assert!(status["benzinga"]);
        
        // Test restoration
        orchestrator.restore_provider("databento").await.unwrap();
        let status = orchestrator.get_provider_status().await;
        assert!(status["databento"]);
        assert!(status["benzinga"]);
        
        // Test shutdown
        orchestrator.stop_all().await.unwrap();
    }
    
    #[test]
    fn test_market_data_generator() {
        let mut generator = MarketDataGenerator::new("test");
        
        let data = generator.generate_market_data("AAPL");
        assert_eq!(data["symbol"], "AAPL");
        assert_eq!(data["provider"], "test");
        assert!(data["bid"].as_str().unwrap().parse::<f64>().unwrap() > 0.0);
        assert!(data["ask"].as_str().unwrap().parse::<f64>().unwrap() > 0.0);
    }
    
    #[test]
    fn test_news_generator() {
        let generator = NewsGenerator::new();
        
        let news = generator.generate_news();
        assert!(news["headline"].as_str().unwrap().len() > 0);
        assert!(news["symbols"].as_array().unwrap().len() > 0);
        assert!(news["sentiment_score"].as_f64().unwrap().abs() <= 1.0);
    }
}