//! Position Manager
//!
//! Manages trading positions, P&L tracking, and position-related calculations

use std::collections::HashMap;
// RwLock from std::sync is used via PositionMap type alias
use chrono::Utc;
use tracing::{debug, info, warn};

use crate::trading_operations::ExecutionResult;
use common::{Position, PositionMap}; // Use the new type alias
use rust_decimal::prelude::ToPrimitive;
use rust_decimal::Decimal;

/// Position Manager for tracking and managing positions
#[derive(Debug)]
/// PositionManager
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct PositionManager {
    /// Current positions by symbol
    positions: PositionMap<Position>,
}

impl PositionManager {
    /// Create a new position manager
    pub fn new() -> Self {
        Self {
            positions: PositionMap::default(),
        }
    }

    /// Update position based on execution
    pub fn update_position(&self, execution: &ExecutionResult) -> Result<(), String> {
        let mut positions = self
            .positions
            .write()
            .map_err(|e| format!("Lock error: {}", e))?;

        let position = positions
            .entry(execution.symbol.clone())
            .or_insert_with(|| {
                let now = Utc::now();
                Position {
                    id: uuid::Uuid::new_v4(),
                    symbol: execution.symbol.clone(),
                    quantity: Decimal::ZERO,
                    avg_price: Decimal::ZERO,
                    avg_cost: Decimal::ZERO,
                    basis: Decimal::ZERO,
                    average_price: Decimal::ZERO,
                    market_value: Decimal::ZERO,
                    unrealized_pnl: Decimal::ZERO,
                    realized_pnl: Decimal::ZERO,
                    created_at: now,
                    updated_at: now,
                    last_updated: now,
                    current_price: None,
                    notional_value: Decimal::ZERO,
                    margin_requirement: Decimal::ZERO,
                }
            });

        // Determine if this is a buy or sell from the explicit side field
        let is_buy = execution.side == common::OrderSide::Buy;

        let old_quantity = position.quantity;
        let old_cost = position.avg_cost;

        if is_buy {
            // Increasing position (buy)
            if position.quantity >= Decimal::ZERO {
                // Same direction - calculate new average cost
                let old_qty_decimal = old_quantity;
                let old_cost_decimal = old_cost; // old_cost is already Decimal
                let exec_qty_decimal = execution.executed_quantity;
                let exec_price_decimal = execution.execution_price;

                let total_cost =
                    old_qty_decimal * old_cost_decimal + exec_qty_decimal * exec_price_decimal;
                let new_quantity_decimal = old_qty_decimal + exec_qty_decimal;

                position.quantity = new_quantity_decimal;
                let new_avg_cost = if new_quantity_decimal > Decimal::ZERO {
                    total_cost / new_quantity_decimal
                } else {
                    Decimal::ZERO
                };
                position.avg_cost = new_avg_cost;
                position.avg_price = new_avg_cost;
                position.average_price = new_avg_cost;
            } else {
                // Reducing short position
                let exec_qty_decimal = execution.executed_quantity;
                let exec_price_decimal = execution.execution_price;
                let old_qty_decimal = old_quantity;
                let old_cost_decimal = old_cost;

                let reduction = exec_qty_decimal.min(old_qty_decimal.abs());
                let realized_pnl = reduction * (old_cost_decimal - exec_price_decimal);
                position.realized_pnl = position.realized_pnl + realized_pnl;

                let new_quantity = old_qty_decimal + exec_qty_decimal;
                position.quantity = new_quantity;

                if new_quantity > Decimal::ZERO {
                    // Flipped to long - remaining quantity at execution price
                    position.avg_cost = exec_price_decimal;
                    position.avg_price = exec_price_decimal;
                    position.average_price = exec_price_decimal;
                }
            }
        } else {
            // Decreasing position (sell) - executed_quantity is always positive magnitude
            let exec_qty_decimal = execution.executed_quantity;
            let exec_price_decimal = execution.execution_price;
            let old_qty_decimal = old_quantity;
            let old_cost_decimal = old_cost;

            if old_qty_decimal > Decimal::ZERO {
                // Reducing long position
                let reduction = exec_qty_decimal.min(old_qty_decimal);
                let realized_pnl = reduction * (exec_price_decimal - old_cost_decimal);
                position.realized_pnl = position.realized_pnl + realized_pnl;

                let new_quantity_decimal = old_qty_decimal - exec_qty_decimal;
                position.quantity = new_quantity_decimal;

                if new_quantity_decimal < Decimal::ZERO {
                    // Flipped to short - remaining quantity at execution price
                    position.avg_cost = exec_price_decimal;
                    position.avg_price = exec_price_decimal;
                    position.average_price = exec_price_decimal;
                }
            } else {
                // Increasing short position
                let total_cost = old_qty_decimal.abs() * old_cost_decimal
                    + exec_qty_decimal * exec_price_decimal;
                let new_quantity_decimal = old_qty_decimal - exec_qty_decimal;

                position.quantity = new_quantity_decimal;
                let new_avg_cost = if new_quantity_decimal < Decimal::ZERO {
                    total_cost / new_quantity_decimal.abs()
                } else {
                    Decimal::ZERO
                };
                position.avg_cost = new_avg_cost;
                position.avg_price = new_avg_cost;
                position.average_price = new_avg_cost;
            }
        }
        position.last_updated = Utc::now();

        info!(
            "Position updated for {}: {} @ {} (realized P&L: {})",
            execution.symbol, position.quantity, position.avg_cost, position.realized_pnl
        );

        Ok(())
    }

