use chrono::{DateTime, Utc, Datelike};
use std::collections::HashMap;
use super::results::*;
use super::{BacktestConfig, CompletedTrade};
use super::trade_tracker::CompletedTrade as TrackedTrade;
use crate::{Position, Side};

pub struct MetricsCalculator {
    config: BacktestConfig,
    equity_curve: Vec<(DateTime<Utc>, f64)>,
    trades: Vec<TrackedTrade>,
    fills: Vec<CompletedTrade>,
    positions: HashMap<String, Position>,
}

impl MetricsCalculator {
    pub fn new(
        config: BacktestConfig,
        equity_curve: Vec<(DateTime<Utc>, f64)>,
        trades: Vec<TrackedTrade>,
        fills: Vec<CompletedTrade>,
        positions: HashMap<String, Position>,
    ) -> Self {
        Self {
            config,
            equity_curve,
            trades,
            fills,
            positions,
        }
    }
    
    pub fn calculate_all_metrics(&self) -> BacktestResult {
        let start_time = Utc::now();
        let backtest_id = format!("rust-{}", start_time.timestamp_millis());
        
        // Calculate comprehensive metrics
        let metrics = self.calculate_metrics();
        
        // Convert equity curve
        let equity_points: Vec<EquityPoint> = self.equity_curve.iter()
            .map(|(dt, value)| EquityPoint {
                timestamp: dt.timestamp_millis(),
                value: *value,
            })
            .collect();
        
        // Calculate drawdown curve
        let drawdown_curve = self.calculate_drawdown_curve();
        
        // Calculate returns
        let daily_returns = self.calculate_daily_returns();
        let cumulative_returns = self.calculate_cumulative_returns();
        
        // Convert trades to UI format
        let trades = self.convert_fills_to_trades();
        let open_trades = self.get_open_trades();
        
        // Period analysis
        let monthly_returns = self.calculate_period_returns("monthly");
        let yearly_returns = self.calculate_period_returns("yearly");
        
        // Symbol analysis
        let symbol_analysis = self.calculate_symbol_analysis();
        
        // Drawdown periods
        let drawdown_periods = self.analyze_drawdown_periods();
        
        // Exposure analysis
        let exposure_analysis = self.calculate_exposure_analysis();
        
        // Position history
        let position_history = self.calculate_position_history();
        
        // Trade signals (extracted from trades)
        let trade_signals = self.extract_trade_signals();
        
        BacktestResult {
            backtest_id,
            status: "completed".to_string(),
            started_at: self.config.start_time.to_rfc3339(),
            completed_at: Utc::now().to_rfc3339(),
            execution_time_ms: (Utc::now() - start_time).num_milliseconds() as u64,
            config: self.config.clone(),
            metrics,
            equity_curve: equity_points,
            drawdown_curve,
            daily_returns,
            cumulative_returns,
            trades,
            open_trades,
            trade_signals,
            positions: self.positions.clone(),
            position_history,
            monthly_returns,
            yearly_returns,
            symbol_analysis,
            drawdown_periods,
            exposure_analysis,
            ohlc_data: HashMap::new(), // Will be filled by orchestrator
            error: None,
            warnings: None,
        }
    }
    
