use chrono::{DateTime, Utc, Datelike};
use serde::{Serialize, Deserialize};
use serde_json;
use std::collections::HashMap;
use crate::Position;
use super::{BacktestConfig, CompletedTrade};
use super::trade_tracker::CompletedTrade as TrackedTrade;

// Simplified metrics that match what the web app expects
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BacktestMetrics {
    pub total_return: f64,
    pub sharpe_ratio: f64,
    pub max_drawdown: f64,
    pub win_rate: f64,
    pub total_trades: usize,
    pub profitable_trades: usize,
    // Additional fields for compatibility
    pub profit_factor: f64,
    pub total_pnl: f64,
    pub avg_win: f64,
    pub avg_loss: f64,
    pub expectancy: f64,
    pub calmar_ratio: f64,
    pub sortino_ratio: f64,
    // Missing fields required by web app
    pub final_value: f64,
    pub winning_trades: usize,
    pub losing_trades: usize,
    pub largest_win: f64,
    pub largest_loss: f64,
    pub annual_return: f64,
}

// Individual trade (fill) structure for UI
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Trade {
    pub id: String,
    pub timestamp: String,
    pub symbol: String,
    pub side: String, // "buy" or "sell"
    pub quantity: f64,
    pub price: f64,
    pub commission: f64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub pnl: Option<f64>,
    pub position_after: f64, // Position size after this trade
}

// Analytics data structure
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Analytics {
    pub drawdown_series: Vec<EquityPoint>,
    pub daily_returns: Vec<f64>,
    pub monthly_returns: HashMap<String, f64>,
    pub exposure_time: f64,
    pub risk_metrics: HashMap<String, f64>,
}

// Position structure for web app
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PositionInfo {
    pub symbol: String,
    pub quantity: f64,
    pub average_price: f64,
    pub current_price: f64,
    pub unrealized_pnl: f64,
    pub realized_pnl: f64,
}

// Equity data point for charts
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EquityDataPoint {
    pub date: String,
    pub value: f64,
}