    /// Get position for a specific symbol
    pub fn get_position(&self, symbol: &str) -> Option<Position> {
        let positions = self.positions.read().ok()?;
        positions.get(symbol).cloned()
    }

    /// Get all positions, optionally filtered by symbol
    pub fn get_positions(&self, symbol_filter: Option<String>) -> Result<Vec<Position>, String> {
        let positions = self
            .positions
            .read()
            .map_err(|e| format!("Lock error: {}", e))?;

        let filtered_positions: Vec<Position> = positions
            .values()
            .filter(|position| {
                if let Some(ref filter) = symbol_filter {
                    position.symbol.as_str() == filter
                } else {
                    true
                }
            })
            .cloned()
            .collect();

        // Ok variant
        Ok(filtered_positions)
    }

    /// Update market value for a single symbol
    pub fn update_market_values(&self, symbol: &str, market_price: Decimal) -> Result<(), String> {
        let mut prices = HashMap::new();
        prices.insert(symbol.to_string(), market_price);
        self.update_market_values_batch(prices)
    }

    /// Update market values based on current market prices (batch update)
    pub fn update_market_values_batch(
        &self,
        market_prices: HashMap<String, Decimal>,
    ) -> Result<(), String> {
        let mut positions = self
            .positions
            .write()
            .map_err(|e| format!("Lock error: {}", e))?;

        for (symbol, market_price) in market_prices {
            if let Some(position) = positions.get_mut(&symbol) {
                let qty_decimal = position.quantity;
                let avg_cost_decimal = position.avg_cost;

                // Calculate market value
                let market_value_decimal = qty_decimal * market_price;
                position.market_value = market_value_decimal;
                // Calculate unrealized P&L
                if qty_decimal != Decimal::ZERO {
                    let unrealized_pnl = if qty_decimal > Decimal::ZERO {
                        // Long position
                        qty_decimal * (market_price - avg_cost_decimal)
                    } else {
                        // Short position
                        qty_decimal.abs() * (avg_cost_decimal - market_price)
                    };
                    position.unrealized_pnl = unrealized_pnl;
                } else {
                    position.unrealized_pnl = Decimal::ZERO;
                }

                position.last_updated = Utc::now();

                debug!(
                    "Updated market value for {}: {} (unrealized P&L: {})",
                    symbol, position.market_value, position.unrealized_pnl
                );
            }
        }

        Ok(())
    }

    /// Get total portfolio value
    pub fn get_total_portfolio_value(&self) -> Decimal {
        let positions = match self.positions.read() {
            Ok(pos) => pos,
            Err(_) => return Decimal::ZERO,
        };

        positions
            .values()
            .map(|pos| pos.market_value)
            .sum::<Decimal>()
    }

    /// Get total unrealized P&L
    pub fn get_total_unrealized_pnl(&self) -> Decimal {
        let positions = match self.positions.read() {
            Ok(pos) => pos,
            Err(_) => return Decimal::ZERO,
        };

        positions.values().map(|pos| pos.unrealized_pnl).sum()
    }

