//! # Trading Service Utilities Module
//!
//! Trading-specific utilities that extend shared library functionality.
//! Focus on domain-specific trading operations that aren't available in common libraries.
//!
//! ## Features
//!
//! - Order validation specific to trading rules
//! - Trading-specific helper functions
//! - Domain-specific calculations

// Use shared library functionality
use crate::error::{Result, TradingServiceError};
use chrono::{Datelike, Timelike};
use serde::{Deserialize, Serialize};

/// Trading-specific order validation utilities
pub mod validation {
    use super::*;

    /// `Order` validator for trading operations
    #[derive(Debug, Clone)]
    pub struct OrderValidator {
        max_order_size: f64,
        min_order_size: f64,
        max_price_deviation: f64,
        enable_symbol_validation: bool,
        allowed_symbols: Option<Vec<String>>,
    }

    impl OrderValidator {
        pub fn new(
            max_order_size: f64,
            min_order_size: f64,
            max_price_deviation: f64,
            enable_symbol_validation: bool,
            allowed_symbols: Option<Vec<String>>,
        ) -> Self {
            Self {
                max_order_size,
                min_order_size,
                max_price_deviation,
                enable_symbol_validation,
                allowed_symbols,
            }
        }

        /// Validate order size within trading limits
        pub fn validate_order_size(&self, size: f64) -> Result<()> {
            if size <= 0.0 {
                return Err(TradingServiceError::OrderValidation {
                    reason: "Order size must be positive".to_string(),
                });
            }

            if size < self.min_order_size {
                return Err(TradingServiceError::OrderValidation {
                    reason: format!(
                        "Order size {:.6} below minimum {:.6}",
                        size, self.min_order_size
                    ),
                });
            }

            if size > self.max_order_size {
                return Err(TradingServiceError::OrderValidation {
                    reason: format!(
                        "Order size {:.6} exceeds maximum {:.6}",
                        size, self.max_order_size
                    ),
                });
            }

            Ok(())
        }

        /// Validate order price against market data
        pub fn validate_price(&self, price: f64, market_price: f64) -> Result<()> {
            if price <= 0.0 {
                return Err(TradingServiceError::OrderValidation {
                    reason: "Price must be positive".to_string(),
                });
            }

            let deviation = ((price - market_price) / market_price).abs() * 100.0;
            if deviation > self.max_price_deviation {
                return Err(TradingServiceError::OrderValidation {
                    reason: format!(
                        "Price deviation {:.2}% exceeds maximum {:.2}%",
                        deviation, self.max_price_deviation
                    ),
                });
            }

            Ok(())
        }

        /// Validate trading symbol
        pub fn validate_symbol(&self, symbol: &str) -> Result<()> {
            if symbol.is_empty() {
                return Err(TradingServiceError::OrderValidation {
                    reason: "Symbol cannot be empty".to_string(),
                });
            }

            if self.enable_symbol_validation {
                if let Some(ref allowed) = self.allowed_symbols {
                    if !allowed.contains(&symbol.to_string()) {
                        return Err(TradingServiceError::OrderValidation {
                            reason: format!("Symbol '{}' not in allowed list", symbol),
                        });
                    }
                }
            }

            Ok(())
        }

        /// Validate order type constraints
        pub fn validate_order_type(&self, order_type: &str, time_in_force: &str) -> Result<()> {
            match order_type {
                "MARKET" => {
                    if time_in_force != "IOC" && time_in_force != "FOK" {
                        return Err(TradingServiceError::OrderValidation {
                            reason: "Market orders must use IOC or FOK".to_string(),
                        });
                    }
                },
                "LIMIT" | "STOP" | "STOP_LIMIT" => {
                    // Limit orders can use any TIF
                },
                _ => {
                    return Err(TradingServiceError::OrderValidation {
                        reason: format!("Invalid order type: {}", order_type),
                    });
                },
            }

            Ok(())
        }
    }

    impl Default for OrderValidator {
        fn default() -> Self {
            Self::new(
                1_000_000.0, // max_order_size
                0.001,       // min_order_size
                5.0,         // max_price_deviation (5%)
                false,       // enable_symbol_validation
                None,        // allowed_symbols
            )
        }
    }
}