    fn calculate_metrics(&self) -> BacktestMetrics {
        let initial_capital = self.config.initial_capital;
        let final_capital = self.equity_curve.last().map(|(_, v)| *v).unwrap_or(initial_capital);
        
        // Core performance
        let total_return = final_capital - initial_capital;
        let total_return_pct = (total_return / initial_capital) * 100.0;
        let days = (self.config.end_time - self.config.start_time).num_days() as f64;
        let years = days / 365.25;
        let annualized_return = if years > 0.0 {
            ((final_capital / initial_capital).powf(1.0 / years) - 1.0) * 100.0
        } else {
            0.0
        };
        
        // Trade statistics
        let total_trades = self.trades.len();
        let winning_trades = self.trades.iter().filter(|t| t.pnl > 0.0).count();
        let losing_trades = self.trades.iter().filter(|t| t.pnl < 0.0).count();
        let breakeven_trades = self.trades.iter().filter(|t| t.pnl == 0.0).count();
        
        let win_rate = if total_trades > 0 {
            (winning_trades as f64 / total_trades as f64) * 100.0
        } else {
            0.0
        };
        
        let loss_rate = if total_trades > 0 {
            (losing_trades as f64 / total_trades as f64) * 100.0
        } else {
            0.0
        };
        
        // PnL calculations
        let total_pnl = self.trades.iter().map(|t| t.pnl).sum::<f64>();
        let total_commission = self.trades.iter().map(|t| t.commission).sum::<f64>();
        let net_pnl = total_pnl - total_commission;
        
        let wins: Vec<f64> = self.trades.iter().filter(|t| t.pnl > 0.0).map(|t| t.pnl).collect();
        let losses: Vec<f64> = self.trades.iter().filter(|t| t.pnl < 0.0).map(|t| t.pnl).collect();
        
        let avg_win = if !wins.is_empty() {
            wins.iter().sum::<f64>() / wins.len() as f64
        } else {
            0.0
        };
        
        let avg_loss = if !losses.is_empty() {
            losses.iter().sum::<f64>() / losses.len() as f64
        } else {
            0.0
        };
        
        let largest_win = wins.iter().cloned().fold(0.0, f64::max);
        let largest_loss = losses.iter().cloned().fold(0.0, f64::min);
        
        let avg_trade_pnl = if total_trades > 0 {
            total_pnl / total_trades as f64
        } else {
            0.0
        };
        
        // Risk metrics
        let returns = self.calculate_returns_series();
        let volatility = self.calculate_volatility(&returns);
        let sharpe_ratio = self.calculate_sharpe_ratio(&returns, volatility);
        let sortino_ratio = self.calculate_sortino_ratio(&returns);
        let (max_drawdown, max_drawdown_pct, max_dd_duration) = self.calculate_max_drawdown();
        
        let calmar_ratio = if max_drawdown_pct != 0.0 {
            annualized_return / max_drawdown_pct.abs()
        } else {
            0.0
        };
        
        // Value at Risk
        let (var_95, cvar_95) = self.calculate_var_cvar(&returns, 0.95);
        
        // Profit factor
        let gross_profit = wins.iter().sum::<f64>();
        let gross_loss = losses.iter().map(|l| l.abs()).sum::<f64>();
        let profit_factor = if gross_loss > 0.0 {
            gross_profit / gross_loss
        } else if gross_profit > 0.0 {
            f64::INFINITY
        } else {
            0.0
        };
        
        // Expectancy
        let expectancy = avg_win * (win_rate / 100.0) - avg_loss.abs() * (loss_rate / 100.0);
        let expectancy_ratio = if avg_loss != 0.0 {
            expectancy / avg_loss.abs()
        } else {
            0.0
        };
        
        // Position statistics
        let avg_trade_duration_hours = if total_trades > 0 {
            self.trades.iter()
                .map(|t| t.duration_seconds as f64 / 3600.0)
                .sum::<f64>() / total_trades as f64
        } else {
            0.0
        };
        
        let total_long_trades = self.trades.iter().filter(|t| matches!(t.side, Side::Buy)).count();
        let total_short_trades = self.trades.iter().filter(|t| matches!(t.side, Side::Sell)).count();
        
        let long_wins = self.trades.iter()
            .filter(|t| matches!(t.side, Side::Buy) && t.pnl > 0.0)
            .count();
        let short_wins = self.trades.iter()
            .filter(|t| matches!(t.side, Side::Sell) && t.pnl > 0.0)
            .count();
        
        let long_win_rate = if total_long_trades > 0 {
            (long_wins as f64 / total_long_trades as f64) * 100.0
        } else {
            0.0
        };
        
        let short_win_rate = if total_short_trades > 0 {
            (short_wins as f64 / total_short_trades as f64) * 100.0
        } else {
            0.0
        };
        
        // Trading activity
        let total_volume_traded = self.trades.iter()
            .map(|t| t.quantity * t.entry_price + t.quantity * t.exit_price)
            .sum::<f64>();
        
        let commission_pct_of_pnl = if total_pnl != 0.0 {
            (total_commission / total_pnl.abs()) * 100.0
        } else {
            0.0
        };
        
        // Calculate additional metrics
        let downside_deviation = self.calculate_downside_deviation(&returns);
        let win_loss_ratio = if avg_loss != 0.0 {
            avg_win / avg_loss.abs()
        } else {
            0.0
        };
        
        let kelly_criterion = if win_loss_ratio > 0.0 {
            (win_rate / 100.0) - ((1.0 - win_rate / 100.0) / win_loss_ratio)
        } else {
            0.0
        };
        
        // Streaks
        let (max_wins, max_losses, current_streak) = self.calculate_streaks();
        
        BacktestMetrics {
            // Core Performance
            total_return,
            total_return_pct,
            annualized_return,
            total_pnl,
            net_pnl,
            
            // Risk Metrics
            sharpe_ratio,
            sortino_ratio,
            calmar_ratio,
            max_drawdown,
            max_drawdown_pct,
            max_drawdown_duration_days: max_dd_duration,
            volatility,
            downside_deviation,
            var_95,
            cvar_95,
            
            // Trade Statistics
            total_trades,
            winning_trades,
            losing_trades,
            breakeven_trades,
            win_rate,
            loss_rate,
            avg_win,
            avg_loss,
            largest_win,
            largest_loss,
            avg_trade_pnl,
            avg_trade_duration_hours,
            profit_factor,
            expectancy,
            expectancy_ratio,
            
            // Position Statistics
            avg_position_size: 0.0, // TODO: Calculate from position history
            max_position_size: 0.0,
            avg_positions_held: 0.0,
            max_concurrent_positions: 0,
            total_long_trades,
            total_short_trades,
            long_win_rate,
            short_win_rate,
            
            // Trading Activity
            total_volume_traded,
            total_commission_paid: total_commission,
            commission_pct_of_pnl,
            avg_daily_trades: 0.0, // TODO: Calculate
            max_daily_trades: 0,
            trading_days: 0,
            exposure_time_pct: 0.0,
            
            // Efficiency Metrics
            return_over_max_dd: if max_drawdown != 0.0 {
                total_return / max_drawdown.abs()
            } else {
                0.0
            },
            win_loss_ratio,
            kelly_criterion,
            ulcer_index: 0.0, // TODO: Calculate
            serenity_ratio: 0.0,
            lake_ratio: 0.0,
            
            // Monthly/Period Analysis
            best_month_return: 0.0, // TODO: Calculate from monthly returns
            worst_month_return: 0.0,
            positive_months: 0,
            negative_months: 0,
            monthly_win_rate: 0.0,
            avg_monthly_return: 0.0,
            
            // Streak Analysis
            max_consecutive_wins: max_wins,
            max_consecutive_losses: max_losses,
            current_streak,
            avg_winning_streak: 0.0, // TODO: Calculate
            avg_losing_streak: 0.0,
        }
    }
    
