//! Migration utilities for converting between primitive and unified types
//!
//! This module provides safe conversion functions to help migrate from
//! primitive types (f64, String) to unified types (Price, Symbol, etc.)

#![deny(
    clippy::unwrap_used,
    clippy::expect_used,
    clippy::panic,
    clippy::unimplemented,
    clippy::todo,
    clippy::unreachable
)]

use crate::basic::*;
use crate::clippy_compliant_patterns::*;
use crate::ConversionError;
// CANONICAL TYPE IMPORTS - Use types::prelude::Decimal
use trading_engine::str::FromStr;

/// Safe conversion from f64 to Price with error handling
pub fn f64_to_price(value: f64) -> Result<Price, ConversionError> {
    if value.is_nan() || value.is_infinite() || value < 0.0 {
        return Err(ConversionError::invalid_number(format!(
            "Invalid price value: {}",
            value
        )));
    }
    Price::from_f64(value).map_err(|e| ConversionError::type_conversion(e.to_string()))
}

/// Safe conversion from f64 to Quantity with error handling
pub fn f64_to_quantity(value: f64) -> Result<Quantity, ConversionError> {
    if value.is_nan() || value.is_infinite() || value < 0.0 {
        return Err(ConversionError::invalid_number(format!(
            "Invalid quantity value: {}",
            value
        )));
    }
    Quantity::from_f64(value).map_err(|e| ConversionError::type_conversion(e.to_string()))
}

/// Safe conversion from f64 to Volume with error handling
pub fn f64_to_volume(value: f64) -> Result<Volume, ConversionError> {
    if value.is_nan() || value.is_infinite() || value < 0.0 {
        return Err(ConversionError::invalid_number(format!(
            "Invalid volume value: {}",
            value
        )));
    }
    Decimal::try_from(value).map_err(|_| ConversionError::type_conversion("Invalid f64 to Decimal conversion".to_string()))
}

/// Safe conversion from `String` to Symbol with validation
pub fn string_to_symbol(value: &str) -> Result<Symbol, ConversionError> {
    if value.is_empty() {
        return Err(ConversionError::invalid_format(
            "Symbol cannot be empty".to_string()
        ));
    }
    if value.len() > 32 {
        return Err(ConversionError::invalid_format(format!(
            "Symbol too long: {} (max 32 characters)",
            value.len()
        )));
    }
    // Check for valid symbol characters (alphanumeric and some special chars)
    if !value.chars().all(|c| c.is_alphanumeric() || c == '.' || c == '-' || c == '_') {
        return Err(ConversionError::invalid_format(format!(
            "Invalid symbol format: {}",
            value
        )));
    }
    Ok(Symbol::from(value))
}

