use std::collections::{HashMap, VecDeque};
use chrono::{DateTime, Utc};
use serde::{Serialize, Deserialize};
use crate::{Fill, Side};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompletedTrade {
    pub id: String,
    pub symbol: String,
    pub entry_time: DateTime<Utc>,
    pub exit_time: DateTime<Utc>,
    pub entry_price: f64,
    pub exit_price: f64,
    pub quantity: f64,
    pub side: Side, // Side of the opening trade
    pub pnl: f64,
    pub pnl_percent: f64,
    pub commission: f64,
    pub duration_seconds: i64,
}

#[derive(Debug, Clone)]
struct OpenPosition {
    symbol: String,
    side: Side,
    quantity: f64,
    entry_price: f64,
    entry_time: DateTime<Utc>,
    commission: f64,
}

/// Tracks fills and matches them into completed trades
pub struct TradeTracker {
    open_positions: HashMap<String, VecDeque<OpenPosition>>,
    completed_trades: Vec<CompletedTrade>,
    trade_counter: u64,
}

impl TradeTracker {
    pub fn new() -> Self {
        Self {
            open_positions: HashMap::new(),
            completed_trades: Vec::new(),
            trade_counter: 0,
        }
    }
    
    pub fn get_completed_trades(&self) -> Vec<CompletedTrade> {
        self.completed_trades.clone()
    }

    pub fn process_fill(&mut self, symbol: &str, side: Side, fill: &Fill) {
        let positions = self.open_positions.entry(symbol.to_string()).or_insert_with(VecDeque::new);
        
        // Check if this fill closes existing positions
        let mut remaining_quantity = fill.quantity;
        let mut fills_to_remove = Vec::new();
        
        for (idx, open_pos) in positions.iter_mut().enumerate() {
            // Only match against opposite side positions
            if open_pos.side == side {
                continue;
            }
            
            if remaining_quantity <= 0.0 {
                break;
            }
            
            let matched_quantity = remaining_quantity.min(open_pos.quantity);
            
            // Calculate PnL
            let (pnl, pnl_percent) = Self::calculate_pnl(
                &open_pos,
                fill.price,
                matched_quantity,
                fill.commission,
            );
            
            // Create completed trade
            self.trade_counter += 1;
            let completed_trade = CompletedTrade {
                id: format!("trade-{}", self.trade_counter),
                symbol: symbol.to_string(),
                entry_time: open_pos.entry_time,
                exit_time: fill.timestamp,
                entry_price: open_pos.entry_price,
                exit_price: fill.price,
                quantity: matched_quantity,
                side: open_pos.side.clone(),
                pnl,
                pnl_percent,
                commission: open_pos.commission + (fill.commission * matched_quantity / fill.quantity),
                duration_seconds: (fill.timestamp - open_pos.entry_time).num_seconds(),
            };
            
            eprintln!("📈 TRADE CLOSED: {} {} @ entry: ${:.2} ({}), exit: ${:.2} ({}) = P&L: ${:.2}",
                symbol,
                match open_pos.side { Side::Buy => "LONG", Side::Sell => "SHORT" },
                open_pos.entry_price,
                open_pos.entry_time.format("%Y-%m-%d"),
                fill.price,
                fill.timestamp.format("%Y-%m-%d"),
                pnl
            );
            
            self.completed_trades.push(completed_trade);
            
            // Update open position
            open_pos.quantity -= matched_quantity;
            remaining_quantity -= matched_quantity;
            
            if open_pos.quantity <= 0.0 {
                fills_to_remove.push(idx);
            }
        }
        
        // Remove fully closed positions
        for idx in fills_to_remove.iter().rev() {
            positions.remove(*idx);
        }
        
        // If there's remaining quantity, it opens a new position
        if remaining_quantity > 0.0 {
            let new_position = OpenPosition {
                symbol: symbol.to_string(),
                side,
                quantity: remaining_quantity,
                entry_price: fill.price,
                entry_time: fill.timestamp,
                commission: fill.commission * remaining_quantity / fill.quantity,
            };
            positions.push_back(new_position);
        }
    }
    