/// Trading-specific risk calculation utilities
///
/// For basic risk calculations, use the shared `risk` crate
pub mod risk {
    use super::*;

    /// `Position` risk metrics specific to trading service
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct PositionRisk {
        pub position_value: f64,
        pub portfolio_value: f64,
        pub position_ratio: f64,
        pub risk_score: f64,
        pub is_over_limit: bool,
    }

    /// Trading-specific risk calculations that extend shared risk library
    pub struct TradingRiskCalculator {
        max_position_value: f64,
    }

    impl TradingRiskCalculator {
        pub fn new(max_position_value: f64) -> Self {
            Self { max_position_value }
        }

        /// Calculate position risk metrics specific to trading
        pub fn calculate_position_risk(
            &self,
            position_value: f64,
            portfolio_value: f64,
        ) -> PositionRisk {
            let position_ratio = if portfolio_value > 0.0 {
                position_value / portfolio_value
            } else {
                0.0
            };

            let risk_score = if position_value > self.max_position_value {
                1.0 // High risk
            } else {
                position_value / self.max_position_value
            };

            PositionRisk {
                position_value,
                portfolio_value,
                position_ratio,
                risk_score,
                is_over_limit: position_value > self.max_position_value,
            }
        }
    }

    impl Default for TradingRiskCalculator {
        fn default() -> Self {
            Self::new(100_000.0) // Default max position value
        }
    }
}

/// Trading-specific performance monitoring
///
/// Note: Basic metrics are available in the common library via Metrics trait
pub mod monitoring {
    use super::*;
    use std::sync::atomic::{AtomicU64, Ordering};
    use std::time::Instant;

    /// Trading-specific metrics that extend common metrics
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct TradingMetricsSnapshot {
        pub order_count: u64,
        pub fill_count: u64,
        pub cancel_count: u64,
        pub reject_count: u64,
        pub total_volume: f64,
        pub total_pnl: f64,
        pub uptime_seconds: u64,
        pub fill_rate: f64,
        pub orders_per_second: f64,
    }

    /// Simplified trading metrics collector
    ///
    /// For advanced metrics, consider using common::traits::Metrics
    #[derive(Debug)]
    pub struct TradingMetrics {
        order_count: AtomicU64,
        fill_count: AtomicU64,
        cancel_count: AtomicU64,
        reject_count: AtomicU64,
        start_time: Instant,
    }

    impl TradingMetrics {
        pub fn new() -> Self {
            Self {
                order_count: AtomicU64::new(0),
                fill_count: AtomicU64::new(0),
                cancel_count: AtomicU64::new(0),
                reject_count: AtomicU64::new(0),
                start_time: Instant::now(),
            }
        }

        /// Record order submission
        pub fn record_order(&self) {
            self.order_count.fetch_add(1, Ordering::Relaxed);
        }

        /// Record order fill
        pub fn record_fill(&self) {
            self.fill_count.fetch_add(1, Ordering::Relaxed);
        }

        /// Record order cancellation
        pub fn record_cancel(&self) {
            self.cancel_count.fetch_add(1, Ordering::Relaxed);
        }

        /// Record order rejection
        pub fn record_reject(&self) {
            self.reject_count.fetch_add(1, Ordering::Relaxed);
        }

        /// Get basic trading metrics
        pub fn get_snapshot(&self) -> TradingMetricsSnapshot {
            let uptime = self.start_time.elapsed();
            let orders = self.order_count.load(Ordering::Relaxed);
            let fills = self.fill_count.load(Ordering::Relaxed);

            TradingMetricsSnapshot {
                order_count: orders,
                fill_count: fills,
                cancel_count: self.cancel_count.load(Ordering::Relaxed),
                reject_count: self.reject_count.load(Ordering::Relaxed),
                total_volume: 0.0, // Would be calculated externally
                total_pnl: 0.0,    // Would be calculated externally
                uptime_seconds: uptime.as_secs(),
                fill_rate: if orders > 0 {
                    fills as f64 / orders as f64
                } else {
                    0.0
                },
                orders_per_second: if uptime.as_secs_f64() > 0.0 {
                    orders as f64 / uptime.as_secs_f64()
                } else {
                    0.0
                },
            }
        }
    }