    fn calculate_returns_series(&self) -> Vec<f64> {
        let mut returns = Vec::new();
        for i in 1..self.equity_curve.len() {
            let prev_value = self.equity_curve[i - 1].1;
            let curr_value = self.equity_curve[i].1;
            if prev_value > 0.0 {
                returns.push((curr_value - prev_value) / prev_value);
            }
        }
        returns
    }
    
    fn calculate_volatility(&self, returns: &[f64]) -> f64 {
        if returns.is_empty() {
            return 0.0;
        }
        
        let mean = returns.iter().sum::<f64>() / returns.len() as f64;
        let variance = returns.iter()
            .map(|r| (r - mean).powi(2))
            .sum::<f64>() / returns.len() as f64;
        
        // Annualize the volatility
        variance.sqrt() * (252.0_f64).sqrt()
    }
    
    fn calculate_sharpe_ratio(&self, returns: &[f64], volatility: f64) -> f64 {
        if volatility == 0.0 || returns.is_empty() {
            return 0.0;
        }
        
        let mean_return = returns.iter().sum::<f64>() / returns.len() as f64;
        let annualized_return = mean_return * 252.0;
        let risk_free_rate = 0.02; // 2% annual risk-free rate
        
        (annualized_return - risk_free_rate) / volatility
    }
    
    fn calculate_sortino_ratio(&self, returns: &[f64]) -> f64 {
        if returns.is_empty() {
            return 0.0;
        }
        
        let mean_return = returns.iter().sum::<f64>() / returns.len() as f64;
        let downside_deviation = self.calculate_downside_deviation(returns);
        
        if downside_deviation == 0.0 {
            return 0.0;
        }
        
        let annualized_return = mean_return * 252.0;
        let risk_free_rate = 0.02;
        
        (annualized_return - risk_free_rate) / downside_deviation
    }
    
