//! Type System Bridge for ML-Financial Integration
//!
//! This module provides seamless conversion between ML computational types (f64/f32)
//! and canonical financial types (common::Price, common::Decimal). It ensures type
//! consistency across the ML-financial system boundary while maintaining computational
//! efficiency for pure ML operations.

use crate::{MLError, MLResult};
use common::Price;
use rust_decimal::prelude::FromPrimitive;
use rust_decimal::Decimal;

// Note: Using common::Price directly now, no alias needed

/// Conversion utilities for ML numeric types to financial types
#[derive(Debug)]
pub struct MLFinancialBridge;

impl MLFinancialBridge {
    /// Convert f64 ML value to common::Price with validation
    pub fn f64_to_price(value: f64) -> MLResult<Price> {
        Price::from_f64(value).map_err(|e| {
            MLError::InvalidInput(format!(
                "Price conversion failed for value {}: {}",
                value, e
            ))
        })
    }

    /// Convert f64 ML value to common::Price (fixed-point) with validation
    pub fn f64_to_common_price(value: f64) -> MLResult<Price> {
        Price::from_f64(value).map_err(|e| {
            MLError::InvalidInput(format!(
                "Common price conversion failed for value {}: {}",
                value, e
            ))
        })
    }

    /// Convert f32 ML value to common::Price with validation
    pub fn f32_to_price(value: f32) -> MLResult<Price> {
        Self::f64_to_price(value as f64)
    }

    /// Convert f32 ML value to common::Price (fixed-point) with validation
    pub fn f32_to_common_price(value: f32) -> MLResult<Price> {
        Self::f64_to_common_price(value as f64)
    }

    /// Convert f64 ML value to common::Decimal with validation
    pub fn f64_to_decimal(value: f64) -> MLResult<Decimal> {
        Decimal::from_f64(value).ok_or_else(|| {
            MLError::InvalidInput(format!(
                "Decimal conversion failed for f64 value: {}",
                value
            ))
        })
    }

    /// Convert f32 ML value to common::Decimal with validation
    pub fn f32_to_decimal(value: f32) -> MLResult<Decimal> {
        Self::f64_to_decimal(value as f64)
    }

    /// Convert common::Price to f64 for ML computations
    pub fn price_to_f64(price: &Price) -> f64 {
        price.to_f64()
    }

    /// Convert common::Price (fixed-point) to f64 for ML computations
    pub fn common_price_to_f64(price: &Price) -> f64 {
        price.to_f64()
    }

    /// Convert common::Price to f32 for ML computations
    pub fn price_to_f32(price: &Price) -> f32 {
        price.to_f64() as f32
    }

    /// Convert common::Price (fixed-point) to f32 for ML computations
    pub fn common_price_to_f32(price: &Price) -> f32 {
        price.to_f64() as f32
    }

    /// Convert common::Decimal to f64 for ML computations
    pub fn decimal_to_f64(decimal: &Decimal) -> MLResult<f64> {
        use rust_decimal::prelude::ToPrimitive;
        decimal.to_f64().ok_or_else(|| {
            MLError::InvalidInput(format!("Failed to convert Decimal {} to f64", decimal))
        })
    }

    /// Convert common::Decimal to f32 for ML computations
    pub fn decimal_to_f32(decimal: &Decimal) -> MLResult<f32> {
        use rust_decimal::prelude::ToPrimitive;
        decimal.to_f32().ok_or_else(|| {
            MLError::InvalidInput(format!("Failed to convert Decimal {} to f32", decimal))
        })
    }

    /// Batch convert f64 vector to Price vector
    pub fn f64_vec_to_prices(values: &[f64]) -> MLResult<Vec<Price>> {
        values.iter().map(|&v| Self::f64_to_price(v)).collect()
    }

    /// Batch convert Price vector to f64 vector
    pub fn prices_to_f64_vec(prices: &[Price]) -> Vec<f64> {
        prices.iter().map(Self::price_to_f64).collect()
    }

    /// Batch convert f64 vector to Decimal vector
    pub fn f64_vec_to_decimals(values: &[f64]) -> MLResult<Vec<Decimal>> {
        values.iter().map(|&v| Self::f64_to_decimal(v)).collect()
    }

