//! Account Manager
//!
//! Manages account information, buying power, and account-related validations

use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::{debug, info, warn};

use super::engine::AccountInfo;
use crate::trading_operations::{ExecutionResult, TradingOrder};
use common::OrderSide;
use rust_decimal::prelude::ToPrimitive;
use rust_decimal::Decimal;

/// Account Manager for managing account information and validations
#[derive(Debug)]
/// AccountManager
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct AccountManager {
    /// Account information storage
    accounts: Arc<RwLock<HashMap<String, AccountInfo>>>,
}

impl AccountManager {
    /// Create a new account manager with default demo account
    pub fn new() -> Self {
        let mut accounts = HashMap::new();

        // Create default demo account
        let demo_account = AccountInfo {
            account_id: "DEMO_ACCOUNT".to_owned(),
            total_value: Decimal::from(100000),  // $100k total
            cash_balance: Decimal::from(50000),  // $50k cash
            buying_power: Decimal::from(100000), // $100k buying power
            maintenance_margin: Decimal::ZERO,   // No margin requirement
            day_trading_buying_power: Decimal::from(200000), // $200k day trading power
        };

        accounts.insert(demo_account.account_id.clone(), demo_account);

        Self {
            accounts: Arc::new(RwLock::new(accounts)),
        }
    }

    /// Get account information
    pub async fn get_account_info(&self, account_id: &str) -> Result<AccountInfo, String> {
        let accounts = self.accounts.read().await;

        accounts
            .get(account_id)
            .cloned()
            .ok_or_else(|| format!("Account {} not found", account_id))
    }

    /// Update account information
    pub async fn update_account_info(&self, account_info: AccountInfo) -> Result<(), String> {
        let mut accounts = self.accounts.write().await;

        info!("Updating account info for {}", account_info.account_id);
        accounts.insert(account_info.account_id.clone(), account_info);

        Ok(())
    }

    /// Check if account has sufficient buying power for an order
    pub async fn check_buying_power(&self, order: &TradingOrder) -> Result<(), String> {
        let accounts = self.accounts.read().await;

        // For now, use default demo account
        let account = accounts
            .get("DEMO_ACCOUNT")
            .ok_or("Demo account not found")?;

        let required_capital = match order.side {
            OrderSide::Buy => {
                // For buy orders, check against buying power
                order.quantity * order.price
            },
            OrderSide::Sell => {
                // For sell orders, typically no buying power check needed
                // unless it's a short sale, which would require margin
                Decimal::ZERO
            },
        };

        if required_capital > account.buying_power {
            return Err(format!(
                "Insufficient buying power: required {}, available {}",
                required_capital, account.buying_power
            ));
        }

        debug!(
            "Buying power check passed for order {}: required {}, available {}",
            order.id, required_capital, account.buying_power
        );

        Ok(())
    }

    /// Update account from execution
    pub async fn update_from_execution(&self, execution: &ExecutionResult) -> Result<(), String> {
        let mut accounts = self.accounts.write().await;

        // For now, use default demo account
        let account = accounts
            .get_mut("DEMO_ACCOUNT")
            .ok_or("Demo account not found")?;

        // Convert Quantity and Price to Decimal for calculation
        let execution_value = execution.executed_quantity * execution.execution_price;
        let commission = execution.commission;

        // Update cash balance based on execution side
        match execution.side {
            OrderSide::Buy => {
                // Buying: cash decreases by execution value + commission
                account.cash_balance -= execution_value + commission;
            }
            OrderSide::Sell => {
                // Selling: cash increases by execution value, minus commission
                account.cash_balance += execution_value - commission;
            }
        }

        // Update total value from cash (position values tracked separately)
        account.total_value = account.cash_balance;

        info!(
            "Account updated from execution {}: commission {}, new cash balance {}",
            execution.order_id, commission, account.cash_balance
        );

        Ok(())
    }