    fn calculate_downside_deviation(&self, returns: &[f64]) -> f64 {
        let negative_returns: Vec<f64> = returns.iter()
            .filter(|&&r| r < 0.0)
            .cloned()
            .collect();
        
        if negative_returns.is_empty() {
            return 0.0;
        }
        
        let mean = negative_returns.iter().sum::<f64>() / negative_returns.len() as f64;
        let variance = negative_returns.iter()
            .map(|r| (r - mean).powi(2))
            .sum::<f64>() / negative_returns.len() as f64;
        
        variance.sqrt() * (252.0_f64).sqrt()
    }
    
    fn calculate_max_drawdown(&self) -> (f64, f64, i64) {
        let mut max_drawdown = 0.0;
        let mut max_drawdown_pct = 0.0;
        let mut max_duration = 0i64;
        let mut peak = self.config.initial_capital;
        let mut peak_date = self.config.start_time;
        
        for (date, value) in &self.equity_curve {
            if *value > peak {
                peak = *value;
                peak_date = *date;
            }
            
            let drawdown = peak - value;
            let drawdown_pct = (drawdown / peak) * 100.0;
            
            if drawdown > max_drawdown {
                max_drawdown = drawdown;
                max_drawdown_pct = drawdown_pct;
            }
            
            if drawdown > 0.0 {
                let duration = (*date - peak_date).num_days();
                if duration > max_duration {
                    max_duration = duration;
                }
            }
        }
        
        (max_drawdown, max_drawdown_pct, max_duration)
    }
    
    fn calculate_var_cvar(&self, returns: &[f64], confidence: f64) -> (f64, f64) {
        if returns.is_empty() {
            return (0.0, 0.0);
        }
        
        let mut sorted_returns = returns.to_vec();
        sorted_returns.sort_by(|a, b| a.partial_cmp(b).unwrap());
        
        let index = ((1.0 - confidence) * sorted_returns.len() as f64) as usize;
        let var = sorted_returns[index];
        
        let cvar = sorted_returns[..=index].iter().sum::<f64>() / (index + 1) as f64;
        
        (var * 100.0, cvar * 100.0)
    }
    
    fn calculate_streaks(&self) -> (usize, usize, i32) {
        let mut max_wins = 0;
        let mut max_losses = 0;
        let mut current_streak = 0;
        let mut current_wins = 0;
        let mut current_losses = 0;
        
        for trade in &self.trades {
            if trade.pnl > 0.0 {
                current_wins += 1;
                current_losses = 0;
                if current_wins > max_wins {
                    max_wins = current_wins;
                }
                current_streak = current_wins as i32;
            } else if trade.pnl < 0.0 {
                current_losses += 1;
                current_wins = 0;
                if current_losses > max_losses {
                    max_losses = current_losses;
                }
                current_streak = -(current_losses as i32);
            }
        }
        
        (max_wins, max_losses, current_streak)
    }
    
    fn calculate_drawdown_curve(&self) -> Vec<EquityPoint> {
        let mut drawdown_curve = Vec::new();
        let mut peak = self.config.initial_capital;
        
        for (date, value) in &self.equity_curve {
            if *value > peak {
                peak = *value;
            }
            let drawdown_pct = ((peak - value) / peak) * 100.0;
            drawdown_curve.push(EquityPoint {
                timestamp: date.timestamp_millis(),
                value: -drawdown_pct, // Negative for visualization
            });
        }
        
        drawdown_curve
    }
    
    fn calculate_daily_returns(&self) -> Vec<DailyReturn> {
        let mut daily_returns = Vec::new();
        
        for i in 1..self.equity_curve.len() {
            let prev = &self.equity_curve[i - 1];
            let curr = &self.equity_curve[i];
            
            // Only include if it's a new day
            if prev.0.date() != curr.0.date() {
                let return_pct = ((curr.1 - prev.1) / prev.1) * 100.0;
                daily_returns.push(DailyReturn {
                    date: curr.0.format("%Y-%m-%d").to_string(),
                    value: return_pct,
                });
            }
        }
        
        daily_returns
    }
    
    fn calculate_cumulative_returns(&self) -> Vec<EquityPoint> {
        let initial = self.config.initial_capital;
        self.equity_curve.iter()
            .map(|(date, value)| EquityPoint {
                timestamp: date.timestamp_millis(),
                value: ((value - initial) / initial) * 100.0,
            })
            .collect()
    }
    
