//! Type conversion helpers for eliminating E0308 type mismatch errors
//!
//! This module provides safe conversion helpers to handle common type mismatches
//! in the financial trading system. All conversions use TryFrom/TryInto patterns
//! to avoid silent overflow errors.

// CANONICAL TYPE IMPORTS - Use canonical paths to avoid conflicts
use chrono::{DateTime, Duration, Utc};
use common::{HftTimestamp, Money, Price, Quantity, Volume};
use rust_decimal::Decimal;

/// Trait for converting types to protocol buffer types  
pub trait ToProtocol<T> {
    fn to_protocol(self) -> T;
}

/// Trait for converting from protocol buffer types
pub trait FromProtocol<T> {
    fn from_protocol(value: T) -> Self;
}

/// `HftTimestamp` conversion helpers
impl HftTimestamp {
    /// Create timestamp from `u64` microseconds (direct conversion)
    #[must_use]
    pub const fn from_epoch_micros_u64(micros: u64) -> Self {
        Self::from_nanos(micros * 1000)
    }

    /// Create timestamp from `u64` nanoseconds (direct conversion)
    #[must_use]
    pub const fn from_nanos_u64(nanos: u64) -> Self {
        Self::from_nanos(nanos)
    }

    /// Convert to `u64` microseconds
    #[must_use]
    pub const fn epoch_micros_u64(self) -> u64 {
        self.nanos() / 1000
    }

    /// Convert to `u64` nanoseconds
    #[must_use]
    pub const fn as_nanos_u64(self) -> u64 {
        self.nanos()
    }
}

/// Implement From traits for common conversions
impl From<Price> for Decimal {
    fn from(price: Price) -> Self {
        price.to_decimal().unwrap_or(Self::ZERO)
    }
}

// NOTE: TryFrom<Decimal> for Price is automatically provided by Rust
// since there's already a From<Decimal> for Price implementation in basic.rs

impl From<Quantity> for Decimal {
    fn from(qty: Quantity) -> Self {
        Self::new(qty.value() as i64, 8)
    }
}

// NOTE: TryFrom<Decimal> for Quantity is now provided by common::types
// Removed local implementation to avoid conflicts
// Volume is a type alias for Quantity, so it uses the same From implementation above
// No separate From<Volume> implementation needed to avoid conflict

/// MISSING TRAIT IMPLEMENTATION: From<Money> for `Decimal`
/// Extract the amount from Money for calculations
impl From<Money> for Decimal {
    fn from(money: Money) -> Self {
        money.amount
    }
}

/// `SystemTime` to `HftTimestamp` conversions
impl From<std::time::SystemTime> for HftTimestamp {
    fn from(system_time: std::time::SystemTime) -> Self {
        let duration = system_time
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default();
        Self::from_nanos(duration.as_nanos() as u64)
    }
}

impl From<DateTime<Utc>> for HftTimestamp {
    fn from(dt: DateTime<Utc>) -> Self {
        let nanos = dt.timestamp_nanos_opt().unwrap_or_default();
        Self::from_nanos(nanos as u64)
    }
}

impl From<HftTimestamp> for std::time::SystemTime {
    fn from(timestamp: HftTimestamp) -> Self {
        std::time::UNIX_EPOCH + std::time::Duration::from_nanos(timestamp.nanos())
    }
}

impl From<HftTimestamp> for DateTime<Utc> {
    fn from(timestamp: HftTimestamp) -> Self {
        let nanos = timestamp.nanos();
        let secs = (nanos / 1_000_000_000) as i64;
        let nanos_remainder = (nanos % 1_000_000_000) as u32;

        Self::from_timestamp(secs, nanos_remainder).unwrap_or_else(chrono::Utc::now)
    }
}

/// Convert Unix milliseconds to nanoseconds
#[must_use]
/// fn
pub const fn unix_millis_to_nanos(millis: i64) -> i64 {
    millis * 1_000_000
}