/// Safe conversion from `String` to `OrderId` with validation
pub fn string_to_order_id(value: &str) -> Result<OrderId, ConversionError> {
    if value.is_empty() {
        return Err(ConversionError::invalid_format(
            "Order ID cannot be empty".to_string()
        ));
    OrderId::from_str(value).map_err(|e| ConversionError::type_conversion(e.to_string()))
}

/// Safe conversion from `String` to TradeId with validation
pub fn string_to_trade_id(value: &str) -> Result<TradeId, ConversionError> {
    if value.is_empty() {
        return Err(ConversionError::invalid_format(
            "Trade ID cannot be empty".to_string()
        ));
    TradeId::from_str(value).map_err(|e| ConversionError::type_conversion(e.to_string()))
}

/// Safe conversion from `String` to AccountId with validation
pub fn string_to_account_id(value: &str) -> Result<AccountId, ConversionError> {
    if value.is_empty() {
        return Err(ConversionError::invalid_format(
            "Account ID cannot be empty".to_string()
        ));
    AccountId::from_str(value).map_err(|e| ConversionError::type_conversion(e.to_string()))
}

/// Safe conversion from `String` to `PositionId` with validation
pub fn string_to_position_id(value: &str) -> Result<PositionId, ConversionError> {
    if value.is_empty() {
        return Err(ConversionError::invalid_format(
            "Position ID cannot be empty".to_string()
        ));
    PositionId::from_str(value).map_err(|e| ConversionError::type_conversion(e.to_string()))
}

/// Safe conversion from `Decimal` to Price
pub fn decimal_to_price(value: Decimal) -> Result<Price, ConversionError> {
    if value.is_sign_negative() {
        return Err(ConversionError::invalid_number(
            "Price cannot be negative".to_string()
        ));
    Price::from_decimal(value).map_err(|e| ConversionError::type_conversion(e.to_string()))
}

/// Safe conversion from `Decimal` to Quantity
pub fn decimal_to_quantity(value: Decimal) -> Result<Quantity, ConversionError> {
    if value.is_sign_negative() {
        return Err(ConversionError::invalid_number(
            "Quantity cannot be negative".to_string()
        ));
    Quantity::from_decimal(value).map_err(|e| ConversionError::type_conversion(e.to_string()))
}

/// Safe conversion from `Decimal` to Volume
pub fn decimal_to_volume(value: Decimal) -> Result<Volume, ConversionError> {
    if value.is_sign_negative() {
        return Err(ConversionError::invalid_number(
            "Volume cannot be negative".to_string()
        ));
    Ok(value) // Decimal to Decimal is direct
}

/// Conversion helper struct for batch operations
pub struct TypeConverter;

impl TypeConverter {
    /// Convert a batch of f64 values to Prices
    pub fn batch_f64_to_prices(values: &[f64]) -> Vec<Result<Price, ConversionError>> {
        values.iter().map(|&v| f64_to_price(v)).collect()
    }

    /// Convert a batch of f64 values to Quantities
    pub fn batch_f64_to_quantities(values: &[f64]) -> Vec<Result<Quantity, ConversionError>> {
        values.iter().map(|&v| f64_to_quantity(v)).collect()
    }

    /// Convert a batch of Strings to Symbols
    pub fn batch_strings_to_symbols(values: &[String]) -> Vec<Result<Symbol, ConversionError>> {
        values.iter().map(|v| string_to_symbol(v)).collect()
    }

    /// Convert a `HashMap` of String->f64 to Symbol->Price
    pub fn convert_price_map(
        map: &core::collections::HashMap<String, f64>,
    ) -> Result<core::collections::HashMap<Symbol, Price>, ConversionError> {
        let mut result = core::collections::HashMap::new();
        for (key, &value) in map {
            let symbol = string_to_symbol(key)?;
            let price = f64_to_price(value)?;
            result.insert(symbol, price);
        }
    // Ok variant
        Ok(result)
    }

    /// Convert a `HashMap` of String->f64 to Symbol->Quantity
    pub fn convert_quantity_map(
        map: &core::collections::HashMap<String, f64>,
    ) -> Result<core::collections::HashMap<Symbol, Quantity>, ConversionError> {
        let mut result = core::collections::HashMap::new();
        for (key, &value) in map {
            let symbol = string_to_symbol(key)?;
            let quantity = f64_to_quantity(value)?;
            result.insert(symbol, quantity);
        }
    // Ok variant
        Ok(result)
    }
}

/// Macro for safe conversion with fallback
#[macro_export]
macro_rules! safe_convert {
    ($converter:expr, $value:expr, $fallback:expr) => {
        match $converter($value) {
            ::std::result::Result::Ok(result) => result,
            ::std::result::Result::Err(e) => {
                ::tracing::warn!("Conversion failed: {}, using fallback", e);
                $fallback
            }
        }
    }
}

/// Macro for safe price conversion
#[macro_export]
macro_rules! safe_price {
    ($value:expr) => {
        safe_convert!(f64_to_price, $value, Price::ZERO)
    };
    ($value:expr, $fallback:expr) => {
        safe_convert!(f64_to_price, $value, $fallback)
    };
}

/// Macro for safe quantity conversion
#[macro_export]
macro_rules! safe_quantity {
    ($value:expr) => {
        safe_convert!(f64_to_quantity, $value, Quantity::ZERO)
    };
    ($value:expr, $fallback:expr) => {
        safe_convert!(f64_to_quantity, $value, $fallback)
    };
}

/// Macro for safe symbol conversion
#[macro_export]
macro_rules! safe_symbol {
    ($value:expr) => {
        safe_convert!(string_to_symbol, $value, Symbol::from("UNKNOWN"))
    };
    ($value:expr, $fallback:expr) => {
        safe_convert!(string_to_symbol, $value, $fallback)
    };
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::types::test_utils::test_symbols::*;

    #[test]
    fn test_f64_to_price_valid() {
        let result = f64_to_price(123.45);
        assert!(result.is_ok(), "Valid price conversion should succeed: {:?}", result.err());
        let price = result.unwrap();
        assert_eq!(price.to_f64(), 123.45);
    }

    #[test]
    fn test_f64_to_price_invalid() {
        assert!(f64_to_price(-1.0).is_err());
        assert!(f64_to_price(f64::NAN).is_err());
        assert!(f64_to_price(f64::INFINITY).is_err());
    }

    #[test]
    fn test_string_to_symbol_valid() {
        let result = string_to_symbol(TEST_SYMBOL_1);
        assert!(result.is_ok(), "Valid symbol conversion should succeed: {:?}", result.err());
        let symbol = result.unwrap();
        assert_eq!(symbol.as_str(), TEST_SYMBOL_1);
    }

    #[test]
    fn test_string_to_symbol_invalid() {
        assert!(string_to_symbol("").is_err());
        assert!(string_to_symbol("SYMBOL_WITH_INVALID_@_CHARS").is_err());
        assert!(string_to_symbol(&"X".repeat(50)).is_err());
    }

    #[test]
    fn test_batch_conversion() {
        let values = vec![1.0, 2.0, 3.0];
        let results = TypeConverter::batch_f64_to_prices(&values);
        assert_eq!(results.len(), 3);
        assert!(results.iter().all(|r| r.is_ok()));
    }

    #[test]
    fn test_safe_conversion_macros() {
        let price = safe_price!(123.45);
        assert_eq!(price.to_f64(), 123.45);
    
        let fallback_result = Price::from_f64(100.0);
        assert!(fallback_result.is_ok(), "Fallback price should be valid: {:?}", fallback_result.err());
        let price_with_fallback = safe_price!(-1.0, fallback_result.unwrap());
        assert_eq!(price_with_fallback.to_f64(), 100.0);
    }

    #[test]
    fn test_convert_price_map() {
        let mut input = core::collections::HashMap::new();
        input.insert(TEST_SYMBOL_1.to_string(), 150.0);
        input.insert(TEST_SYMBOL_2.to_string(), 300.0);
    
        let conversion_result = TypeConverter::convert_price_map(&input);
        assert!(conversion_result.is_ok(), "Valid price map conversion should succeed: {:?}", conversion_result.err());
        let result = conversion_result.unwrap();
        assert_eq!(result.len(), 2);
        assert!(result.contains_key(&test_symbol_1()));
        assert!(result.contains_key(&test_symbol_2()));
    }
}