use crate::{Side, Order, Fill, MarketMicrostructure};
use chrono::{DateTime, Utc};

#[derive(Debug, Clone)]
pub struct CostComponents {
    pub spread_cost: f64,
    pub market_impact: f64,
    pub commission: f64,
    pub slippage: f64,
    pub opportunity_cost: f64,
    pub timing_cost: f64,
    pub total_cost: f64,
    pub cost_bps: f64,
}

#[derive(Debug, Clone)]
pub struct TransactionCostAnalysis {
    pub order_id: String,
    pub symbol: String,
    pub side: Side,
    pub intended_size: f64,
    pub filled_size: f64,
    pub avg_fill_price: f64,
    pub arrival_price: f64,
    pub benchmark_price: f64,
    pub cost_components: CostComponents,
    pub implementation_shortfall: f64,
    pub duration_ms: i64,
}

pub struct TransactionCostModel {
    commission_rate_bps: f64,
    min_commission: f64,
    exchange_fees_bps: f64,
    regulatory_fees_bps: f64,
    benchmark_type: BenchmarkType,
}

#[derive(Debug, Clone, Copy)]
pub enum BenchmarkType {
    ArrivalPrice,      // Price when order was placed
    VWAP,             // Volume-weighted average price
    TWAP,             // Time-weighted average price
    Close,            // Closing price
    MidpointAtArrival, // Mid price at order arrival
}

impl TransactionCostModel {
    pub fn new(commission_rate_bps: f64) -> Self {
        Self {
            commission_rate_bps,
            min_commission: 1.0,
            exchange_fees_bps: 0.3,  // Typical exchange fees
            regulatory_fees_bps: 0.1, // SEC fees etc
            benchmark_type: BenchmarkType::ArrivalPrice,
        }
    }
    
    pub fn with_benchmark_type(mut self, benchmark_type: BenchmarkType) -> Self {
        self.benchmark_type = benchmark_type;
        self
    }
    
    pub fn analyze_execution(
        &self,
        order: &Order,
        fills: &[Fill],
        arrival_price: f64,
        benchmark_prices: &BenchmarkPrices,
        microstructure: &MarketMicrostructure,
        order_start_time: DateTime<Utc>,
        order_end_time: DateTime<Utc>,
    ) -> TransactionCostAnalysis {
        // Calculate filled size and average price
        let filled_size = fills.iter().map(|f| f.quantity).sum::<f64>();
        let total_value = fills.iter().map(|f| f.price * f.quantity).sum::<f64>();
        let avg_fill_price = if filled_size > 0.0 {
            total_value / filled_size
        } else {
            arrival_price
        };
        
        // Get benchmark price based on type
        let benchmark_price = match self.benchmark_type {
            BenchmarkType::ArrivalPrice => arrival_price,
            BenchmarkType::VWAP => benchmark_prices.vwap,
            BenchmarkType::TWAP => benchmark_prices.twap,
            BenchmarkType::Close => benchmark_prices.close,
            BenchmarkType::MidpointAtArrival => benchmark_prices.midpoint_at_arrival,
        };
        
        // Calculate various cost components
        let cost_components = self.calculate_cost_components(
            order,
            fills,
            avg_fill_price,
            arrival_price,
            benchmark_price,
            microstructure,
        );
        
        // Calculate implementation shortfall
        let side_multiplier = match order.side {
            Side::Buy => 1.0,
            Side::Sell => -1.0,
        };
        
        let implementation_shortfall = side_multiplier * filled_size * 
            (avg_fill_price - arrival_price) + 
            side_multiplier * (order.quantity - filled_size) * 
            (benchmark_price - arrival_price);
        
        // Calculate duration
        let duration_ms = (order_end_time - order_start_time).num_milliseconds();
        
        TransactionCostAnalysis {
            order_id: order.id.clone(),
            symbol: order.symbol.clone(),
            side: order.side,
            intended_size: order.quantity,
            filled_size,
            avg_fill_price,
            arrival_price,
            benchmark_price,
            cost_components,
            implementation_shortfall,
            duration_ms,
        }
    }
    
    fn calculate_cost_components(
        &self,
        order: &Order,
        fills: &[Fill],
        avg_fill_price: f64,
        arrival_price: f64,
        benchmark_price: f64,
        microstructure: &MarketMicrostructure,
    ) -> CostComponents {
        let filled_size = fills.iter().map(|f| f.quantity).sum::<f64>();
        let total_value = filled_size * avg_fill_price;
        
        // Spread cost (crossing the spread)
        let spread_cost = filled_size * avg_fill_price * microstructure.avg_spread_bps / 10000.0;
        
        // Market impact (price movement due to our order)
        let side_multiplier = match order.side {
            Side::Buy => 1.0,
            Side::Sell => -1.0,
        };
        let market_impact = side_multiplier * filled_size * (avg_fill_price - arrival_price);
        
        // Commission and fees
        let gross_commission = total_value * self.commission_rate_bps / 10000.0;
        let commission = gross_commission.max(self.min_commission * fills.len() as f64);
        let exchange_fees = total_value * self.exchange_fees_bps / 10000.0;
        let regulatory_fees = total_value * self.regulatory_fees_bps / 10000.0;
        let total_fees = commission + exchange_fees + regulatory_fees;
        
        // Slippage (difference from benchmark)
        let slippage = side_multiplier * filled_size * (avg_fill_price - benchmark_price);
        
        // Opportunity cost (unfilled portion)
        let unfilled_size = order.quantity - filled_size;
        let opportunity_cost = if unfilled_size > 0.0 {
            // Cost of not executing at arrival price
            side_multiplier * unfilled_size * (benchmark_price - arrival_price)
        } else {
            0.0
        };
        
        // Timing cost (delay cost)
        let timing_cost = side_multiplier * filled_size * 
            (benchmark_price - arrival_price).max(0.0);
        
        // Total cost
        let total_cost = spread_cost + market_impact.abs() + total_fees + 
            slippage.abs() + opportunity_cost.abs() + timing_cost;
        
        // Cost in basis points
        let cost_bps = if total_value > 0.0 {
            (total_cost / total_value) * 10000.0
        } else {
            0.0
        };
        
        CostComponents {
            spread_cost,
            market_impact: market_impact.abs(),
            commission: total_fees,
            slippage: slippage.abs(),
            opportunity_cost: opportunity_cost.abs(),
            timing_cost,
            total_cost,
            cost_bps,
        }
    }
    