    /// Calculate and update buying power based on positions and market values
    pub async fn recalculate_buying_power(
        &self,
        account_id: &str,
        position_values: HashMap<String, Decimal>,
        _market_prices: HashMap<String, Decimal>,
    ) -> Result<(), String> {
        let mut accounts = self.accounts.write().await;

        let account = accounts
            .get_mut(account_id)
            .ok_or_else(|| format!("Account {} not found", account_id))?;

        // Calculate total position value
        let total_position_value: Decimal = position_values.values().sum();

        // Calculate maintenance margin requirements
        // This is simplified - real calculation would be based on position types,
        // volatility, exchange requirements, etc.
        let maintenance_margin =
            total_position_value * Decimal::try_from(0.05).unwrap_or(Decimal::ZERO); // 5% margin

        // Calculate new buying power
        // Buying power = Cash + (Total Position Value - Maintenance Margin) * Margin Multiplier
        let margin_multiplier = Decimal::try_from(2.0).unwrap_or(Decimal::from(1)); // 2:1 leverage
        let excess_liquidity = if total_position_value > maintenance_margin {
            total_position_value - maintenance_margin
        } else {
            Decimal::ZERO
        };

        let new_buying_power = account.cash_balance + (excess_liquidity * margin_multiplier);

        // Update account
        account.buying_power = new_buying_power;
        account.maintenance_margin = maintenance_margin;
        account.total_value = account.cash_balance + total_position_value;

        info!(
            "Recalculated buying power for {}: {} (maintenance margin: {})",
            account_id, new_buying_power, maintenance_margin
        );

        Ok(())
    }

    /// Check if account is in margin call
    pub async fn check_margin_call(&self, account_id: &str) -> Result<bool, String> {
        let accounts = self.accounts.read().await;

        let account = accounts
            .get(account_id)
            .ok_or_else(|| format!("Account {} not found", account_id))?;

        // Simple margin call check: if total value < maintenance margin
        let in_margin_call = account.total_value < account.maintenance_margin;

        if in_margin_call {
            warn!(
                "Account {} is in margin call: total value {} < maintenance margin {}",
                account_id, account.total_value, account.maintenance_margin
            );
        }

        // Ok variant
        Ok(in_margin_call)
    }

    /// Get account risk metrics
    pub async fn get_risk_metrics(&self, account_id: &str) -> Result<AccountRiskMetrics, String> {
        let accounts = self.accounts.read().await;

        let account = accounts
            .get(account_id)
            .ok_or_else(|| format!("Account {} not found", account_id))?;

        let leverage_ratio = if account.cash_balance > Decimal::ZERO {
            (account.total_value / account.cash_balance)
                .to_f64()
                .unwrap_or(0.0)
        } else {
            0.0
        };

        let margin_utilization = if account.buying_power > Decimal::ZERO {
            ((account.buying_power - account.cash_balance) / account.buying_power)
                .to_f64()
                .unwrap_or(0.0)
                * 100.0
        } else {
            0.0
        };

        let cash_ratio = if account.total_value > Decimal::ZERO {
            (account.cash_balance / account.total_value)
                .to_f64()
                .unwrap_or(0.0)
                * 100.0
        } else {
            0.0
        };

        Ok(AccountRiskMetrics {
            account_id: account_id.to_owned(),
            leverage_ratio,
            margin_utilization,
            cash_ratio,
            total_value: account.total_value,
            buying_power: account.buying_power,
            maintenance_margin: account.maintenance_margin,
        })
    }

    /// Add a new account
    pub async fn add_account(&self, account_info: AccountInfo) -> Result<(), String> {
        let mut accounts = self.accounts.write().await;

        if accounts.contains_key(&account_info.account_id) {
            return Err(format!(
                "Account {} already exists",
                account_info.account_id
            ));
        }

        info!("Adding new account: {}", account_info.account_id);
        accounts.insert(account_info.account_id.clone(), account_info);

        Ok(())
    }

    /// Remove an account
    pub async fn remove_account(&self, account_id: &str) -> Result<(), String> {
        // Protect demo account from deletion
        if account_id == "DEMO_ACCOUNT" {
            return Err("Cannot remove default demo account".to_owned());
        }

        let mut accounts = self.accounts.write().await;

        if accounts.remove(account_id).is_some() {
            info!("Removed account: {}", account_id);
            Ok(())
        } else {
            Err(format!("Account {} not found", account_id))
        }
    }

    /// Get all account IDs
    pub async fn get_account_ids(&self) -> Vec<String> {
        let accounts = self.accounts.read().await;
        accounts.keys().cloned().collect()
    }
}

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

/// Account risk metrics
#[derive(Debug)]
/// AccountRiskMetrics
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct AccountRiskMetrics {
    /// Account Id
    pub account_id: String,
    /// Leverage Ratio
    pub leverage_ratio: f64,
    /// Margin Utilization
    pub margin_utilization: f64,
    /// Cash Ratio
    pub cash_ratio: f64,
    /// Total Value
    pub total_value: Decimal,
    /// Buying Power
    pub buying_power: Decimal,
    /// Maintenance Margin
    pub maintenance_margin: Decimal,
}

#[cfg(test)]
#[allow(clippy::assertions_on_result_states)]
mod tests {
    use super::*;
    use crate::trading_operations::LiquidityFlag;
    use chrono::Utc;
    use common::{OrderStatus, OrderType, TimeInForce};