    /// Get total realized P&L
    pub fn get_total_realized_pnl(&self) -> Decimal {
        let positions = match self.positions.read() {
            Ok(pos) => pos,
            Err(_) => return Decimal::ZERO,
        };

        positions.values().map(|pos| pos.realized_pnl).sum()
    }

    /// Close position for a symbol
    pub fn close_position(&self, symbol: &str) -> Result<Option<Position>, String> {
        let mut positions = self
            .positions
            .write()
            .map_err(|e| format!("Lock error: {}", e))?;

        if let Some(position) = positions.remove(symbol) {
            info!(
                "Position closed for {}: final P&L: {}",
                symbol, position.realized_pnl
            );
            Ok(Some(position))
        } else {
            warn!("Attempted to close non-existent position for {}", symbol);
            // Ok variant
            Ok(None)
        }
    }

    /// Get positions that exceed risk limits
    pub fn get_positions_exceeding_limits(&self, max_position_value: Decimal) -> Vec<Position> {
        let positions = match self.positions.read() {
            Ok(pos) => pos,
            Err(_) => return Vec::new(),
        };

        positions
            .values()
            .filter(|pos| pos.market_value.abs() > max_position_value)
            .cloned()
            .collect()
    }

    /// Calculate position concentration risk
    pub fn calculate_concentration_risk(&self) -> HashMap<String, f64> {
        let positions = match self.positions.read() {
            Ok(pos) => pos,
            Err(_) => return HashMap::new(),
        };
        let total_value = positions
            .values()
            .map(|pos| pos.market_value.abs())
            .sum::<Decimal>();

        if total_value == Decimal::ZERO {
            return HashMap::new();
        }

        positions
            .iter()
            .map(|(symbol, position)| {
                let concentration = (position.market_value.abs() / total_value)
                    .to_f64()
                    .unwrap_or(0.0)
                    * 100.0;
                (symbol.clone(), concentration)
            })
            .collect()
    }

    /// Get position statistics
    pub fn get_position_stats(&self) -> PositionStats {
        let positions = match self.positions.read() {
            Ok(pos) => pos,
            Err(_) => return PositionStats::default(),
        };

        let total_positions = positions.len();
        let long_positions = positions
            .values()
            .filter(|p| p.quantity > Decimal::ZERO)
            .count();
        let short_positions = positions
            .values()
            .filter(|p| p.quantity < Decimal::ZERO)
            .count();

        let total_market_value = positions.values().map(|p| p.market_value).sum::<Decimal>();
        let total_unrealized_pnl = positions.values().map(|p| p.unrealized_pnl).sum();
        let total_realized_pnl = positions.values().map(|p| p.realized_pnl).sum();

        PositionStats {
            total_positions,
            long_positions,
            short_positions,
            total_market_value,
            total_unrealized_pnl,
            total_realized_pnl,
        }
    }
}

impl Default for PositionManager {
    fn default() -> Self {
        Self::new()
    }
}

/// Position statistics
#[derive(Debug, Default)]
/// PositionStats
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct PositionStats {
    /// Total Positions
    pub total_positions: usize,
    /// Long Positions
    pub long_positions: usize,
    /// Short Positions
    pub short_positions: usize,
    /// Total Market Value
    pub total_market_value: Decimal,
    /// Total Unrealized Pnl
    pub total_unrealized_pnl: Decimal,
    /// Total Realized Pnl
    pub total_realized_pnl: Decimal,
}

#[cfg(test)]
#[allow(clippy::assertions_on_result_states, clippy::redundant_clone, clippy::string_to_string)]
mod tests {
    use super::*;
    use crate::trading_operations::LiquidityFlag;
    use common::OrderSide;

    /// Create a buy execution (positive quantity, side = Buy)
    fn create_buy_execution(
        order_id: &str,
        symbol: &str,
        quantity: i64,
        price: i64,
    ) -> ExecutionResult {
        ExecutionResult {
            order_id: order_id.to_string().into(),
            symbol: symbol.to_string(),
            side: OrderSide::Buy,
            executed_quantity: Decimal::from(quantity.abs()),
            execution_price: Decimal::from(price),
            execution_time: Utc::now(),
            commission: Decimal::ZERO,
            liquidity_flag: LiquidityFlag::Maker,
        }
    }