    /// Batch convert Decimal vector to f64 vector
    pub fn decimals_to_f64_vec(decimals: &[Decimal]) -> MLResult<Vec<f64>> {
        decimals.iter().map(Self::decimal_to_f64).collect()
    }
}

/// Trait for types that can be converted to financial types
pub trait ToFinancial {
    /// Convert to common::Price
    fn to_price(&self) -> MLResult<Price>;

    /// Convert to common::Decimal
    fn to_decimal(&self) -> MLResult<Decimal>;
}

/// Trait for types that can be converted from financial types
pub trait FromFinancial<T> {
    /// Convert from common::Price
    fn from_price(price: &Price) -> Self;

    /// Convert from common::Decimal
    fn from_decimal(decimal: &Decimal) -> MLResult<Self>
    where
        Self: Sized;
}

impl ToFinancial for f64 {
    fn to_price(&self) -> MLResult<Price> {
        MLFinancialBridge::f64_to_price(*self)
    }

    fn to_decimal(&self) -> MLResult<Decimal> {
        MLFinancialBridge::f64_to_decimal(*self)
    }
}

impl ToFinancial for f32 {
    fn to_price(&self) -> MLResult<Price> {
        MLFinancialBridge::f32_to_price(*self)
    }

    fn to_decimal(&self) -> MLResult<Decimal> {
        MLFinancialBridge::f32_to_decimal(*self)
    }
}

impl FromFinancial<Price> for f64 {
    fn from_price(price: &Price) -> Self {
        MLFinancialBridge::price_to_f64(price)
    }

    fn from_decimal(decimal: &Decimal) -> MLResult<Self> {
        MLFinancialBridge::decimal_to_f64(decimal)
    }
}

impl FromFinancial<Price> for f32 {
    fn from_price(price: &Price) -> Self {
        MLFinancialBridge::price_to_f32(price)
    }

    fn from_decimal(decimal: &Decimal) -> MLResult<Self> {
        MLFinancialBridge::decimal_to_f32(decimal)
    }
}

/// Specialized converters for common ML use cases
pub mod converters {
    use super::*;

    /// Convert ML prediction results to financial format
    #[derive(Debug)]
    pub struct PredictionConverter;

    impl PredictionConverter {
        /// Convert ML model output (f64) to trading signal with Price
        pub fn prediction_to_signal(
            prediction: f64,
            confidence: f64,
            current_price: &Price,
        ) -> MLResult<(Price, Decimal)> {
            // Convert prediction to price change
            let price_change = prediction * MLFinancialBridge::price_to_f64(current_price);
            let new_price = MLFinancialBridge::f64_to_price(
                MLFinancialBridge::price_to_f64(current_price) + price_change,
            )?;

            let confidence_decimal = MLFinancialBridge::f64_to_decimal(confidence)?;

            Ok((new_price, confidence_decimal))
        }

        /// Convert order book features (f32 array) to Price/Decimal format
        pub fn features_to_financial(
            features: &[f32],
            feature_names: &[&str],
        ) -> MLResult<Vec<(String, Decimal)>> {
            features
                .iter()
                .zip(feature_names.iter())
                .map(|(&value, &name)| {
                    let decimal = MLFinancialBridge::f32_to_decimal(value)?;
                    Ok((name.to_string(), decimal))
                })
                .collect()
        }

        /// Convert portfolio weights (f64 array) to Decimal format
        pub fn weights_to_decimals(weights: &[f64]) -> MLResult<Vec<Decimal>> {
            MLFinancialBridge::f64_vec_to_decimals(weights)
        }
    }

    /// Convert financial data to ML format
    #[derive(Debug)]
    pub struct FinancialConverter;

    impl FinancialConverter {
        /// Convert price history to ML features (f64 array)
        pub fn prices_to_features(prices: &[Price]) -> Vec<f64> {
            MLFinancialBridge::prices_to_f64_vec(prices)
        }