/// Database conversion utilities
#[cfg(feature = "database-conversions")]
pub mod database {
    use super::*;
    use common::Currency;
    use num_bigint::BigInt;
    // CANONICAL TYPE IMPORTS - Decimal available through parent scope

    /// Database-specific type conversions for PostgreSQL/`ClickHouse`
    #[derive(Debug)]
    pub struct DatabaseConversions;

    impl DatabaseConversions {
        /// Convert Price to BigInt for database storage with high precision
        pub fn price_to_bigint(price: Price) -> Result<BigInt, crate::types::errors::ConversionError> {
            let decimal = price.to_decimal().map_err(|e| {
                crate::types::errors::ConversionError::type_conversion(format!(
                    "Failed to convert Price to Decimal: {}",
                    e
                ))
            })?;
            let mantissa = decimal.mantissa();
            Ok(BigInt::from(mantissa))
        }

        /// Convert Quantity to BigInt for precise database storage
        pub fn quantity_to_bigint(quantity: Quantity) -> BigInt {
            let decimal = Decimal::try_from(quantity.to_f64()).unwrap_or(Decimal::ZERO);
            let mantissa = decimal.mantissa();
            BigInt::from(mantissa)
        }

        /// Convert Money to separate columns (amount as BigInt, currency as String)
        pub fn money_to_db_parts(money: Money) -> (BigInt, String) {
            let mantissa = money.amount.mantissa();
            let amount_bigint = BigInt::from(mantissa);
            let currency_str = money.currency.to_string();
            (amount_bigint, currency_str)
        }

        /// Convert database parts back to Money
        pub fn db_parts_to_money(
            amount: BigInt,
            currency: String,
        ) -> Result<Money, crate::types::errors::ConversionError> {
            let mantissa = amount.try_into().map_err(|_| {
                crate::types::errors::ConversionError::invalid_number(
                    "BigInt too large for Decimal".to_string(),
                )
            })?;
            let decimal = Decimal::new(mantissa, 8);

            let currency_enum: Currency = currency.parse().map_err(|_| {
                crate::types::errors::ConversionError::invalid_number(format!(
                    "Invalid currency: {}",
                    currency
                ))
            })?;

            Ok(Money::new(decimal, currency_enum))
        }

        /// Convert HftTimestamp to database-compatible `DateTime` for `PostgreSQL`
        pub fn timestamp_to_db_datetime(timestamp: HftTimestamp) -> DateTime<Utc> {
            let nanos = timestamp.nanos() as i64;
            chrono::DateTime::from_timestamp_nanos(nanos)
        }

        /// Convert database `DateTime` back to HftTimestamp
        pub fn db_datetime_to_timestamp(dt: DateTime<Utc>) -> HftTimestamp {
            let nanos = dt.timestamp_nanos_opt().unwrap_or_default() as u64;
            HftTimestamp::from_nanos(nanos)
        }

        /// Convert Volume to BigInt for database storage
        pub fn volume_to_bigint(volume: Volume) -> BigInt {
            let decimal = Decimal::try_from(volume.to_f64()).unwrap_or(Decimal::ZERO);
            let mantissa = decimal.mantissa();
            BigInt::from(mantissa)
        }
    }
}

/// Additional conversion utilities for the trading system
pub mod trading {
    use super::{Decimal, Money, Price, Quantity, Volume};

    /// Convert Money to string representation for logging
    #[must_use]
    pub fn money_to_string(money: Money) -> String {
        format!("{} {}", money.amount, money.currency)
    }

    /// Convert Quantity to string representation  
    #[must_use]
    pub fn quantity_to_string(quantity: Quantity) -> String {
        quantity.to_f64().to_string()
    }

    /// Convert Price to string representation
    #[must_use]
    pub fn price_to_string(price: Price) -> String {
        price.to_decimal().unwrap_or(Decimal::ZERO).to_string()
    }

    /// Convert Volume to string representation
    #[must_use]
    pub fn volume_to_string(volume: Volume) -> String {
        volume.to_f64().to_string()
    }
}
