//! Financial Validation for ML Predictions
//!
//! This module provides comprehensive validation for ML predictions
//! that will be used in financial trading decisions.

use common::types::Price;
use std::collections::HashMap;

use serde::{Deserialize, Serialize};
use tracing::{debug, warn};

// IntegerPrice replaced with common::Price

use super::{MLSafetyConfig, MLSafetyError, SafetyResult};

/// Financial validation configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FinancialValidationConfig {
    /// Maximum allowed price in USD
    pub max_price_usd: f64,
    /// Minimum allowed price in USD  
    pub min_price_usd: f64,
    /// Maximum allowed prediction change percentage
    pub max_change_percent: f64,
    /// Require prices to be positive
    pub require_positive_prices: bool,
    /// Maximum number of decimal places for prices
    pub max_decimal_places: u8,
    /// Enable range validation
    pub enable_range_validation: bool,
    /// Enable precision validation
    pub enable_precision_validation: bool,
}

impl Default for FinancialValidationConfig {
    fn default() -> Self {
        Self {
            max_price_usd: 1_000_000.0, // $1M max
            min_price_usd: 0.0001,      // $0.0001 min (0.01 cent)
            max_change_percent: 50.0,   // 50% max change
            require_positive_prices: true,
            max_decimal_places: 6,
            enable_range_validation: true,
            enable_precision_validation: true,
        }
    }
}

/// Financial validator for ML predictions
#[derive(Debug, Clone)]
pub struct FinancialValidator {
    config: MLSafetyConfig,
    financial_config: FinancialValidationConfig,
    historical_prices: HashMap<String, Vec<f64>>,
}

impl FinancialValidator {
    /// Create new financial validator
    pub fn new(config: &MLSafetyConfig) -> Self {
        Self {
            config: config.clone(),
            financial_config: FinancialValidationConfig::default(),
            historical_prices: HashMap::new(),
        }
    }

    /// Create with custom financial configuration
    pub fn with_financial_config(
        config: &MLSafetyConfig,
        financial_config: FinancialValidationConfig,
    ) -> Self {
        Self {
            config: config.clone(),
            financial_config,
            historical_prices: HashMap::new(),
        }
    }

    /// Validate a price prediction
    pub async fn validate_price(&self, prediction: f64, context: &str) -> SafetyResult<Price> {
        // Check for NaN/Infinity
        if self.config.nan_infinity_checks && !prediction.is_finite() {
            return Err(MLSafetyError::InvalidFloat {
                operation: format!("Price validation in {}: {}", context, prediction),
            });
        }

        // Range validation
        if self.financial_config.enable_range_validation {
            self.validate_price_range(prediction, context)?;
        }

        // Precision validation
        if self.financial_config.enable_precision_validation {
            self.validate_price_precision(prediction, context)?;
        }

        // Convert to safe financial type with proper error handling
        let integer_price =
            Price::from_f64(prediction).map_err(|e| MLSafetyError::FinancialValidation {
                reason: format!(
                    "Failed to convert validated price to Price type in {}: {} - {}",
                    context, prediction, e
                ),
            })?;

        debug!(
            "Price validation passed: {} = {:.6} -> {} (raw: {})",
            context,
            prediction,
            integer_price.to_f64(),
            integer_price.raw_value()
        );

        Ok(integer_price)
    }

    /// Validate price range
    fn validate_price_range(&self, price: f64, context: &str) -> SafetyResult<()> {
        // Check positive requirement
        if self.financial_config.require_positive_prices && price <= 0.0 {
            return Err(MLSafetyError::FinancialValidation {
                reason: format!(
                    "Negative or zero price in {}: {} (positive required)",
                    context, price
                ),
            });
        }

        // Check minimum price
        if price < self.financial_config.min_price_usd {
            return Err(MLSafetyError::FinancialValidation {
                reason: format!(
                    "Price too low in {}: {} < {} minimum",
                    context, price, self.financial_config.min_price_usd
                ),
            });
        }

        // Check maximum price
        if price > self.financial_config.max_price_usd {
            return Err(MLSafetyError::FinancialValidation {
                reason: format!(
                    "Price too high in {}: {} > {} maximum",
                    context, price, self.financial_config.max_price_usd
                ),
            });
        }

        Ok(())
    }