        /// Convert price and volume data to ML input matrix
        pub fn market_data_to_matrix(
            prices: &[Price],
            volumes: &[Decimal],
        ) -> MLResult<Vec<Vec<f64>>> {
            let price_features = Self::prices_to_features(prices);
            let volume_features = MLFinancialBridge::decimals_to_f64_vec(volumes)?;

            Ok(price_features
                .into_iter()
                .zip(volume_features.into_iter())
                .map(|(p, v)| vec![p, v])
                .collect())
        }

        /// Normalize prices for ML input (log returns)
        pub fn prices_to_log_returns(prices: &[Price]) -> Vec<f64> {
            let price_values = Self::prices_to_features(prices);
            price_values
                .windows(2)
                .filter_map(|window| {
                    let p0 = window.get(0)?;
                    let p1 = window.get(1)?;
                    (*p0 > 0.0).then(|| (p1 / p0).ln())
                })
                .collect()
        }
    }
}

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

    #[test]
    fn test_f64_to_price_conversion() {
        let value = 123.45;
        let price = MLFinancialBridge::f64_to_price(value).unwrap();
        assert!((MLFinancialBridge::price_to_f64(&price) - value).abs() < 1e-8);
    }

    #[test]
    fn test_f64_to_decimal_conversion() {
        let value = 123.45;
        let decimal = MLFinancialBridge::f64_to_decimal(value).unwrap();
        let back_to_f64 = MLFinancialBridge::decimal_to_f64(&decimal).unwrap();
        assert!((back_to_f64 - value).abs() < 1e-10);
    }

    #[test]
    fn test_batch_conversions() {
        let values = vec![10.0, 20.0, 30.0];
        let prices = MLFinancialBridge::f64_vec_to_prices(&values).unwrap();
        let back_to_f64 = MLFinancialBridge::prices_to_f64_vec(&prices);

        for (original, converted) in values.into_iter().zip(back_to_f64.into_iter()) {
            assert!((original - converted).abs() < 1e-8);
        }
    }

    #[test]
    fn test_trait_implementations() {
        let value = 42.0_f64;
        let price = value.to_price().unwrap();
        let back_to_f64 = f64::from_price(&price);
        assert!((value - back_to_f64).abs() < 1e-8);
    }

    #[test]
    fn test_prediction_converter() {
        use converters::PredictionConverter;

        let current_price_decimal = Decimal::from_f64(100.0).unwrap();
        let current_price = Price::from(current_price_decimal);
        let prediction = 0.05; // 5% increase
        let confidence = 0.85;

        let (new_price, conf_decimal) =
            PredictionConverter::prediction_to_signal(prediction, confidence, &current_price)
                .unwrap();

        assert!((MLFinancialBridge::price_to_f64(&new_price) - 105.0).abs() < 1e-6);
        assert!((MLFinancialBridge::decimal_to_f64(&conf_decimal).unwrap() - 0.85).abs() < 1e-10);
    }

    #[test]
    fn test_financial_converter() {
        use converters::FinancialConverter;

        let prices: Vec<Price> = vec![
            Decimal::from_f64(100.0).unwrap(),
            Decimal::from_f64(105.0).unwrap(),
            Decimal::from_f64(110.0).unwrap(),
        ]
        .into_iter()
        .map(Price::from)
        .collect();

        let log_returns = FinancialConverter::prices_to_log_returns(&prices);
        assert_eq!(log_returns.len(), 2);
        let r0 = log_returns.get(0).copied().unwrap();
        let r1 = log_returns.get(1).copied().unwrap();
        assert!((r0 - (105.0_f64 / 100.0_f64).ln()).abs() < 1e-10);
        assert!((r1 - (110.0_f64 / 105.0_f64).ln()).abs() < 1e-10);
    }

    #[test]
    fn test_invalid_conversions() {
        // Test negative price conversion
        assert!(MLFinancialBridge::f64_to_price(-10.0).is_err());

        // Test NaN conversion
        assert!(MLFinancialBridge::f64_to_price(f64::NAN).is_err());

        // Test infinity conversion
        assert!(MLFinancialBridge::f64_to_price(f64::INFINITY).is_err());
    }
}