    fn convert_fills_to_trades(&self) -> Vec<Trade> {
        self.fills.iter()
            .enumerate()
            .map(|(i, fill)| Trade {
                id: format!("trade-{}", i),
                timestamp: fill.timestamp,
                symbol: fill.symbol.clone(),
                side: match fill.side {
                    Side::Buy => "buy".to_string(),
                    Side::Sell => "sell".to_string(),
                },
                quantity: fill.quantity,
                price: fill.price,
                commission: fill.commission,
                pnl: None, // Individual fills don't have PnL
            })
            .collect()
    }
    
    fn get_open_trades(&self) -> Vec<Trade> {
        // TODO: Implement based on position tracker
        Vec::new()
    }
    
    fn calculate_period_returns(&self, period: &str) -> Vec<PeriodReturn> {
        // TODO: Implement monthly/yearly return calculations
        Vec::new()
    }
    
    fn calculate_symbol_analysis(&self) -> HashMap<String, SymbolAnalysis> {
        let mut analysis: HashMap<String, SymbolAnalysis> = HashMap::new();
        
        // Group trades by symbol
        for trade in &self.trades {
            let entry = analysis.entry(trade.symbol.clone()).or_insert(SymbolAnalysis {
                symbol: trade.symbol.clone(),
                total_trades: 0,
                winning_trades: 0,
                losing_trades: 0,
                total_pnl: 0.0,
                win_rate: 0.0,
                avg_win: 0.0,
                avg_loss: 0.0,
                profit_factor: 0.0,
                total_volume: 0.0,
                avg_position_size: 0.0,
                exposure_time_pct: 0.0,
            });
            
            entry.total_trades += 1;
            entry.total_pnl += trade.pnl;
            entry.total_volume += trade.quantity * (trade.entry_price + trade.exit_price);
            
            if trade.pnl > 0.0 {
                entry.winning_trades += 1;
            } else if trade.pnl < 0.0 {
                entry.losing_trades += 1;
            }
        }
        
        // Calculate derived metrics
        for (_, entry) in analysis.iter_mut() {
            if entry.total_trades > 0 {
                entry.win_rate = (entry.winning_trades as f64 / entry.total_trades as f64) * 100.0;
                entry.avg_position_size = entry.total_volume / (entry.total_trades as f64 * 2.0);
            }
            
            // Calculate avg win/loss
            let wins: Vec<f64> = self.trades.iter()
                .filter(|t| t.symbol == entry.symbol && t.pnl > 0.0)
                .map(|t| t.pnl)
                .collect();
            let losses: Vec<f64> = self.trades.iter()
                .filter(|t| t.symbol == entry.symbol && t.pnl < 0.0)
                .map(|t| t.pnl)
                .collect();
            
            if !wins.is_empty() {
                entry.avg_win = wins.iter().sum::<f64>() / wins.len() as f64;
            }
            if !losses.is_empty() {
                entry.avg_loss = losses.iter().sum::<f64>() / losses.len() as f64;
            }
            
            // Profit factor
            let gross_profit = wins.iter().sum::<f64>();
            let gross_loss = losses.iter().map(|l| l.abs()).sum::<f64>();
            if gross_loss > 0.0 {
                entry.profit_factor = gross_profit / gross_loss;
            } else if gross_profit > 0.0 {
                entry.profit_factor = f64::INFINITY;
            }
        }
        
        analysis
    }
    
    fn analyze_drawdown_periods(&self) -> Vec<DrawdownPeriod> {
        // TODO: Implement comprehensive drawdown period analysis
        Vec::new()
    }
    
    fn calculate_exposure_analysis(&self) -> ExposureAnalysis {
        // TODO: Implement exposure analysis
        ExposureAnalysis {
            avg_gross_exposure: 0.0,
            avg_net_exposure: 0.0,
            max_gross_exposure: 0.0,
            max_net_exposure: 0.0,
            time_in_market_pct: 0.0,
            long_exposure_pct: 0.0,
            short_exposure_pct: 0.0,
        }
    }
    
    fn calculate_position_history(&self) -> Vec<PositionSnapshot> {
        // TODO: Implement position history tracking
        Vec::new()
    }
    
    fn extract_trade_signals(&self) -> Vec<TradeSignal> {
        // TODO: Extract signals from strategy execution
        Vec::new()
    }
}