//! Realistic market data simulation for stress testing

use common::types::Price;
use config::{ml_config::MarketCapTier, MLSymbolConfig as SymbolConfig, SimulationConfig};
use rust_decimal::Decimal;

use anyhow::Result;
use rand::prelude::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::{Duration, SystemTime};
use tokio::sync::mpsc;

use super::MarketDataUpdate;

/// Market condition types for simulation
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum MarketCondition {
    Normal,
    HighVolatility,
    Flash,
    Circuit,
    Opening,
    Closing,
}

/// Market data simulator configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SimulatorConfig {
    pub symbols: Vec<String>,
    pub update_rate_hz: u32,
    pub volatility: f64,
    pub trend: f64,
    /// Configuration-driven simulation settings
    pub simulation_config: Option<SimulationConfig>,
}

/// Realistic market data simulator
#[derive(Debug, Clone)]
pub struct MarketDataSimulator {
    config: SimulatorConfig,
    symbol_states: HashMap<String, SymbolState>,
}

#[derive(Debug, Clone)]
struct SymbolState {
    current_price: Price,
    bid: f64,
    ask: f64,
    volume: f64,
    last_update: SystemTime,
}

impl MarketDataSimulator {
    /// Create new market data simulator with configuration-driven approach
    pub fn new(config: SimulatorConfig) -> Result<Self> {
        let mut symbol_states = HashMap::new();

        // Get simulation configuration or use default
        let default_sim_config = SimulationConfig::default();
        let sim_config = config
            .simulation_config
            .as_ref()
            .unwrap_or(&default_sim_config);

        // Initialize symbol states using configuration-driven approach
        for symbol in &config.symbols {
            let symbol_config = sim_config
                .initial_market_state
                .symbols
                .get(symbol)
                .unwrap_or(&sim_config.initial_market_state.default_symbol);

            let starting_price = Price::from_f64(symbol_config.initial_price)
                .map_err(|e| anyhow::anyhow!("Invalid initial price for {}: {}", symbol, e))?;

            // Calculate realistic bid/ask spread based on configuration
            let spread_bps = (symbol_config.min_spread_bps + symbol_config.max_spread_bps) / 2.0;
            let spread = starting_price.to_f64() * spread_bps / 10000.0;

            symbol_states.insert(
                symbol.clone(),
                SymbolState {
                    current_price: starting_price,
                    bid: starting_price.to_f64() - spread / 2.0,
                    ask: starting_price.to_f64() + spread / 2.0,
                    volume: symbol_config.base_volume,
                    last_update: SystemTime::now(),
                },
            );
        }

        Ok(Self {
            config,
            symbol_states,
        })
    }

    /// Create simulator with full simulation configuration
    pub fn new_with_simulation_config(simulation_config: SimulationConfig) -> Result<Self> {
        let symbols: Vec<String> = simulation_config
            .initial_market_state
            .symbols
            .keys()
            .cloned()
            .collect();

        let config = SimulatorConfig {
            symbols,
            update_rate_hz: simulation_config.parameters.update_rate_hz,
            volatility: simulation_config.parameters.base_volatility,
            trend: simulation_config.parameters.trend,
            simulation_config: Some(simulation_config),
        };

        Self::new(config)
    }

    /// Generate test symbols based on configuration
    pub fn generate_test_symbols(simulation_config: &SimulationConfig) -> Vec<String> {
        let test_config = &simulation_config.test_symbols;
        (0..test_config.count)
            .map(|i| format!("{}{:03}", test_config.symbol_prefix, i + 1))
            .collect()
    }

    /// Start market data simulation
    pub async fn start_simulation(&mut self, tx: mpsc::Sender<MarketDataUpdate>) -> Result<()> {
        let update_interval = Duration::from_millis(1000 / self.config.update_rate_hz as u64);
        let mut rng = StdRng::from_entropy();

        loop {
            for symbol in &self.config.symbols.clone() {
                let update = self.generate_market_update(symbol, &mut rng)?;

                if tx.send(update).await.is_err() {
                    // Channel closed, stop simulation
                    return Ok(());
                }
            }

            tokio::time::sleep(update_interval).await;
        }
    }