    impl Default for TradingMetrics {
        fn default() -> Self {
            Self::new()
        }
    }
}

/// Trading-specific position tracking
///
/// Note: For advanced portfolio analytics, consider integrating with shared libraries
pub mod portfolio {
    use super::*;

    /// Simplified position information for a single symbol
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct Position {
        pub quantity: f64,
        pub avg_price: f64,
        pub realized_pnl: f64,
        pub last_update: chrono::DateTime<chrono::Utc>,
    }

    impl Position {
        pub fn new() -> Self {
            Self {
                quantity: 0.0,
                avg_price: 0.0,
                realized_pnl: 0.0,
                last_update: chrono::Utc::now(),
            }
        }

        /// Update position with new trade
        pub fn update(&mut self, quantity_change: f64, price: f64) {
            if quantity_change == 0.0 {
                return;
            }

            // Use checked arithmetic for position quantity updates
            // Note: For f64, we validate bounds rather than overflow since f64 has inf
            let new_quantity = self.quantity + quantity_change;
            if !new_quantity.is_finite() {
                tracing::error!(
                    "Position quantity overflow: {} + {} = {}",
                    self.quantity,
                    quantity_change,
                    new_quantity
                );
                return; // Prevent invalid position state
            }

            if self.quantity == 0.0 {
                // Opening new position
                self.quantity = new_quantity;
                self.avg_price = price;
            } else if (self.quantity > 0.0 && quantity_change > 0.0)
                || (self.quantity < 0.0 && quantity_change < 0.0)
            {
                // Adding to existing position - use checked arithmetic for cost calculations
                let current_cost = self.quantity * self.avg_price;
                let new_cost = quantity_change * price;
                let total_cost = current_cost + new_cost;

                if !total_cost.is_finite() || !current_cost.is_finite() || !new_cost.is_finite() {
                    tracing::error!(
                        "Cost calculation overflow: ({} * {}) + ({} * {}) = {}",
                        self.quantity,
                        self.avg_price,
                        quantity_change,
                        price,
                        total_cost
                    );
                    return; // Prevent invalid position state
                }

                self.avg_price = if new_quantity != 0.0 {
                    total_cost / new_quantity
                } else {
                    0.0
                };
                self.quantity = new_quantity;
            } else {
                // Reducing or closing position - calculate realized PnL
                let closed_quantity = quantity_change.abs().min(self.quantity.abs());
                let pnl_per_share = if self.quantity > 0.0 {
                    price - self.avg_price
                } else {
                    self.avg_price - price
                };

                // Use checked arithmetic for PnL calculation
                let pnl_change = closed_quantity * pnl_per_share;
                if !pnl_change.is_finite() {
                    tracing::error!(
                        "PnL calculation overflow: {} * {} = {}",
                        closed_quantity,
                        pnl_per_share,
                        pnl_change
                    );
                    return; // Prevent invalid PnL state
                }
                self.realized_pnl += pnl_change;
                self.quantity = new_quantity;

                if self.quantity.abs() < 1e-8 {
                    self.quantity = 0.0;
                    self.avg_price = 0.0;
                }
            }

            self.last_update = chrono::Utc::now();
        }

        /// Calculate unrealized PnL based on current market price
        pub fn unrealized_pnl(&self, market_price: f64) -> f64 {
            if self.quantity == 0.0 {
                0.0
            } else {
                self.quantity * (market_price - self.avg_price)
            }
        }
    }

    impl Default for Position {
        fn default() -> Self {
            Self::new()
        }
    }

    /// P&L snapshot at a point in time
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct PnlSnapshot {
        pub timestamp: chrono::DateTime<chrono::Utc>,
        pub realized_pnl: f64,
        pub unrealized_pnl: f64,
        pub total_pnl: f64,
    }
}

/// Trading-specific utility functions
pub mod helpers {
    use super::*;
    use std::sync::atomic::{AtomicU64, Ordering};