    pub fn calculate_pretrade_cost_estimate(
        &self,
        order: &Order,
        microstructure: &MarketMicrostructure,
        current_price: f64,
        expected_fill_price: f64,
        expected_fill_rate: f64,
    ) -> CostComponents {
        let expected_filled_size = order.quantity * expected_fill_rate;
        let total_value = expected_filled_size * expected_fill_price;
        
        // Estimate spread cost
        let spread_cost = expected_filled_size * expected_fill_price * 
            microstructure.avg_spread_bps / 10000.0;
        
        // Estimate market impact
        let side_multiplier = match order.side {
            Side::Buy => 1.0,
            Side::Sell => -1.0,
        };
        let market_impact = side_multiplier * expected_filled_size * 
            (expected_fill_price - current_price);
        
        // Calculate commission
        let gross_commission = total_value * self.commission_rate_bps / 10000.0;
        let commission = gross_commission.max(self.min_commission);
        let exchange_fees = total_value * self.exchange_fees_bps / 10000.0;
        let regulatory_fees = total_value * self.regulatory_fees_bps / 10000.0;
        let total_fees = commission + exchange_fees + regulatory_fees;
        
        // Estimate opportunity cost for unfilled portion
        let unfilled_size = order.quantity - expected_filled_size;
        let opportunity_cost = if unfilled_size > 0.0 {
            // Assume 10bps adverse movement for unfilled portion
            unfilled_size * current_price * 0.001
        } else {
            0.0
        };
        
        // No slippage or timing cost for pre-trade estimate
        let slippage = 0.0;
        let timing_cost = 0.0;
        
        // Total cost
        let total_cost = spread_cost + market_impact.abs() + total_fees + opportunity_cost;
        
        // Cost in basis points
        let cost_bps = if total_value > 0.0 {
            (total_cost / total_value) * 10000.0
        } else {
            0.0
        };
        
        CostComponents {
            spread_cost,
            market_impact: market_impact.abs(),
            commission: total_fees,
            slippage,
            opportunity_cost,
            timing_cost,
            total_cost,
            cost_bps,
        }
    }
}

#[derive(Debug, Clone)]
pub struct BenchmarkPrices {
    pub vwap: f64,
    pub twap: f64,
    pub close: f64,
    pub midpoint_at_arrival: f64,
}

impl Default for BenchmarkPrices {
    fn default() -> Self {
        Self {
            vwap: 0.0,
            twap: 0.0,
            close: 0.0,
            midpoint_at_arrival: 0.0,
        }
    }
}

// Helper to track and calculate various price benchmarks
pub struct BenchmarkCalculator {
    trades: Vec<(DateTime<Utc>, f64, f64)>, // (time, price, volume)
    quotes: Vec<(DateTime<Utc>, f64, f64)>, // (time, bid, ask)
}

impl BenchmarkCalculator {
    pub fn new() -> Self {
        Self {
            trades: Vec::new(),
            quotes: Vec::new(),
        }
    }
    
    pub fn add_trade(&mut self, time: DateTime<Utc>, price: f64, volume: f64) {
        self.trades.push((time, price, volume));
    }
    
    pub fn add_quote(&mut self, time: DateTime<Utc>, bid: f64, ask: f64) {
        self.quotes.push((time, bid, ask));
    }
    
    pub fn calculate_benchmarks(
        &self,
        start_time: DateTime<Utc>,
        end_time: DateTime<Utc>,
    ) -> BenchmarkPrices {
        // Filter trades within time window
        let window_trades: Vec<_> = self.trades.iter()
            .filter(|(t, _, _)| *t >= start_time && *t <= end_time)
            .cloned()
            .collect();
        
        // Calculate VWAP
        let total_volume: f64 = window_trades.iter().map(|(_, _, v)| v).sum();
        let vwap = if total_volume > 0.0 {
            window_trades.iter()
                .map(|(_, p, v)| p * v)
                .sum::<f64>() / total_volume
        } else {
            0.0
        };
        
        // Calculate TWAP
        let twap = if !window_trades.is_empty() {
            window_trades.iter()
                .map(|(_, p, _)| p)
                .sum::<f64>() / window_trades.len() as f64
        } else {
            0.0
        };
        
        // Get close price (last trade)
        let close = window_trades.last()
            .map(|(_, p, _)| *p)
            .unwrap_or(0.0);
        
        // Get midpoint at arrival
        let midpoint_at_arrival = self.quotes.iter()
            .filter(|(t, _, _)| *t <= start_time)
            .last()
            .map(|(_, b, a)| (b + a) / 2.0)
            .unwrap_or(0.0);
        
        BenchmarkPrices {
            vwap,
            twap,
            close,
            midpoint_at_arrival,
        }
    }
}