    /// Validate price precision
    fn validate_price_precision(&self, price: f64, context: &str) -> SafetyResult<()> {
        // Check decimal places
        let price_str = format!("{:.12}", price);
        if let Some(decimal_pos) = price_str.find('.') {
            let decimal_part = price_str.get(decimal_pos + 1..).unwrap_or_default();
            let significant_decimals = decimal_part.trim_end_matches('0').len();

            if significant_decimals > self.financial_config.max_decimal_places as usize {
                warn!(
                    "Price precision excessive in {}: {} has {} decimal places > {} limit",
                    context, price, significant_decimals, self.financial_config.max_decimal_places
                );
                // Don't error, just warn for precision issues
            }
        }

        // Validate against financial type scaling
        if let Ok(integer_price) = Price::from_f64(price) {
            let reconstructed = integer_price.to_f64();
            let precision_loss = (price - reconstructed).abs();

            if precision_loss > 1e-6 {
                warn!(
                    "Price precision loss in {}: {} -> {} (loss: {:.9})",
                    context, price, reconstructed, precision_loss
                );
            }
        } else {
            warn!(
                "Unable to convert price to Price type for precision validation in {}: {}",
                context, price
            );
        }

        Ok(())
    }

    /// Validate price change against historical data
    pub async fn validate_price_change(
        &mut self,
        symbol: &str,
        new_price: f64,
        context: &str,
    ) -> SafetyResult<f64> {
        // First validate the price itself
        self.validate_price(new_price, context).await?;

        // Get historical prices for this symbol
        let historical = self
            .historical_prices
            .entry(symbol.to_string())
            .or_insert_with(Vec::new);

        if historical.is_empty() {
            // No historical data, just store and accept
            historical.push(new_price);
            debug!(
                "No historical data for {}, accepting first price: {}",
                symbol, new_price
            );
            return Ok(0.0); // 0% change
        }

        // Calculate change from last price
        let last_price = *historical
            .last()
            .ok_or_else(|| MLSafetyError::FinancialValidation {
                reason: format!("Inconsistent historical data state for {}", symbol),
            })?;
        let change_percent = ((new_price - last_price) / last_price).abs() * 100.0;

        // Validate change isn't excessive
        if change_percent > self.financial_config.max_change_percent {
            return Err(MLSafetyError::FinancialValidation {
                reason: format!(
                    "Excessive price change for {} in {}: {:.2}% > {:.2}% limit (${:.6} -> ${:.6})",
                    symbol,
                    context,
                    change_percent,
                    self.financial_config.max_change_percent,
                    last_price,
                    new_price
                ),
            });
        }

        // Store new price (maintain window)
        historical.push(new_price);
        if historical.len() > 100 {
            historical.remove(0);
        }

        debug!(
            "Price change validation passed for {}: {:.2}% change (${:.6} -> ${:.6})",
            symbol, change_percent, last_price, new_price
        );

        Ok(change_percent)
    }

    /// Validate a batch of predictions
    pub async fn validate_predictions(
        &self,
        predictions: &[f64],
        context: &str,
    ) -> SafetyResult<Vec<Price>> {
        if predictions.is_empty() {
            return Err(MLSafetyError::FinancialValidation {
                reason: format!("Empty predictions in {}", context),
            });
        }

        let mut validated_prices = Vec::with_capacity(predictions.len());

        for (i, &prediction) in predictions.into_iter().enumerate() {
            let item_context = format!("{}[{}]", context, i);
            let validated = self.validate_price(prediction, &item_context).await?;
            validated_prices.push(validated);
        }

        debug!(
            "Batch validation passed: {} predictions in {}",
            predictions.len(),
            context
        );

        Ok(validated_prices)
    }

