use crate::{Fill, Side};
use chrono::{DateTime, Utc};
use dashmap::DashMap;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use parking_lot::RwLock;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Position {
    pub symbol: String,
    pub quantity: f64,
    pub average_price: f64,
    pub realized_pnl: f64,
    pub unrealized_pnl: f64,
    pub total_cost: f64,
    pub last_update: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PositionUpdate {
    pub symbol: String,
    pub fill: Fill,
    pub resulting_position: Position,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TradeRecord {
    pub id: String,
    pub symbol: String,
    pub side: Side,
    pub quantity: f64,
    pub price: f64,
    pub timestamp: DateTime<Utc>,
    pub commission: f64,
    pub order_id: Option<String>,
    pub strategy_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClosedTrade {
    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_ms: i64,
    pub entry_fill_id: String,
    pub exit_fill_id: String,
}

pub struct PositionTracker {
    positions: DashMap<String, Position>,
    trade_history: Arc<RwLock<Vec<TradeRecord>>>,
    closed_trades: Arc<RwLock<Vec<ClosedTrade>>>,
    open_trades: DashMap<String, Vec<TradeRecord>>, // Track open trades by symbol
    next_trade_id: Arc<RwLock<u64>>,
}

impl PositionTracker {
    pub fn new() -> Self {
        Self {
            positions: DashMap::new(),
            trade_history: Arc::new(RwLock::new(Vec::new())),
            closed_trades: Arc::new(RwLock::new(Vec::new())),
            open_trades: DashMap::new(),
            next_trade_id: Arc::new(RwLock::new(1)),
        }
    }

    fn generate_trade_id(&self) -> String {
        let mut id = self.next_trade_id.write();
        let current_id = *id;
        *id += 1;
        format!("T{:08}", current_id)
    }

    pub fn process_fill_with_tracking(
        &self, 
        symbol: &str, 
        fill: &Fill, 
        side: Side,
        order_id: Option<String>,
        strategy_id: Option<String>
    ) -> PositionUpdate {
        // First process the fill normally
        let update = self.process_fill(symbol, fill, side);
        
        // Create trade record
        let trade_record = TradeRecord {
            id: self.generate_trade_id(),
            symbol: symbol.to_string(),
            side,
            quantity: fill.quantity,
            price: fill.price,
            timestamp: fill.timestamp,
            commission: fill.commission,
            order_id,
            strategy_id,
        };
        
        // Add to trade history
        self.trade_history.write().push(trade_record.clone());
        
        // Handle trade matching for closed trades
        match side {
            Side::Buy => {
                // For buy orders, try to match with open sell trades (closing shorts)
                if let Some(mut open_trades) = self.open_trades.get_mut(symbol) {
                    let mut remaining_quantity = fill.quantity;
                    let mut trades_to_remove = Vec::new();
                    
                    // FIFO matching against short positions
                    for (idx, open_trade) in open_trades.iter_mut().enumerate() {
                        if open_trade.side == Side::Sell && remaining_quantity > 0.0 {
                            let close_quantity = remaining_quantity.min(open_trade.quantity);
                            
                            // Create closed trade record for short position
                            let closed_trade = ClosedTrade {
                                id: format!("CT{}", self.generate_trade_id()),
                                symbol: symbol.to_string(),
                                entry_time: open_trade.timestamp,
                                exit_time: fill.timestamp,
                                entry_price: open_trade.price,
                                exit_price: fill.price,
                                quantity: close_quantity,
                                side: Side::Sell, // Opening side (short)
                                pnl: close_quantity * (open_trade.price - fill.price) - (open_trade.commission + fill.commission * close_quantity / fill.quantity),
                                pnl_percent: ((open_trade.price - fill.price) / open_trade.price) * 100.0,
                                commission: open_trade.commission + fill.commission * close_quantity / fill.quantity,
                                duration_ms: (fill.timestamp - open_trade.timestamp).num_milliseconds(),
                                entry_fill_id: open_trade.id.clone(),
                                exit_fill_id: trade_record.id.clone(),
                            };
                            
                            self.closed_trades.write().push(closed_trade);
                            
                            // Update quantities
                            remaining_quantity -= close_quantity;
                            open_trade.quantity -= close_quantity;
                            
                            if open_trade.quantity <= 0.0 {
                                trades_to_remove.push(idx);
                            }
                        }
                    }
                    
                    // Remove fully closed trades
                    for idx in trades_to_remove.into_iter().rev() {
                        open_trades.remove(idx);
                    }
                    
                    // If we still have quantity left, it's a new long position
                    if remaining_quantity > 0.0 {
                        let long_trade = TradeRecord {
                            quantity: remaining_quantity,
                            ..trade_record.clone()
                        };
                        open_trades.push(long_trade);
                    }
                } else {
                    // No open trades, start a new long position
                    self.open_trades.entry(symbol.to_string())
                        .or_insert_with(Vec::new)
                        .push(trade_record);
                }
            }
            Side::Sell => {
                // For sell orders, try to match with open buy trades
                if let Some(mut open_trades) = self.open_trades.get_mut(symbol) {
                    let mut remaining_quantity = fill.quantity;
                    let mut trades_to_remove = Vec::new();
                    
                    // FIFO matching
                    for (idx, open_trade) in open_trades.iter_mut().enumerate() {
                        if open_trade.side == Side::Buy && remaining_quantity > 0.0 {
                            let close_quantity = remaining_quantity.min(open_trade.quantity);
                            
                            // Create closed trade record
                            let closed_trade = ClosedTrade {
                                id: format!("CT{}", self.generate_trade_id()),
                                symbol: symbol.to_string(),
                                entry_time: open_trade.timestamp,
                                exit_time: fill.timestamp,
                                entry_price: open_trade.price,
                                exit_price: fill.price,
                                quantity: close_quantity,
                                side: Side::Buy, // Opening side
                                pnl: close_quantity * (fill.price - open_trade.price) - (open_trade.commission + fill.commission * close_quantity / fill.quantity),
                                pnl_percent: ((fill.price - open_trade.price) / open_trade.price) * 100.0,
                                commission: open_trade.commission + fill.commission * close_quantity / fill.quantity,
                                duration_ms: (fill.timestamp - open_trade.timestamp).num_milliseconds(),
                                entry_fill_id: open_trade.id.clone(),
                                exit_fill_id: trade_record.id.clone(),
                            };
                            
                            self.closed_trades.write().push(closed_trade);
                            
                            // Update quantities
                            remaining_quantity -= close_quantity;
                            open_trade.quantity -= close_quantity;
                            
                            if open_trade.quantity <= 0.0 {
                                trades_to_remove.push(idx);
                            }
                        }
                    }
                    
                    // Remove fully closed trades
                    for idx in trades_to_remove.into_iter().rev() {
                        open_trades.remove(idx);
                    }
                    
                    // If we still have quantity left, it's a short position
                    if remaining_quantity > 0.0 {
                        let short_trade = TradeRecord {
                            quantity: remaining_quantity,
                            ..trade_record.clone()
                        };
                        open_trades.push(short_trade);
                    }
                } else {
                    // No open trades, start a new short position
                    self.open_trades.entry(symbol.to_string())
                        .or_insert_with(Vec::new)
                        .push(trade_record);
                }
            }
        }
        
        update
    }

    pub fn process_fill(&self, symbol: &str, fill: &Fill, side: Side) -> PositionUpdate {
        let mut entry = self.positions.entry(symbol.to_string()).or_insert_with(|| {
            Position {
                symbol: symbol.to_string(),
                quantity: 0.0,
                average_price: 0.0,
                realized_pnl: 0.0,
                unrealized_pnl: 0.0,
                total_cost: 0.0,
                last_update: fill.timestamp,
            }
        });

        let position = entry.value_mut();
        let old_quantity = position.quantity;
        let old_avg_price = position.average_price;

        // Calculate new position
        match side {
            Side::Buy => {
                // Adding to position
                position.quantity += fill.quantity;
                if old_quantity >= 0.0 {
                    // Already long or flat, average up/down
                    position.total_cost += fill.price * fill.quantity;
                    position.average_price = if position.quantity > 0.0 {
                        position.total_cost / position.quantity
                    } else {
                        0.0
                    };
                } else {
                    // Was short, closing or flipping
                    let close_quantity = fill.quantity.min(-old_quantity);
                    let open_quantity = fill.quantity - close_quantity;
                    
                    // Realize P&L on closed portion
                    position.realized_pnl += close_quantity * (old_avg_price - fill.price);
                    
                    // Update position for remaining
                    if open_quantity > 0.0 {
                        position.total_cost = open_quantity * fill.price;
                        position.average_price = fill.price;
                    } else {
                        position.total_cost = (position.quantity.abs()) * old_avg_price;
                    }
                }
            }
            Side::Sell => {
                // Reducing position
                position.quantity -= fill.quantity;
                if old_quantity <= 0.0 {
                    // Already short or flat, average up/down
                    position.total_cost += fill.price * fill.quantity;
                    position.average_price = if position.quantity < 0.0 {
                        position.total_cost / position.quantity.abs()
                    } else {
                        0.0
                    };
                } else {
                    // Was long, closing or flipping
                    let close_quantity = fill.quantity.min(old_quantity);
                    let open_quantity = fill.quantity - close_quantity;
                    
                    // Realize P&L on closed portion
                    position.realized_pnl += close_quantity * (fill.price - old_avg_price);
                    
                    // Update position for remaining
                    if open_quantity > 0.0 {
                        position.total_cost = open_quantity * fill.price;
                        position.average_price = fill.price;
                    } else {
                        position.total_cost = (position.quantity.abs()) * old_avg_price;
                    }
                }
            }
        }

        // Subtract commission from realized P&L
        position.realized_pnl -= fill.commission;
        position.last_update = fill.timestamp;

        PositionUpdate {
            symbol: symbol.to_string(),
            fill: fill.clone(),
            resulting_position: position.clone(),
        }
    }

    pub fn get_position(&self, symbol: &str) -> Option<Position> {
        self.positions.get(symbol).map(|p| p.clone())
    }

    pub fn get_all_positions(&self) -> Vec<Position> {
        self.positions.iter().map(|entry| entry.value().clone()).collect()
    }

    pub fn get_open_positions(&self) -> Vec<Position> {
        self.positions
            .iter()
            .filter(|entry| entry.value().quantity.abs() > 0.0001)
            .map(|entry| entry.value().clone())
            .collect()
    }

    pub fn update_unrealized_pnl(&self, symbol: &str, current_price: f64) {
        if let Some(mut position) = self.positions.get_mut(symbol) {
            if position.quantity > 0.0 {
                position.unrealized_pnl = position.quantity * (current_price - position.average_price);
            } else if position.quantity < 0.0 {
                position.unrealized_pnl = position.quantity * (current_price - position.average_price);
            } else {
                position.unrealized_pnl = 0.0;
            }
        }
    }

    pub fn get_total_pnl(&self) -> (f64, f64) {
        let mut realized = 0.0;
        let mut unrealized = 0.0;

        for position in self.positions.iter() {
            realized += position.realized_pnl;
            unrealized += position.unrealized_pnl;
        }

        (realized, unrealized)
    }

    pub fn reset(&self) {
        self.positions.clear();
        self.trade_history.write().clear();
        self.closed_trades.write().clear();
        self.open_trades.clear();
        *self.next_trade_id.write() = 1;
    }
    
    pub fn get_trade_history(&self) -> Vec<TradeRecord> {
        self.trade_history.read().clone()
    }
    
    pub fn get_closed_trades(&self) -> Vec<ClosedTrade> {
        self.closed_trades.read().clone()
    }
    
    pub fn get_open_trades(&self) -> Vec<TradeRecord> {
        let mut all_open_trades = Vec::new();
        for entry in self.open_trades.iter() {
            all_open_trades.extend(entry.value().clone());
        }
        all_open_trades
    }
    
    pub fn get_trade_count(&self) -> usize {
        self.trade_history.read().len()
    }
    
    pub fn get_closed_trade_count(&self) -> usize {
        self.closed_trades.read().len()
    }
}