    fn generate_market_update(
        &mut self,
        symbol: &str,
        rng: &mut StdRng,
    ) -> Result<MarketDataUpdate> {
        let state = self.symbol_states.get_mut(symbol).ok_or_else(|| {
            anyhow::anyhow!("Unknown symbol in market simulator: {}", symbol)
        })?;

        // Get symbol-specific configuration for realistic behavior
        let default_sim_config = SimulationConfig::default();
        let symbol_config = self
            .config
            .simulation_config
            .as_ref()
            .and_then(|sc| sc.initial_market_state.symbols.get(symbol))
            .unwrap_or(&default_sim_config.initial_market_state.default_symbol);

        // Generate price movement using geometric Brownian motion with symbol-specific volatility
        let dt = 1.0 / self.config.update_rate_hz as f64;
        let drift = self.config.trend * dt;
        let symbol_volatility = symbol_config.volatility * self.config.volatility;
        let diffusion =
            symbol_volatility * dt.sqrt() * rng.sample::<f64, _>(rand_distr::StandardNormal);

        // Update price
        let current_f64 = state.current_price.to_f64();
        let price_change = current_f64 * (drift + diffusion);
        let new_price = (current_f64 + price_change).max(0.01);
        state.current_price = Price::from_f64(new_price).unwrap_or_default();

        // Update bid/ask with realistic spread based on symbol configuration
        let spread_bps = rng.gen_range(symbol_config.min_spread_bps..=symbol_config.max_spread_bps);
        let current_f64 = state.current_price.to_f64();
        let spread = current_f64 * spread_bps / 10000.0;

        state.bid = current_f64 - spread / 2.0;
        state.ask = current_f64 + spread / 2.0;

        // Generate volume based on symbol configuration and market cap tier
        let base_volume = symbol_config.base_volume;
        let volume_multiplier = match symbol_config.market_cap_tier {
            MarketCapTier::LargeCap => rng.gen_range(0.5..2.0),
            MarketCapTier::MidCap => rng.gen_range(0.3..3.0),
            MarketCapTier::SmallCap => rng.gen_range(0.1..5.0),
            MarketCapTier::Test => rng.gen_range(0.1..2.0),
        };
        state.volume = base_volume * volume_multiplier;

        state.last_update = SystemTime::now();

        Ok(MarketDataUpdate {
            symbol: symbol.to_owned(),
            price: state.current_price,
            volume: Decimal::try_from(state.volume).unwrap_or(Decimal::ZERO),
            bid: Price::from_f64(state.bid)
                .map_err(|e| anyhow::anyhow!("Invalid bid price: {}", e))?,
            ask: Price::from_f64(state.ask)
                .map_err(|e| anyhow::anyhow!("Invalid ask price: {}", e))?,
            timestamp: state.last_update,
        })
    }

    /// Inject specific market condition with configuration-aware behavior
    pub fn inject_market_condition(&mut self, condition: MarketCondition) {
        let mut rng = thread_rng();

        match condition {
            MarketCondition::HighVolatility => {
                // Increase volatility temporarily based on symbol configuration
                let default_sim_config = SimulationConfig::default();
                for (symbol, state) in self.symbol_states.iter_mut() {
                    let symbol_config = self
                        .config
                        .simulation_config
                        .as_ref()
                        .and_then(|sc| sc.initial_market_state.symbols.get(symbol))
                        .unwrap_or(&default_sim_config.initial_market_state.default_symbol);

                    let volatility_multiplier = match symbol_config.market_cap_tier {
                        MarketCapTier::LargeCap => rng.gen_range(-0.03..0.03),
                        MarketCapTier::MidCap => rng.gen_range(-0.05..0.05),
                        MarketCapTier::SmallCap => rng.gen_range(-0.08..0.08),
                        MarketCapTier::Test => rng.gen_range(-0.05..0.05),
                    };

                    let current_f64 = state.current_price.to_f64();
                    let new_price = (current_f64 * (1.0 + volatility_multiplier)).max(0.01);
                    state.current_price = Price::from_f64(new_price).unwrap_or(state.current_price);
                }
            },
            MarketCondition::Flash => {
                // Simulate flash crash with symbol-specific impacts
                let default_sim_config = SimulationConfig::default();
                for (symbol, state) in self.symbol_states.iter_mut() {
                    let symbol_config = self
                        .config
                        .simulation_config
                        .as_ref()
                        .and_then(|sc| sc.initial_market_state.symbols.get(symbol))
                        .unwrap_or(&default_sim_config.initial_market_state.default_symbol);

                    let crash_magnitude = match symbol_config.market_cap_tier {
                        MarketCapTier::LargeCap => 0.97, // 3% drop for large caps
                        MarketCapTier::MidCap => 0.95,   // 5% drop for mid caps
                        MarketCapTier::SmallCap => 0.90, // 10% drop for small caps
                        MarketCapTier::Test => 0.95,     // 5% drop for test symbols
                    };

                    let current_f64 = state.current_price.to_f64();
                    let new_price = (current_f64 * crash_magnitude).max(0.01);
                    state.current_price = Price::from_f64(new_price).unwrap_or(state.current_price);
                }
            },
            MarketCondition::Circuit => {
                // Simulate circuit breaker - prices freeze
                // No price updates for this condition
            },
            MarketCondition::Normal | MarketCondition::Opening | MarketCondition::Closing => {
                // Normal conditions - no special handling
            },
        }
    }

    /// Get current symbol configuration for a given symbol
    pub fn get_symbol_config(&self, symbol: &str) -> SymbolConfig {
        self.config
            .simulation_config
            .as_ref()
            .and_then(|sc| sc.initial_market_state.symbols.get(symbol))
            .cloned()
            .unwrap_or_else(|| {
                SimulationConfig::default()
                    .initial_market_state
                    .default_symbol
            })
    }

    /// Update symbol configuration at runtime
    pub fn update_symbol_config(&mut self, symbol: String, config: SymbolConfig) -> Result<()> {
        if let Some(ref mut sim_config) = self.config.simulation_config {
            sim_config
                .initial_market_state
                .symbols
                .insert(symbol.clone(), config.clone());

            // Update existing state if symbol exists
            if let Some(state) = self.symbol_states.get_mut(&symbol) {
                // Optionally update current state based on new configuration
                let spread_bps = (config.min_spread_bps + config.max_spread_bps) / 2.0;
                let spread = state.current_price.to_f64() * spread_bps / 10000.0;
                state.bid = state.current_price.to_f64() - spread / 2.0;
                state.ask = state.current_price.to_f64() + spread / 2.0;
            }

            Ok(())
        } else {
            Err(anyhow::anyhow!(
                "No simulation configuration available for updates"
            ))
        }
    }
}