    /// Generate unique order ID
    pub fn generate_order_id() -> String {
        static ORDER_COUNTER: AtomicU64 = AtomicU64::new(0);
        let counter = ORDER_COUNTER.fetch_add(1, Ordering::Relaxed);
        let timestamp = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default()
            .as_millis();

        format!("ORD_{:016x}_{:08x}", timestamp, counter)
    }

    /// Convert price to tick-size aligned value
    pub fn align_price_to_tick(price: f64, tick_size: f64) -> f64 {
        if tick_size <= 0.0 {
            return price;
        }
        (price / tick_size).round() * tick_size
    }

    /// Calculate order value
    pub fn calculate_order_value(quantity: f64, price: f64) -> f64 {
        quantity.abs() * price
    }

    /// Format price for trading display
    pub fn format_price(price: f64, symbol: &str) -> String {
        let decimals = if symbol.len() == 6 && symbol.chars().all(|c| c.is_ascii_alphabetic()) {
            5 // Forex pair
        } else {
            2 // Stock/commodity
        };
        format!("{:.decimals$}", price, decimals = decimals)
    }

    /// Simple market hours check (extend as needed)
    pub fn is_market_open() -> bool {
        let now = chrono::Utc::now();
        let weekday = now.weekday();
        let hour = now.hour();

        matches!(
            weekday,
            chrono::Weekday::Mon
                | chrono::Weekday::Tue
                | chrono::Weekday::Wed
                | chrono::Weekday::Thu
                | chrono::Weekday::Fri
        ) && (9..16).contains(&hour)
    }
}

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

    #[test]
    fn test_order_validator() {
        let validator = validation::OrderValidator::default();

        // Test valid order size
        assert!(validator.validate_order_size(1.0).is_ok());

        // Test invalid order sizes
        assert!(validator.validate_order_size(0.0).is_err());
        assert!(validator.validate_order_size(-1.0).is_err());
        assert!(validator.validate_order_size(2_000_000.0).is_err());
    }

    #[test]
    fn test_position_tracker() {
        use ::risk::position_tracker::PositionTracker;
        use common::types::Price;

        let tracker = PositionTracker::new();
        let portfolio_id = "test-portfolio".to_string();
        let instrument_id = "AAPL".to_string();
        let strategy_id = "test-strategy".to_string();

        // Open position
        let qty1 = 100.0;
        let price1 = Price::new(150.0).unwrap();
        let result1 = tracker.update_position_sync(
            portfolio_id.clone(),
            instrument_id.clone(),
            strategy_id.clone(),
            qty1,
            price1,
        );
        assert!(result1.is_ok());

        // Add to position
        let qty2 = 50.0;
        let price2 = Price::new(160.0).unwrap();
        let result2 = tracker.update_position_sync(
            portfolio_id.clone(),
            instrument_id.clone(),
            strategy_id.clone(),
            qty2,
            price2,
        );
        assert!(result2.is_ok());

        // Verify position was updated
        let position = result2.unwrap();
        // Total quantity should be 150.0
        assert!((position.base_position.quantity.as_f64() - 150.0).abs() < 0.01);
        // Average price should be between the two prices
        let avg_price = position.base_position.avg_price.as_f64();
        assert!(avg_price > price1.as_f64() && avg_price < price2.as_f64());
    }

    #[test]
    fn test_var_calculator_basic() {
        use ::risk::var_calculator::var_engine::RealVaREngine;

        // Test that VaR engine can be created
        let _var_engine = RealVaREngine::new();
        // Successfully created - RealVaREngine::new() returns Self, not Result
        // Just verify compilation and construction works
    }

    #[test]
    fn test_helpers() {
        let order_id = helpers::generate_order_id();
        assert!(order_id.starts_with("ORD_"));

        let aligned_price = helpers::align_price_to_tick(100.567, 0.01);
        // Use epsilon comparison for float precision (tolerance: 1e-10)
        assert!(
            (aligned_price - 100.57).abs() < 1e-10,
            "aligned_price {} should be approximately 100.57",
            aligned_price
        );

        let order_value = helpers::calculate_order_value(100.0, 50.0);
        assert_eq!(order_value, 5000.0);

        let formatted = helpers::format_price(123.456789, "EURUSD");
        assert_eq!(formatted, "123.45679");
    }
}