// Simplified backtest result
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BacktestResult {
    pub backtest_id: String,
    pub status: String,
    pub completed_at: String,
    pub config: BacktestConfig,
    pub metrics: BacktestMetrics,
    pub equity: Vec<EquityDataPoint>,
    pub trades: Vec<Trade>, // Now shows all individual fills
    pub positions: Vec<PositionInfo>,
    pub analytics: Analytics,
    pub execution_time: u64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<String>,
    pub ohlc_data: HashMap<String, Vec<serde_json::Value>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct EquityPoint {
    pub timestamp: i64,
    pub value: f64,
}


impl BacktestResult {
    pub fn from_engine_data(
        config: BacktestConfig,
        equity_curve: Vec<(DateTime<Utc>, f64)>,
        fills: Vec<CompletedTrade>,
        final_positions: HashMap<String, Position>,
        start_time: DateTime<Utc>,
    ) -> Self {
        let initial_capital = config.initial_capital;
        let final_value = equity_curve.last().map(|(_, v)| *v).unwrap_or(initial_capital);
        
        // Convert fills to trades
        let trades: Vec<Trade> = fills.iter()
            .enumerate()
            .map(|(i, fill)| Trade {
                id: format!("trade-{}", i),
                timestamp: fill.timestamp.to_rfc3339(),
                symbol: fill.symbol.clone(),
                side: match fill.side {
                    crate::Side::Buy => "buy".to_string(),
                    crate::Side::Sell => "sell".to_string(),
                },
                quantity: fill.quantity,
                price: fill.price,
                commission: fill.commission,
                pnl: fill.pnl,
                position_after: fill.position_after,
            })
            .collect();
        
        // Calculate metrics
        let total_trades = trades.len();
        let total_return = ((final_value - initial_capital) / initial_capital) * 100.0;
        let total_pnl = final_value - initial_capital;
        
        // Basic win rate calculation - this is simplified
        let profitable_trades = 0; // TODO: Calculate from paired trades
        let win_rate = 0.0; // TODO: Calculate properly
        
        // Calculate daily returns
        let mut daily_returns_vec = Vec::new();
        let mut prev_date = equity_curve.first().map(|(d, _)| d.date_naive()).unwrap_or_default();
        let mut prev_value = initial_capital;
        
        for (dt, value) in &equity_curve {
            if dt.date_naive() != prev_date {
                let daily_return = (value - prev_value) / prev_value;
                daily_returns_vec.push(daily_return);
                prev_date = dt.date_naive();
                prev_value = *value;
            }
        }
        
        // Calculate volatility (annualized)
        let volatility = if daily_returns_vec.len() > 1 {
            let mean = daily_returns_vec.iter().sum::<f64>() / daily_returns_vec.len() as f64;
            let variance = daily_returns_vec.iter()
                .map(|r| (r - mean).powi(2))
                .sum::<f64>() / daily_returns_vec.len() as f64;
            variance.sqrt() * (252.0_f64).sqrt()
        } else {
            0.0
        };
        
        // Calculate Sharpe ratio
        let risk_free_rate = 0.02; // 2% annual
        let days = (config.end_time - config.start_time).num_days() as f64;
        let years = days / 365.25;
        let annualized_return = if years > 0.0 {
            (final_value / initial_capital).powf(1.0 / years) - 1.0
        } else {
            0.0
        };
        let sharpe_ratio = if volatility > 0.0 {
            (annualized_return - risk_free_rate) / volatility
        } else {
            0.0
        };
        
        // Calculate max drawdown
        let (max_drawdown, drawdown_series) = Self::calculate_drawdown(&equity_curve, initial_capital);
        
        // Calculate Calmar ratio
        let calmar_ratio = if max_drawdown > 0.0 {
            annualized_return / max_drawdown
        } else {
            0.0
        };
        
        // Convert positions
        let positions: Vec<PositionInfo> = final_positions.iter()
            .map(|(symbol, pos)| {
                let current_price = pos.average_price; // TODO: Get actual current price
                let unrealized_pnl = (current_price - pos.average_price) * pos.quantity;
                PositionInfo {
                    symbol: symbol.clone(),
                    quantity: pos.quantity,
                    average_price: pos.average_price,
                    current_price,
                    unrealized_pnl,
                    realized_pnl: pos.realized_pnl,
                }
            })
            .collect();
        
        // Convert equity curve to the format expected by web app
        let equity: Vec<EquityDataPoint> = equity_curve.iter()
            .map(|(dt, val)| EquityDataPoint {
                date: dt.to_rfc3339(),
                value: *val,
            })
            .collect();
        
        let metrics = BacktestMetrics {
            total_return,
            sharpe_ratio,
            max_drawdown,
            win_rate,
            total_trades,
            profitable_trades,
            profit_factor: 0.0,
            total_pnl,
            avg_win: 0.0,
            avg_loss: 0.0,
            expectancy: 0.0,
            calmar_ratio,
            sortino_ratio: 0.0, // TODO: Calculate
            final_value,
            winning_trades: profitable_trades,
            losing_trades: 0,
            largest_win: 0.0,
            largest_loss: 0.0,
            annual_return: annualized_return * 100.0,
        };
        
        // Create analytics
        let analytics = Analytics {
            drawdown_series,
            daily_returns: daily_returns_vec,
            monthly_returns: HashMap::new(), // TODO: Calculate
            exposure_time: 0.0, // TODO: Calculate
            risk_metrics: HashMap::new(), // TODO: Add risk metrics
        };
        
        BacktestResult {
            backtest_id: format!("rust-{}", Utc::now().timestamp_millis()),
            status: "completed".to_string(),
            completed_at: Utc::now().to_rfc3339(),
            config,
            metrics,
            equity,
            trades: Vec::new(), // No completed trades in simple version
            positions,
            analytics,
            execution_time: (Utc::now() - start_time).num_milliseconds() as u64,
            error: None,
            ohlc_data: HashMap::new(),
        }
    }
    
    fn calculate_drawdown(equity_curve: &[(DateTime<Utc>, f64)], initial_capital: f64) -> (f64, Vec<EquityPoint>) {
        let mut max_drawdown = 0.0;
        let mut peak = initial_capital;
        let mut drawdown_series = Vec::new();
        
        for (dt, value) in equity_curve {
            if *value > peak {
                peak = *value;
            }
            let drawdown_pct = if peak > 0.0 {
                ((peak - value) / peak) * 100.0
            } else {
                0.0
            };
            
            if drawdown_pct > max_drawdown {
                max_drawdown = drawdown_pct;
            }
            
            drawdown_series.push(EquityPoint {
                timestamp: dt.timestamp_millis(),
                value: -drawdown_pct, // Negative for visualization
            });
        }
        
        (max_drawdown / 100.0, drawdown_series) // Return as decimal
    }
    
    pub fn from_engine_data_with_trades(
        config: BacktestConfig,
        equity_curve: Vec<(DateTime<Utc>, f64)>,
        fills: Vec<CompletedTrade>,
        completed_trades: Vec<TrackedTrade>,
        final_positions: HashMap<String, Position>,
        start_time: DateTime<Utc>,
        last_prices: &HashMap<String, f64>,
        ohlc_history: &HashMap<String, Vec<(DateTime<Utc>, crate::Bar)>>,
    ) -> Self {
        let initial_capital = config.initial_capital;
        let final_value = equity_curve.last().map(|(_, v)| *v).unwrap_or(initial_capital);
        
        // Convert fills to web app format (all individual trades)
        let trades: Vec<Trade> = fills.iter()
            .enumerate()
            .map(|(i, fill)| Trade {
                id: format!("trade-{}", i + 1),
                timestamp: fill.timestamp.to_rfc3339(),
                symbol: fill.symbol.clone(),
                side: match fill.side {
                    crate::Side::Buy => "buy".to_string(),
                    crate::Side::Sell => "sell".to_string(),
                },
                quantity: fill.quantity,
                price: fill.price,
                commission: fill.commission,
                pnl: fill.pnl,
                position_after: fill.position_after,
            })
            .collect();
        
        // Calculate metrics from completed trades
        let total_trades = completed_trades.len();
        let total_return = ((final_value - initial_capital) / initial_capital) * 100.0;
        let total_pnl = final_value - initial_capital;
        
        // Calculate win rate and profit metrics
        let winning_trades: Vec<&TrackedTrade> = completed_trades.iter()
            .filter(|t| t.pnl > 0.0)
            .collect();
        let losing_trades: Vec<&TrackedTrade> = completed_trades.iter()
            .filter(|t| t.pnl < 0.0)
            .collect();
        
        let profitable_trades = winning_trades.len();
        let win_rate = if total_trades > 0 {
            (profitable_trades as f64 / total_trades as f64) * 100.0
        } else {
            0.0
        };
        
        let avg_win = if !winning_trades.is_empty() {
            winning_trades.iter().map(|t| t.pnl).sum::<f64>() / winning_trades.len() as f64
        } else {
            0.0
        };
        
        let avg_loss = if !losing_trades.is_empty() {
            losing_trades.iter().map(|t| t.pnl).sum::<f64>() / losing_trades.len() as f64
        } else {
            0.0
        };
        
        let gross_profit = winning_trades.iter().map(|t| t.pnl).sum::<f64>();
        let gross_loss = losing_trades.iter().map(|t| t.pnl.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
        };
        
        let expectancy = avg_win * (win_rate / 100.0) - avg_loss.abs() * ((100.0 - win_rate) / 100.0);
        
        // Calculate daily returns
        let mut daily_returns_vec = Vec::new();
        let mut prev_date = equity_curve.first().map(|(d, _)| d.date_naive()).unwrap_or_default();
        let mut prev_value = initial_capital;
        
        for (dt, value) in &equity_curve {
            if dt.date_naive() != prev_date {
                let daily_return = (value - prev_value) / prev_value;
                daily_returns_vec.push(daily_return);
                prev_date = dt.date_naive();
                prev_value = *value;
            }
        }
        
        // Calculate volatility (annualized)
        let volatility = if daily_returns_vec.len() > 1 {
            let mean = daily_returns_vec.iter().sum::<f64>() / daily_returns_vec.len() as f64;
            let variance = daily_returns_vec.iter()
                .map(|r| (r - mean).powi(2))
                .sum::<f64>() / daily_returns_vec.len() as f64;
            variance.sqrt() * (252.0_f64).sqrt()
        } else {
            0.0
        };
        
        // Calculate Sharpe ratio
        let risk_free_rate = 0.02; // 2% annual
        let days = (config.end_time - config.start_time).num_days() as f64;
        let years = days / 365.25;
        let annualized_return = if years > 0.0 {
            (final_value / initial_capital).powf(1.0 / years) - 1.0
        } else {
            0.0
        };
        let sharpe_ratio = if volatility > 0.0 {
            (annualized_return - risk_free_rate) / volatility
        } else {
            0.0
        };
        
        // Calculate Sortino ratio (using downside deviation)
        let negative_returns: Vec<f64> = daily_returns_vec.iter()
            .filter(|&&r| r < 0.0)
            .cloned()
            .collect();
        let downside_deviation = if !negative_returns.is_empty() {
            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()
        } else {
            0.0
        };
        let sortino_ratio = if downside_deviation > 0.0 {
            (annualized_return - risk_free_rate) / downside_deviation
        } else {
            0.0
        };
        
        // Calculate max drawdown
        let (max_drawdown, drawdown_series) = Self::calculate_drawdown(&equity_curve, initial_capital);
        
        // Calculate Calmar ratio
        let calmar_ratio = if max_drawdown > 0.0 {
            annualized_return / max_drawdown
        } else {
            0.0
        };
        
        // Convert positions with actual last prices
        let positions: Vec<PositionInfo> = final_positions.iter()
            .map(|(symbol, pos)| {
                let current_price = last_prices.get(symbol).copied()
                    .unwrap_or(pos.average_price);
                let unrealized_pnl = (current_price - pos.average_price) * pos.quantity;
                PositionInfo {
                    symbol: symbol.clone(),
                    quantity: pos.quantity,
                    average_price: pos.average_price,
                    current_price,
                    unrealized_pnl,
                    realized_pnl: pos.realized_pnl,
                }
            })
            .collect();
        
        // Convert equity curve to the format expected by web app
        let equity: Vec<EquityDataPoint> = equity_curve.iter()
            .map(|(dt, val)| EquityDataPoint {
                date: dt.to_rfc3339(),
                value: *val,
            })
            .collect();
        
        // Convert individual fills to UI format (for backward compatibility)
        let _fill_trades: Vec<Trade> = fills.iter()
            .enumerate()
            .map(|(i, fill)| Trade {
                id: format!("fill-{}", i),
                timestamp: fill.timestamp.to_rfc3339(),
                symbol: fill.symbol.clone(),
                side: match fill.side {
                    crate::Side::Buy => "buy".to_string(),
                    crate::Side::Sell => "sell".to_string(),
                },
                quantity: fill.quantity,
                price: fill.price,
                commission: fill.commission,
                pnl: fill.pnl,
                position_after: fill.position_after,
            })
            .collect();
        
        // Find largest win/loss
        let largest_win = winning_trades.iter()
            .map(|t| t.pnl)
            .max_by(|a, b| a.partial_cmp(b).unwrap())
            .unwrap_or(0.0);
        let largest_loss = losing_trades.iter()
            .map(|t| t.pnl)
            .min_by(|a, b| a.partial_cmp(b).unwrap())
            .unwrap_or(0.0);
        
        let metrics = BacktestMetrics {
            total_return,
            sharpe_ratio,
            max_drawdown,
            win_rate,
            total_trades,
            profitable_trades,
            profit_factor,
            total_pnl,
            avg_win,
            avg_loss,
            expectancy,
            calmar_ratio,
            sortino_ratio,
            final_value,
            winning_trades: profitable_trades,
            losing_trades: losing_trades.len(),
            largest_win,
            largest_loss,
            annual_return: annualized_return * 100.0, // Convert to percentage
        };
        
        // Calculate monthly returns
        let mut monthly_returns = HashMap::new();
        let mut monthly_values: HashMap<String, Vec<f64>> = HashMap::new();
        
        for (dt, value) in &equity_curve {
            let month_key = format!("{}-{:02}", dt.year(), dt.month());
            monthly_values.entry(month_key).or_insert_with(Vec::new).push(*value);
        }
        
        for (month, values) in monthly_values {
            if values.len() >= 2 {
                let start = values.first().unwrap();
                let end = values.last().unwrap();
                let monthly_return = ((end - start) / start) * 100.0;
                monthly_returns.insert(month, monthly_return);
            }
        }
        
        // Create analytics
        let analytics = Analytics {
            drawdown_series,
            daily_returns: daily_returns_vec,
            monthly_returns,
            exposure_time: 0.0, // TODO: Calculate based on position history
            risk_metrics: {
                let mut metrics = HashMap::new();
                metrics.insert("volatility".to_string(), volatility);
                metrics.insert("downside_deviation".to_string(), downside_deviation);
                metrics.insert("value_at_risk_95".to_string(), 0.0); // TODO: Calculate VaR
                metrics
            },
        };
        
        BacktestResult {
            backtest_id: format!("rust-{}", Utc::now().timestamp_millis()),
            status: "completed".to_string(),
            completed_at: Utc::now().to_rfc3339(),
            config,
            metrics,
            equity,
            trades, // Return the completed trades
            positions,
            analytics,
            execution_time: (Utc::now() - start_time).num_milliseconds() as u64,
            error: None,
            ohlc_data: {
                let mut ohlc_map = HashMap::new();
                for (symbol, bars) in ohlc_history {
                    let ohlc_vec: Vec<serde_json::Value> = bars.iter()
                        .map(|(timestamp, bar)| serde_json::json!({
                            "timestamp": timestamp.timestamp_millis(),
                            "open": bar.open,
                            "high": bar.high,
                            "low": bar.low,
                            "close": bar.close,
                            "volume": bar.volume,
                        }))
                        .collect();
                    ohlc_map.insert(symbol.clone(), ohlc_vec);
                }
                ohlc_map
            },
        }
    }
}