    fn create_test_order(
        id: &str,
        symbol: &str,
        side: OrderSide,
        quantity: i64,
        price: i64,
    ) -> TradingOrder {
        TradingOrder {
            id: id.to_string().into(),
            symbol: symbol.to_string(),
            side,
            order_type: OrderType::Limit,
            quantity: Decimal::from(quantity),
            price: Decimal::from(price),
            time_in_force: TimeInForce::GoodTillCancel,
            account_id: None,
            metadata: HashMap::new(),
            created_at: Utc::now(),
            submitted_at: None,
            executed_at: None,
            status: OrderStatus::Created,
            fill_quantity: Decimal::ZERO,
            average_fill_price: None,
        }
    }

    #[tokio::test]
    async fn test_account_creation() {
        let manager = AccountManager::new();

        let account_info = manager.get_account_info("DEMO_ACCOUNT").await;
        assert!(account_info.is_ok());

        let account = account_info.expect("Account info should be retrieved successfully");
        assert_eq!(account.account_id, "DEMO_ACCOUNT");
        assert_eq!(account.total_value, Decimal::from(100000));
        assert_eq!(account.cash_balance, Decimal::from(50000));
        assert_eq!(account.buying_power, Decimal::from(100000));
    }

    #[tokio::test]
    async fn test_account_not_found() {
        let manager = AccountManager::new();

        let result = manager.get_account_info("NONEXISTENT").await;
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("not found"));
    }

    #[tokio::test]
    async fn test_buying_power_check() {
        let manager = AccountManager::new();

        let small_order = create_test_order("test-001", "BTCUSD", OrderSide::Buy, 1, 50000);

        let result = manager.check_buying_power(&small_order).await;
        assert!(result.is_ok());

        let large_order = create_test_order("test-002", "BTCUSD", OrderSide::Buy, 10, 50000);

        let result = manager.check_buying_power(&large_order).await;
        assert!(result.is_err()); // Should fail - 500k order > 100k buying power
        assert!(result.unwrap_err().contains("Insufficient buying power"));
    }

    #[tokio::test]
    async fn test_buying_power_boundary() {
        let manager = AccountManager::new();

        // Order that exactly matches buying power (should succeed)
        let exact_order = create_test_order("test-exact", "BTCUSD", OrderSide::Buy, 2, 50000);
        let result = manager.check_buying_power(&exact_order).await;
        assert!(result.is_ok());

        // Order that exceeds by 1 cent (should fail)
        let over_order = TradingOrder {
            id: "test-over".to_string().into(),
            symbol: "BTCUSD".to_string(),
            side: OrderSide::Buy,
            order_type: OrderType::Limit,
            quantity: Decimal::from(2),
            price: Decimal::new(5000001, 2), // 50000.01
            time_in_force: TimeInForce::GoodTillCancel,
            account_id: None,
            metadata: HashMap::new(),
            created_at: Utc::now(),
            submitted_at: None,
            executed_at: None,
            status: OrderStatus::Created,
            fill_quantity: Decimal::ZERO,
            average_fill_price: None,
        };
        let result = manager.check_buying_power(&over_order).await;
        assert!(result.is_err());
    }

    #[tokio::test]
    async fn test_sell_order_buying_power() {
        let manager = AccountManager::new();

        // Sell orders should not require buying power (assuming they're closing a position)
        let sell_order = create_test_order("test-sell", "BTCUSD", OrderSide::Sell, 100, 50000);

        let result = manager.check_buying_power(&sell_order).await;
        assert!(result.is_ok()); // Should succeed regardless of size
    }

    #[tokio::test]
    async fn test_update_account_info() {
        let manager = AccountManager::new();

        let new_account = AccountInfo {
            account_id: "TEST_ACCOUNT".to_owned(),
            total_value: Decimal::from(250000),
            cash_balance: Decimal::from(100000),
            buying_power: Decimal::from(500000),
            maintenance_margin: Decimal::from(50000),
            day_trading_buying_power: Decimal::from(1000000),
        };

        let result = manager.update_account_info(new_account.clone()).await;
        assert!(result.is_ok());

        let retrieved = manager.get_account_info("TEST_ACCOUNT").await;
        assert!(retrieved.is_ok());

        let account = retrieved.expect("Account should be retrieved");
        assert_eq!(account.account_id, "TEST_ACCOUNT");
        assert_eq!(account.total_value, Decimal::from(250000));
        assert_eq!(account.buying_power, Decimal::from(500000));
    }

    #[tokio::test]
    async fn test_update_buying_power() {
        let manager = AccountManager::new();

        // Get original account
        let original = manager
            .get_account_info("DEMO_ACCOUNT")
            .await
            .expect("Demo account should exist");
        assert_eq!(original.buying_power, Decimal::from(100000));

        // Update buying power
        let updated_account = AccountInfo {
            account_id: "DEMO_ACCOUNT".to_owned(),
            total_value: original.total_value,
            cash_balance: original.cash_balance,
            buying_power: Decimal::from(150000), // Increased buying power
            maintenance_margin: original.maintenance_margin,
            day_trading_buying_power: original.day_trading_buying_power,
        };

        manager
            .update_account_info(updated_account)
            .await
            .expect("Update should succeed");

        // Verify buying power was updated
        let updated = manager
            .get_account_info("DEMO_ACCOUNT")
            .await
            .expect("Demo account should exist");
        assert_eq!(updated.buying_power, Decimal::from(150000));

        // Now a larger order should succeed
        let large_order = create_test_order("test-large", "BTCUSD", OrderSide::Buy, 2, 60000);
        let result = manager.check_buying_power(&large_order).await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn test_process_execution_updates_balances() {
        let manager = AccountManager::new();

        let execution = ExecutionResult {
            order_id: "exec-001".to_string().into(),
            symbol: "BTCUSD".to_string(),
            side: OrderSide::Buy,
            executed_quantity: Decimal::from(1),
            execution_price: Decimal::from(50000),
            execution_time: Utc::now(),
            commission: Decimal::from(10),
            liquidity_flag: LiquidityFlag::Maker,
        };

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

        // Verify cash balance was reduced by execution value + commission
        // Buy: cash -= (1 * 50000) + 10 = 50010
        let account = manager
            .get_account_info("DEMO_ACCOUNT")
            .await
            .expect("Demo account should exist");

        // Cash balance: 50000 - 50000 - 10 = -10
        assert_eq!(
            account.cash_balance,
            Decimal::from(50000) - Decimal::from(50000) - Decimal::from(10)
        );
        // Total value tracks cash balance
        assert_eq!(account.total_value, account.cash_balance);
    }

    #[tokio::test]
    async fn test_check_buying_power() {
        let manager = AccountManager::new();

        let order = create_test_order("test-reserve", "BTCUSD", OrderSide::Buy, 1, 50000);

        // Check buying power with sufficient funds
        let result = manager.check_buying_power(&order).await;
        assert!(result.is_ok());

        // Try to place order that exceeds buying power (should fail)
        let large_order = create_test_order("test-large", "ETHUSD", OrderSide::Buy, 1, 200000);
        let result = manager.check_buying_power(&large_order).await;
        assert!(result.is_err());

        // Verify original account buying power unchanged
        let account = manager
            .get_account_info("DEMO_ACCOUNT")
            .await
            .expect("Demo account should exist");
        assert_eq!(account.buying_power, Decimal::from(100000));
    }

    #[tokio::test]
    async fn test_multiple_accounts() {
        let manager = AccountManager::new();

        // Add second account
        let account2 = AccountInfo {
            account_id: "LIVE_ACCOUNT".to_owned(),
            total_value: Decimal::from(500000),
            cash_balance: Decimal::from(250000),
            buying_power: Decimal::from(1000000),
            maintenance_margin: Decimal::from(100000),
            day_trading_buying_power: Decimal::from(2000000),
        };

        manager
            .update_account_info(account2)
            .await
            .expect("Update should succeed");

        // Verify both accounts exist
        let demo = manager.get_account_info("DEMO_ACCOUNT").await;
        assert!(demo.is_ok());

        let live = manager.get_account_info("LIVE_ACCOUNT").await;
        assert!(live.is_ok());

        let live_account = live.expect("Live account should exist");
        assert_eq!(live_account.buying_power, Decimal::from(1000000));
    }

    #[tokio::test]
    async fn test_margin_requirements() {
        let manager = AccountManager::new();

        // Update account with margin requirements
        let account = AccountInfo {
            account_id: "DEMO_ACCOUNT".to_owned(),
            total_value: Decimal::from(100000),
            cash_balance: Decimal::from(50000),
            buying_power: Decimal::from(100000),
            maintenance_margin: Decimal::from(20000), // 20k maintenance margin
            day_trading_buying_power: Decimal::from(200000),
        };

        manager
            .update_account_info(account)
            .await
            .expect("Update should succeed");

        let retrieved = manager
            .get_account_info("DEMO_ACCOUNT")
            .await
            .expect("Account should exist");

        assert_eq!(retrieved.maintenance_margin, Decimal::from(20000));
    }
}