    fn calculate_pnl(open_pos: &OpenPosition, exit_price: f64, quantity: f64, exit_commission: f64) -> (f64, f64) {
        let entry_value = open_pos.entry_price * quantity;
        let exit_value = exit_price * quantity;
        
        let gross_pnl = match open_pos.side {
            Side::Buy => exit_value - entry_value,
            Side::Sell => entry_value - exit_value,
        };
        
        let commission = open_pos.commission * (quantity / open_pos.quantity) + exit_commission * (quantity / open_pos.quantity);
        let net_pnl = gross_pnl - commission;
        let pnl_percent = (net_pnl / entry_value) * 100.0;
        
        (net_pnl, pnl_percent)
    }
    
    
    pub fn get_open_positions(&self) -> HashMap<String, Vec<(Side, f64, f64)>> {
        let mut result = HashMap::new();
        
        for (symbol, positions) in &self.open_positions {
            let pos_info: Vec<(Side, f64, f64)> = positions
                .iter()
                .map(|p| (p.side.clone(), p.quantity, p.entry_price))
                .collect();
            
            if !pos_info.is_empty() {
                result.insert(symbol.clone(), pos_info);
            }
        }
        
        result
    }
    
    pub fn get_net_position(&self, symbol: &str) -> f64 {
        let positions = match self.open_positions.get(symbol) {
            Some(pos) => pos,
            None => return 0.0,
        };
        
        let mut net = 0.0;
        for pos in positions {
            match pos.side {
                Side::Buy => net += pos.quantity,
                Side::Sell => net -= pos.quantity,
            }
        }
        
        net
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_simple_round_trip() {
        let mut tracker = TradeTracker::new();
        
        // Buy 100 shares at $50
        let buy_fill = Fill {
            timestamp: Utc::now(),
            price: 50.0,
            quantity: 100.0,
            commission: 1.0,
        };
        tracker.process_fill("AAPL", Side::Buy, &buy_fill);
        
        // Sell 100 shares at $55
        let sell_fill = Fill {
            timestamp: Utc::now(),
            price: 55.0,
            quantity: 100.0,
            commission: 1.0,
        };
        tracker.process_fill("AAPL", Side::Sell, &sell_fill);
        
        let trades = tracker.get_completed_trades();
        assert_eq!(trades.len(), 1);
        
        let trade = &trades[0];
        assert_eq!(trade.symbol, "AAPL");
        assert_eq!(trade.quantity, 100.0);
        assert_eq!(trade.entry_price, 50.0);
        assert_eq!(trade.exit_price, 55.0);
        assert_eq!(trade.pnl, 498.0); // (55-50)*100 - 2 commission
        assert_eq!(trade.side, Side::Buy);
    }
    
    #[test]
    fn test_partial_fills() {
        let mut tracker = TradeTracker::new();
        
        // Buy 100 shares at $50
        let buy_fill = Fill {
            timestamp: Utc::now(),
            price: 50.0,
            quantity: 100.0,
            commission: 1.0,
        };
        tracker.process_fill("AAPL", Side::Buy, &buy_fill);
        
        // Sell 60 shares at $55
        let sell_fill1 = Fill {
            timestamp: Utc::now(),
            price: 55.0,
            quantity: 60.0,
            commission: 0.6,
        };
        tracker.process_fill("AAPL", Side::Sell, &sell_fill1);
        
        // Check we have one completed trade and remaining position
        let trades = tracker.get_completed_trades();
        assert_eq!(trades.len(), 1);
        assert_eq!(trades[0].quantity, 60.0);
        
        assert_eq!(tracker.get_net_position("AAPL"), 40.0);
        
        // Sell remaining 40 shares at $52
        let sell_fill2 = Fill {
            timestamp: Utc::now(),
            price: 52.0,
            quantity: 40.0,
            commission: 0.4,
        };
        tracker.process_fill("AAPL", Side::Sell, &sell_fill2);
        
        // Now we should have 2 completed trades and no position
        let trades = tracker.get_completed_trades();
        assert_eq!(trades.len(), 2);
        assert_eq!(tracker.get_net_position("AAPL"), 0.0);
    }
}