    /// Create a sell execution (positive quantity, side = Sell)
    fn create_sell_execution(
        order_id: &str,
        symbol: &str,
        quantity: i64,
        price: i64,
    ) -> ExecutionResult {
        ExecutionResult {
            order_id: order_id.to_string().into(),
            symbol: symbol.to_string(),
            side: OrderSide::Sell,
            executed_quantity: Decimal::from(quantity.abs()),
            execution_price: Decimal::from(price),
            execution_time: Utc::now(),
            commission: Decimal::ZERO,
            liquidity_flag: LiquidityFlag::Maker,
        }
    }

    /// Legacy helper - creates buy execution
    fn create_execution(
        order_id: &str,
        symbol: &str,
        quantity: i64,
        price: i64,
    ) -> ExecutionResult {
        create_buy_execution(order_id, symbol, quantity, price)
    }

    #[test]
    fn test_position_creation() {
        let manager = PositionManager::new();

        let execution = create_execution("test-001", "BTCUSD", 100, 50000);

        let result = manager.update_position(&execution);
        assert!(result.is_ok());

        let position = manager.get_position("BTCUSD");
        assert!(position.is_some());

        let pos = position.expect("Position should exist after update");
        assert_eq!(pos.symbol.to_string(), "BTCUSD");
        assert_eq!(pos.quantity, Decimal::from(100));
        assert_eq!(pos.avg_cost, Decimal::from(50000));
    }

    #[test]
    fn test_pnl_calculation() {
        let manager = PositionManager::new();

        // First execution - buy
        let buy_execution = create_execution("buy-001", "ETHUSD", 10, 3000);

        manager
            .update_position(&buy_execution)
            .expect("Position update should succeed");

        // Update market values
        let mut market_prices = HashMap::new();
        market_prices.insert("ETHUSD".to_string(), Decimal::from(3100));

        manager
            .update_market_values_batch(market_prices)
            .expect("Market value update should succeed");

        let position = manager
            .get_position("ETHUSD")
            .expect("Position should exist after update");

        // Should have unrealized profit of 10 * (3100 - 3000) = 1000
        assert_eq!(position.unrealized_pnl, Decimal::from(1000));
    }

    #[test]
    fn test_multiple_long_entries() {
        let manager = PositionManager::new();

        // First buy: 100 @ 50000
        let exec1 = create_execution("order-1", "BTCUSD", 100, 50000);
        manager
            .update_position(&exec1)
            .expect("First execution should succeed");

        // Second buy: 50 @ 51000
        let exec2 = create_execution("order-2", "BTCUSD", 50, 51000);
        manager
            .update_position(&exec2)
            .expect("Second execution should succeed");

        let position = manager
            .get_position("BTCUSD")
            .expect("Position should exist");

        // Total quantity: 150
        assert_eq!(position.quantity, Decimal::from(150));

        // Average cost: (100 * 50000 + 50 * 51000) / 150 = 50333.33...
        let expected_avg = (Decimal::from(100) * Decimal::from(50000)
            + Decimal::from(50) * Decimal::from(51000))
            / Decimal::from(150);
        assert_eq!(position.avg_cost, expected_avg);
    }

    #[test]
    fn test_reduce_long_position() {
        let manager = PositionManager::new();

        // Buy 100 @ 50000
        let buy_exec = create_execution("buy-1", "BTCUSD", 100, 50000);
        manager
            .update_position(&buy_exec)
            .expect("Buy should succeed");

        // Sell 40 @ 52000 (reducing position)
        let sell_exec = create_sell_execution("sell-1", "BTCUSD", 40, 52000);
        manager
            .update_position(&sell_exec)
            .expect("Sell should succeed");

        let position = manager
            .get_position("BTCUSD")
            .expect("Position should exist");

        // Remaining quantity: 60
        assert_eq!(position.quantity, Decimal::from(60));

        // Realized P&L: 40 * (52000 - 50000) = 80000
        let expected_pnl = Decimal::from(40) * (Decimal::from(52000) - Decimal::from(50000));
        assert_eq!(position.realized_pnl, expected_pnl);

        // Average cost should remain 50000
        assert_eq!(position.avg_cost, Decimal::from(50000));
    }