    /// Validate portfolio weights (must sum to 1.0)
    pub async fn validate_portfolio_weights(
        &self,
        weights: &[f64],
        context: &str,
    ) -> SafetyResult<()> {
        if weights.is_empty() {
            return Err(MLSafetyError::FinancialValidation {
                reason: format!("Empty portfolio weights in {}", context),
            });
        }

        // Check individual weights
        for (i, &weight) in weights.into_iter().enumerate() {
            if self.config.nan_infinity_checks && !weight.is_finite() {
                return Err(MLSafetyError::InvalidFloat {
                    operation: format!("Portfolio weight {}[{}]: {}", context, i, weight),
                });
            }

            if weight < 0.0 {
                return Err(MLSafetyError::FinancialValidation {
                    reason: format!(
                        "Negative portfolio weight in {}[{}]: {}",
                        context, i, weight
                    ),
                });
            }

            if weight > 1.0 {
                return Err(MLSafetyError::FinancialValidation {
                    reason: format!(
                        "Portfolio weight exceeds 100% in {}[{}]: {}",
                        context, i, weight
                    ),
                });
            }
        }

        // Check sum
        let sum = weights.iter().sum::<f64>();
        let sum_error = (sum - 1.0).abs();

        if sum_error > 1e-6 {
            return Err(MLSafetyError::FinancialValidation {
                reason: format!(
                    "Portfolio weights don't sum to 1.0 in {}: sum = {:.9} (error: {:.9})",
                    context, sum, sum_error
                ),
            });
        }

        debug!(
            "Portfolio weights validation passed: {} weights sum to {:.9}",
            weights.len(),
            sum
        );
        Ok(())
    }

    /// Validate risk metrics (VaR, volatility, etc.)
    pub async fn validate_risk_metric(
        &self,
        metric_value: f64,
        metric_name: &str,
        context: &str,
    ) -> SafetyResult<f64> {
        // Check for NaN/Infinity
        if self.config.nan_infinity_checks && !metric_value.is_finite() {
            return Err(MLSafetyError::InvalidFloat {
                operation: format!(
                    "Risk metric {} in {}: {}",
                    metric_name, context, metric_value
                ),
            });
        }

        // Validate based on metric type
        match metric_name.to_lowercase().as_str() {
            "var" | "value_at_risk" => {
                // VaR should be negative (loss) and reasonable
                if metric_value > 0.0 {
                    warn!(
                        "Positive VaR in {}: {} (should be negative for loss)",
                        context, metric_value
                    );
                }
                if metric_value.abs() > 1.0 {
                    return Err(MLSafetyError::FinancialValidation {
                        reason: format!(
                            "VaR too extreme in {}: {} (absolute value > 100%)",
                            context, metric_value
                        ),
                    });
                }
            },
            "volatility" | "vol" => {
                // Volatility should be positive and reasonable
                if metric_value < 0.0 {
                    return Err(MLSafetyError::FinancialValidation {
                        reason: format!("Negative volatility in {}: {}", context, metric_value),
                    });
                }
                if metric_value > 5.0 {
                    warn!(
                        "Extremely high volatility in {}: {} (> 500%)",
                        context, metric_value
                    );
                }
            },
            "sharpe_ratio" | "sharpe" => {
                // Sharpe ratio reasonable bounds
                if metric_value.abs() > 10.0 {
                    warn!(
                        "Extreme Sharpe ratio in {}: {} (absolute value > 10)",
                        context, metric_value
                    );
                }
            },
            "correlation" | "corr" => {
                // Correlation must be between -1 and 1
                if metric_value < -1.0 || metric_value > 1.0 {
                    return Err(MLSafetyError::FinancialValidation {
                        reason: format!(
                            "Invalid correlation in {}: {} (must be [-1, 1])",
                            context, metric_value
                        ),
                    });
                }
            },
            _ => {
                // Generic validation for unknown metrics
                if metric_value.abs() > 1e6 {
                    warn!(
                        "Large risk metric {} in {}: {} (absolute value > 1M)",
                        metric_name, context, metric_value
                    );
                }
            },
        }

        debug!(
            "Risk metric validation passed: {} = {:.6} in {}",
            metric_name, metric_value, context
        );

        Ok(metric_value)
    }

    /// Get financial validation statistics
    pub fn get_validation_stats(&self) -> HashMap<String, String> {
        let mut stats = HashMap::new();

        stats.insert(
            "max_price_usd".to_owned(),
            self.financial_config.max_price_usd.to_string(),
        );
        stats.insert(
            "min_price_usd".to_owned(),
            self.financial_config.min_price_usd.to_string(),
        );
        stats.insert(
            "max_change_percent".to_owned(),
            self.financial_config.max_change_percent.to_string(),
        );
        stats.insert(
            "require_positive".to_owned(),
            self.financial_config.require_positive_prices.to_string(),
        );
        stats.insert(
            "max_decimal_places".to_owned(),
            self.financial_config.max_decimal_places.to_string(),
        );
        stats.insert(
            "tracked_symbols".to_owned(),
            self.historical_prices.len().to_string(),
        );

        let total_historical_points: usize = self
            .historical_prices
            .values()
            .map(|prices| prices.len())
            .sum();
        stats.insert(
            "historical_data_points".to_owned(),
            total_historical_points.to_string(),
        );

        stats
    }