    #[test]
    fn test_close_long_position() {
        let manager = PositionManager::new();

        // Buy 100 @ 50000
        let buy_exec = create_execution("buy-1", "BTCUSD", 100, 50000);
        manager
            .update_position(&buy_exec)
            .expect("Buy should succeed");

        // Sell all 100 @ 51000
        let sell_exec = create_sell_execution("sell-1", "BTCUSD", 100, 51000);
        manager
            .update_position(&sell_exec)
            .expect("Sell should succeed");

        let position = manager
            .get_position("BTCUSD")
            .expect("Position should exist");

        // Position should be flat
        assert_eq!(position.quantity, Decimal::ZERO);

        // Realized P&L: 100 * (51000 - 50000) = 100000
        let expected_pnl = Decimal::from(100) * (Decimal::from(51000) - Decimal::from(50000));
        assert_eq!(position.realized_pnl, expected_pnl);
    }

    #[test]
    fn test_flip_long_to_short() {
        let manager = PositionManager::new();

        // Buy 100 @ 50000
        let buy_exec = create_execution("buy-1", "BTCUSD", 100, 50000);
        manager
            .update_position(&buy_exec)
            .expect("Buy should succeed");

        // Sell 150 @ 51000 (flipping to short -50)
        let sell_exec = create_sell_execution("sell-1", "BTCUSD", 150, 51000);
        manager
            .update_position(&sell_exec)
            .expect("Sell should succeed");

        let position = manager
            .get_position("BTCUSD")
            .expect("Position should exist");

        // Position should be short -50
        assert_eq!(position.quantity, Decimal::from(-50));

        // Realized P&L from closing long: 100 * (51000 - 50000) = 100000
        let expected_pnl = Decimal::from(100) * (Decimal::from(51000) - Decimal::from(50000));
        assert_eq!(position.realized_pnl, expected_pnl);

        // New average cost for short position should be 51000
        assert_eq!(position.avg_cost, Decimal::from(51000));
    }

    #[test]
    fn test_short_position() {
        let manager = PositionManager::new();

        // Short sell 100 @ 50000
        let sell_exec = create_sell_execution("short-1", "BTCUSD", 100, 50000);
        manager
            .update_position(&sell_exec)
            .expect("Short should succeed");

        let position = manager
            .get_position("BTCUSD")
            .expect("Position should exist");

        // Position should be short -100
        assert_eq!(position.quantity, Decimal::from(-100));
        assert_eq!(position.avg_cost, Decimal::from(50000));
    }

    #[test]
    fn test_increase_short_position() {
        let manager = PositionManager::new();

        // Short 100 @ 50000
        let short1 = create_sell_execution("short-1", "BTCUSD", 100, 50000);
        manager
            .update_position(&short1)
            .expect("First short should succeed");

        // Short another 50 @ 49000
        let short2 = create_sell_execution("short-2", "BTCUSD", 50, 49000);
        manager
            .update_position(&short2)
            .expect("Second short should succeed");

        let position = manager
            .get_position("BTCUSD")
            .expect("Position should exist");

        // Total short quantity: -150
        assert_eq!(position.quantity, Decimal::from(-150));

        // Average cost: (100 * 50000 + 50 * 49000) / 150 = 49666.67...
        let expected_avg = (Decimal::from(100) * Decimal::from(50000)
            + Decimal::from(50) * Decimal::from(49000))
            / Decimal::from(150);
        assert_eq!(position.avg_cost, expected_avg);
    }

    #[test]
    fn test_cover_short_position() {
        let manager = PositionManager::new();

        // Short 100 @ 50000
        let short_exec = create_sell_execution("short-1", "BTCUSD", 100, 50000);
        manager
            .update_position(&short_exec)
            .expect("Short should succeed");

        // Cover 100 @ 49000 (profit on short)
        let cover_exec = create_execution("cover-1", "BTCUSD", 100, 49000);
        manager
            .update_position(&cover_exec)
            .expect("Cover should succeed");

        let position = manager
            .get_position("BTCUSD")
            .expect("Position should exist");

        // Position should be flat
        assert_eq!(position.quantity, Decimal::ZERO);

        // Realized P&L from short: 100 * (50000 - 49000) = 100000
        let expected_pnl = Decimal::from(100) * (Decimal::from(50000) - Decimal::from(49000));
        assert_eq!(position.realized_pnl, expected_pnl);
    }