    /// Clear historical data for a symbol
    pub fn clear_symbol_history(&mut self, symbol: &str) {
        self.historical_prices.remove(symbol);
        debug!("Cleared historical data for symbol: {}", symbol);
    }

    /// Clear all historical data
    pub fn clear_all_history(&mut self) {
        self.historical_prices.clear();
        debug!("Cleared all historical price data");
    }
}

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

    fn create_test_validator() -> FinancialValidator {
        FinancialValidator::new(&MLSafetyConfig::default())
    }

    #[tokio::test]
    async fn test_price_validation() {
        let validator = create_test_validator();

        // Valid price
        let result = validator.validate_price(123.45, "test").await;
        assert!(result.is_ok());
        if let Ok(price) = result {
            assert_eq!(price.to_f64(), 123.45);
        }

        // Invalid prices
        assert!(validator.validate_price(f64::NAN, "test").await.is_err());
        assert!(validator.validate_price(-10.0, "test").await.is_err());
        assert!(validator.validate_price(2_000_000.0, "test").await.is_err());
    }

    #[tokio::test]
    async fn test_price_change_validation() {
        let mut validator = create_test_validator();

        // First price (no history)
        let change = match validator.validate_price_change("AAPL", 150.0, "test").await {
            Ok(change) => change,
            Err(e) => {
                error!("Unexpected validation error: {:?}", e);
                return;
            },
        };
        assert_eq!(change, 0.0);

        // Small change (should pass)
        let change = match validator.validate_price_change("AAPL", 155.0, "test").await {
            Ok(change) => change,
            Err(e) => {
                error!("Unexpected validation error: {:?}", e);
                return;
            },
        };
        assert!(change < 10.0);

        // Large change (should fail)
        let result = validator.validate_price_change("AAPL", 300.0, "test").await;
        assert!(result.is_err());
    }

    #[tokio::test]
    async fn test_portfolio_weights() {
        let validator = create_test_validator();

        // Valid weights
        let weights = vec![0.4, 0.3, 0.3];
        assert!(validator
            .validate_portfolio_weights(&weights, "test")
            .await
            .is_ok());

        // Invalid sum
        let bad_weights = vec![0.4, 0.3, 0.4];
        assert!(validator
            .validate_portfolio_weights(&bad_weights, "test")
            .await
            .is_err());

        // Negative weight
        let negative_weights = vec![0.6, -0.1, 0.5];
        assert!(validator
            .validate_portfolio_weights(&negative_weights, "test")
            .await
            .is_err());
    }

    #[tokio::test]
    async fn test_risk_metrics() {
        let validator = create_test_validator();

        // Valid VaR (negative)
        assert!(validator
            .validate_risk_metric(-0.05, "var", "test")
            .await
            .is_ok());

        // Valid volatility (positive)
        assert!(validator
            .validate_risk_metric(0.2, "volatility", "test")
            .await
            .is_ok());

        // Invalid correlation (out of bounds)
        assert!(validator
            .validate_risk_metric(1.5, "correlation", "test")
            .await
            .is_err());
        assert!(validator
            .validate_risk_metric(-1.5, "correlation", "test")
            .await
            .is_err());

        // Valid correlation
        assert!(validator
            .validate_risk_metric(0.75, "correlation", "test")
            .await
            .is_ok());
    }

    #[tokio::test]
    async fn test_batch_validation() {
        let validator = create_test_validator();

        let predictions = vec![100.0, 200.0, 150.0];
        let result = validator.validate_predictions(&predictions, "test").await;
        assert!(result.is_ok());
        if let Ok(validated) = result {
            assert_eq!(validated.len(), 3);
        }

        // With invalid prediction
        let bad_predictions = vec![100.0, f64::NAN, 150.0];
        assert!(validator
            .validate_predictions(&bad_predictions, "test")
            .await
            .is_err());
    }
}