    #[test]
    fn test_flip_short_to_long() {
        let manager = PositionManager::new();

        // Short 100 @ 50000
        let short_exec = create_sell_execution("short-1", "BTCUSD", 100, 50000);
        manager
            .update_position(&short_exec)
            .expect("Short should succeed");

        // Buy 150 @ 49000 (flipping to long +50)
        let buy_exec = create_execution("buy-1", "BTCUSD", 150, 49000);
        manager
            .update_position(&buy_exec)
            .expect("Buy should succeed");

        let position = manager
            .get_position("BTCUSD")
            .expect("Position should exist");

        // Position should be long +50
        assert_eq!(position.quantity, Decimal::from(50));

        // Realized P&L from closing short: 100 * (50000 - 49000) = 100000
        let expected_pnl = Decimal::from(100) * (Decimal::from(50000) - Decimal::from(49000));
        assert_eq!(position.realized_pnl, expected_pnl);

        // New average cost for long position should be 49000
        assert_eq!(position.avg_cost, Decimal::from(49000));
    }

    #[test]
    fn test_get_all_positions() {
        let manager = PositionManager::new();

        // Create positions in multiple symbols
        manager
            .update_position(&create_execution("o1", "BTCUSD", 100, 50000))
            .expect("BTC position should succeed");
        manager
            .update_position(&create_execution("o2", "ETHUSD", 500, 3000))
            .expect("ETH position should succeed");
        manager
            .update_position(&create_execution("o3", "SOLUSD", 1000, 100))
            .expect("SOL position should succeed");

        let all_positions = manager
            .get_positions(None)
            .expect("Should get all positions");

        assert_eq!(all_positions.len(), 3);

        let symbols: Vec<String> = all_positions.iter().map(|p| p.symbol.to_string()).collect();
        assert!(symbols.contains(&"BTCUSD".to_string()));
        assert!(symbols.contains(&"ETHUSD".to_string()));
        assert!(symbols.contains(&"SOLUSD".to_string()));
    }

    #[test]
    fn test_unrealized_pnl_update() {
        let manager = PositionManager::new();

        // Buy 100 @ 50000
        manager
            .update_position(&create_execution("buy-1", "BTCUSD", 100, 50000))
            .expect("Buy should succeed");

        // Update market price to 52000
        let mut market_prices = HashMap::new();
        market_prices.insert("BTCUSD".to_string(), Decimal::from(52000));
        market_prices.insert("ETHUSD".to_string(), Decimal::from(3000)); // Different symbol

        manager
            .update_market_values_batch(market_prices)
            .expect("Market update should succeed");

        let position = manager
            .get_position("BTCUSD")
            .expect("Position should exist");

        // Unrealized P&L: 100 * (52000 - 50000) = 200000
        let expected_pnl = Decimal::from(100) * (Decimal::from(52000) - Decimal::from(50000));
        assert_eq!(position.unrealized_pnl, expected_pnl);

        // Market value: 100 * 52000 = 5200000
        let expected_value = Decimal::from(100) * Decimal::from(52000);
        assert_eq!(position.market_value, expected_value);
    }

    #[test]
    fn test_unrealized_pnl_short_position() {
        let manager = PositionManager::new();

        // Short 100 @ 50000
        let short_exec = create_sell_execution("short-1", "BTCUSD", 100, 50000);
        manager
            .update_position(&short_exec)
            .expect("Short should succeed");

        // Update market price to 49000 (profit on short)
        let mut market_prices = HashMap::new();
        market_prices.insert("BTCUSD".to_string(), Decimal::from(49000));

        manager
            .update_market_values_batch(market_prices)
            .expect("Market update should succeed");

        let position = manager
            .get_position("BTCUSD")
            .expect("Position should exist");

        // Unrealized P&L for short: -100 * (49000 - 50000) = 100000
        let expected_pnl = Decimal::from(-100) * (Decimal::from(49000) - Decimal::from(50000));
        assert_eq!(position.unrealized_pnl, expected_pnl);
    }

    #[test]
    fn test_position_not_found() {
        let manager = PositionManager::new();

        let position = manager.get_position("NONEXISTENT");
        assert!(position.is_none());
    }
}
