//! Circuit Breaker for Risk Management Service
//! Circuit Breaker Module
//!
//! Implements dynamic portfolio-based circuit breakers with distributed Redis coordination.
//! Eliminates fixed $1M daily loss limits in favor of dynamic 2% portfolio-based limits.

// #![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic)] // COMMENTED: Crate-level allows applied
#![warn(clippy::indexing_slicing)]

use std::collections::HashMap;
use std::marker::Send;
use std::sync::{
    atomic::{AtomicU32, Ordering},
    Arc,
};
// Removed foxhunt_infrastructure - not available in this simplified risk crate

use async_trait::async_trait;
use chrono::{DateTime, Utc};
use common::{Position, Price, Quantity, Symbol};
use redis::{AsyncCommands, RedisResult};
use rust_decimal::Decimal;
// REMOVED: Direct Decimal usage - use canonical types
use serde::{Deserialize, Serialize};
use tokio::sync::RwLock;
use tracing::{debug, error, info, warn};

// Import types using established patterns
use crate::error::{
    decimal_to_f64_safe, f64_to_decimal_safe, f64_to_price_safe, RiskError, RiskResult,
};

/// Circuit breaker state with Redis coordination
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CircuitBreakerState {
    /// Whether circuit breaker is currently active
    pub is_active: bool,
    /// Current portfolio value
    pub portfolio_value: Price,
    /// Dynamic daily loss limit (percentage of portfolio)
    pub daily_loss_limit: Price,
    /// Current realized daily loss
    pub current_daily_loss: Price,
    /// Reason for activation
    pub activation_reason: Option<String>,
    /// When circuit breaker was activated
    pub activated_at: Option<DateTime<Utc>>,
    /// Last state update timestamp
    pub last_updated: DateTime<Utc>,
    /// Associated account ID
    pub account_id: String,
    /// Number of consecutive violations
    pub consecutive_violations: u32,
}

impl Default for CircuitBreakerState {
    fn default() -> Self {
        Self {
            is_active: false,
            portfolio_value: Price::ZERO,
            daily_loss_limit: Price::ZERO,
            current_daily_loss: Price::ZERO,
            activation_reason: None,
            activated_at: None,
            last_updated: Utc::now(),
            account_id: "default".to_owned(),
            consecutive_violations: 0,
        }
    }
}

/// Circuit breaker configuration with dynamic limits
#[derive(Debug, Clone)]
pub struct CircuitBreakerConfig {
    /// Enable circuit breaker functionality
    pub enabled: bool,
    /// Daily loss limit as percentage of portfolio (e.g., 2.0 = 2%)
    pub daily_loss_percentage: Price,
    /// Position size limit as percentage of portfolio (e.g., 5.0 = 5%)
    pub position_limit_percentage: Price,
    /// Maximum consecutive violations before emergency stop
    pub max_consecutive_violations: u32,
    /// Redis connection URL for distributed coordination
    pub redis_url: String,
    /// Redis key prefix for namespacing
    pub redis_key_prefix: String,
    /// Enable automatic recovery from circuit breaker state
    pub auto_recovery_enabled: bool,
    /// Interval for refreshing portfolio values (seconds)
    pub portfolio_refresh_interval_secs: u64,
    /// Cooldown period before allowing new positions after breach (seconds)
    pub cooldown_period_secs: u64,
}

impl Default for CircuitBreakerConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            daily_loss_percentage: f64_to_price_safe(2.0, "default daily loss percentage")
                .unwrap_or_else(|_| {
                    warn!("Failed to create default daily loss percentage, using ZERO");
                    Price::ZERO
                }), // 2.00%
            position_limit_percentage: f64_to_price_safe(5.0, "default position limit percentage")
                .unwrap_or_else(|_| {
                    warn!("Failed to create default position limit percentage, using ZERO");
                    Price::ZERO
                }), // 5.00%
            max_consecutive_violations: 5,
            redis_url: std::env::var("REDIS_URL").unwrap_or_else(|_| {
                std::env::var("FOXHUNT_REDIS_URL").unwrap_or_else(|_| {
                    let redis_host =
                        std::env::var("REDIS_HOST").unwrap_or_else(|_| "localhost".to_owned());
                    let redis_port =
                        std::env::var("REDIS_PORT").unwrap_or_else(|_| "6379".to_owned());
                    format!("redis://{redis_host}:{redis_port}")
                })
            }),
            redis_key_prefix: "foxhunt:circuit_breaker".to_owned(),
            auto_recovery_enabled: false, // Manual recovery for safety
            portfolio_refresh_interval_secs: 60, // 1 minute
            cooldown_period_secs: 300,    // 5 minutes
        }
    }
}

/// Trait for broker account services
#[async_trait]
pub trait BrokerAccountService: Send + Sync {
    /// Get current portfolio value
    async fn get_portfolio_value(&self, account_id: &str) -> RiskResult<Decimal>;

    /// Get daily `PnL` for account
    async fn get_daily_pnl(&self, account_id: &str) -> RiskResult<Decimal>;

    /// Get current positions for account
    async fn get_positions(&self, account_id: &str) -> RiskResult<Vec<Position>>;
}

/// `PnL` metrics for risk calculations
#[derive(Debug, Clone, Default)]
pub struct PnLMetrics {
    /// Unrealized profit/loss from open positions
    pub unrealized_pnl: Decimal,
    /// Realized profit/loss from closed positions
    pub realized_pnl: Decimal,
    /// Total profit/loss (realized + unrealized)
    pub total_pnl: Decimal,
    /// Daily profit/loss for the current trading day
    pub daily_pnl: Decimal,
}

/// Real circuit breaker with dynamic portfolio-based limits
pub struct RealCircuitBreaker {
    config: CircuitBreakerConfig,
    broker_service: Arc<dyn BrokerAccountService>,
    state: Arc<RwLock<HashMap<String, CircuitBreakerState>>>,
    redis_client: Option<redis::Client>,
    consecutive_violations: AtomicU32,
    last_portfolio_refresh: Arc<RwLock<HashMap<String, DateTime<Utc>>>>,
}

impl RealCircuitBreaker {
    /// Create new circuit breaker with real broker integration
    pub async fn new(
        config: CircuitBreakerConfig,
        broker_service: Arc<dyn BrokerAccountService>,
    ) -> RiskResult<Self> {
        info!("\u{1f512} Initializing REAL Circuit Breaker");
        info!(
            "   Daily Loss Limit: {}% of portfolio value",
            config.daily_loss_percentage
        );
        info!("   Redis Coordination: {}", config.redis_url);

        // Initialize Redis connection
        let redis_client = if config.enabled {
            match redis::Client::open(config.redis_url.as_str()) {
                Ok(client) => {
                    // Test Redis connection
                    match client.get_multiplexed_async_connection().await {
                        Ok(mut conn) => {
                            match redis::cmd("PING").query_async::<String>(&mut conn).await {
                                Ok(_) => {
                                    info!("\u{2705} Redis connection established for circuit breaker coordination");
                                    Some(client)
                                },
                                Err(e) => {
                                    warn!("\u{26a0}\u{fe0f} Redis connection test failed: {}", e);
                                    Some(client) // Still store client for retry attempts
                                },
                            }
                        },
                        Err(e) => {
                            warn!(
                                "\u{26a0}\u{fe0f} Could not establish initial Redis connection: {}",
                                e
                            );
                            Some(client) // Still store client for retry attempts
                        },
                    }
                },
                Err(e) => {
                    error!("\u{274c} Failed to create Redis client: {}", e);
                    return Err(RiskError::Network(format!(
                        "Failed to create Redis client: {e}"
                    )));
                },
            }
        } else {
            None
        };

        Ok(Self {
            config,
            broker_service,
            state: Arc::new(RwLock::new(HashMap::new())),
            redis_client,
            consecutive_violations: AtomicU32::new(0),
            last_portfolio_refresh: Arc::new(RwLock::new(HashMap::new())),
        })
    }

    /// Check if circuit breaker should be triggered
    pub async fn check_circuit_breaker(&self, account_id: &str) -> RiskResult<bool> {
        if !self.config.enabled {
            return Ok(false);
        }

        debug!("Checking circuit breaker for account: {}", account_id);

        // Get or create state for account
        let mut state = self.get_or_create_state(account_id).await?;

        // Refresh portfolio value if needed
        if self.should_refresh_portfolio(&state).await? {
            self.refresh_portfolio_value(&mut state).await?;
        }

        // Check daily loss against dynamic limit - use safe conversions
        let loss_percentage =
            if state.portfolio_value > Price::ZERO {
                let current_loss_decimal = state.current_daily_loss.to_decimal().map_err(|_| {
                    RiskError::TypeConversion {
                        from_type: "Price".to_owned(),
                        to_type: "Decimal".to_owned(),
                        reason: "current daily loss conversion failed".to_owned(),
                    }
                })?;
                let portfolio_decimal =
                    state
                        .portfolio_value
                        .to_decimal()
                        .map_err(|_| RiskError::TypeConversion {
                            from_type: "Price".to_owned(),
                            to_type: "Decimal".to_owned(),
                            reason: "portfolio value conversion failed".to_owned(),
                        })?;
                let ratio = current_loss_decimal / portfolio_decimal;
                let ratio_f64 = decimal_to_f64_safe(ratio, "loss ratio calculation")?;
                f64_to_decimal_safe(ratio_f64 * 100.0, "loss percentage calculation")?
            } else {
                Decimal::ZERO
            };

        let daily_loss_limit_decimal =
            self.config
                .daily_loss_percentage
                .to_decimal()
                .map_err(|_| RiskError::TypeConversion {
                    from_type: "Price".to_owned(),
                    to_type: "Decimal".to_owned(),
                    reason: "daily loss limit conversion failed".to_owned(),
                })?;
        let should_activate = loss_percentage >= daily_loss_limit_decimal;

        if should_activate && !state.is_active {
            self.activate_circuit_breaker(
                &mut state,
                format!(
                    "Daily loss {}% exceeds limit {}%",
                    loss_percentage, self.config.daily_loss_percentage
                ),
            )
            .await?;
        }

        Ok(state.is_active)
    }

    /// Get current circuit breaker state
    pub async fn get_state(&self, account_id: &str) -> RiskResult<CircuitBreakerState> {
        let state_map = self.state.read().await;
        Ok(state_map.get(account_id).cloned().unwrap_or_else(|| {
            warn!(
                "No circuit breaker state found for account {}, using default",
                account_id
            );
            CircuitBreakerState::default()
        }))
    }

    /// Check if circuit breaker is active for an account
    pub async fn is_active(&self, account_id: &str) -> bool {
        let state_map = self.state.read().await;
        state_map
            .get(account_id)
            .is_some_and(|state| state.is_active)
    }

    /// Record a violation and potentially activate circuit breaker
    pub async fn record_violation(&self, violation_type: &str) {
        warn!(
            "\u{1f6a8} Circuit breaker violation recorded: {}",
            violation_type
        );
        self.consecutive_violations.fetch_add(1, Ordering::SeqCst);
    }

    /// Manually reset circuit breaker
    pub async fn reset_circuit_breaker(&self, account_id: &str, reason: String) -> RiskResult<()> {
        info!(
            "\u{1f513} Manually resetting circuit breaker for account {}: {}",
            account_id, reason
        );

        let mut state_map = self.state.write().await;
        if let Some(state) = state_map.get_mut(account_id) {
            state.is_active = false;
            state.activation_reason = None;
            state.activated_at = None;
            state.consecutive_violations = 0;
            state.last_updated = Utc::now();

            // Persist to Redis
            if let Err(e) = self.persist_state_to_redis(state).await {
                warn!("Failed to persist reset state to Redis: {}", e);
            }
        }

        self.consecutive_violations.store(0, Ordering::SeqCst);
        info!(
            "\u{2705} Circuit breaker reset completed for account {}",
            account_id
        );
        Ok(())
    }

    /// Check position size limits
    pub async fn check_position_limit(
        &self,
        account_id: &str,
        symbol: &Symbol,
        quantity: Quantity,
    ) -> RiskResult<bool> {
        if !self.config.enabled {
            return Ok(true); // Allow all positions if circuit breaker disabled
        }

        let state = self.get_or_create_state(account_id).await?;

        if state.portfolio_value <= Price::ZERO {
            return Ok(false); // Block if no portfolio value
        }

        // Calculate position value (simplified - would need current price in real implementation)
        let estimated_position_value = quantity.to_f64(); // Convert to f64 for calculation
        let estimated_decimal =
            f64_to_decimal_safe(estimated_position_value, "position value calculation")
                .unwrap_or_else(|_| {
                    warn!("Failed to convert position value to decimal, using ZERO");
                    Decimal::ZERO
                });
        let portfolio_decimal = state
            .portfolio_value
            .to_decimal()
            .map_err(|_| RiskError::TypeConversion {
                from_type: "Price".to_owned(),
                to_type: "Decimal".to_owned(),
                reason: "portfolio value conversion for position limit failed".to_owned(),
            })
            .unwrap_or_else(|e| {
                debug!("Portfolio value conversion used default: {}", e);
                Decimal::from(1) // Use 1 to avoid division by zero
            });
        let position_percentage = estimated_decimal / portfolio_decimal * Decimal::from(100);

        let position_limit_decimal = self
            .config
            .position_limit_percentage
            .to_decimal()
            .map_err(|_| RiskError::TypeConversion {
                from_type: "Price".to_owned(),
                to_type: "Decimal".to_owned(),
                reason: "position limit percentage conversion failed".to_owned(),
            })
            .unwrap_or_else(|e| {
                debug!("Position limit conversion used default 5%: {}", e);
                Decimal::from(5) // 5% default
            });
        let within_limit = position_percentage <= position_limit_decimal;

        if !within_limit {
            warn!(
                "Position size limit exceeded: {}% > {}% for {} in account {}",
                position_percentage, self.config.position_limit_percentage, symbol, account_id
            );
        }

        Ok(within_limit)
    }

    /// Get or create state for account
    async fn get_or_create_state(&self, account_id: &str) -> RiskResult<CircuitBreakerState> {
        let mut state_map = self.state.write().await;

        if let Some(existing_state) = state_map.get(account_id) {
            return Ok(existing_state.clone());
        }

        // Try to load from Redis first
        if let Some(redis_state) = self.load_state_from_redis(account_id).await? {
            state_map.insert(account_id.to_owned(), redis_state.clone());
            return Ok(redis_state);
        }

        // Create new state
        let mut new_state = CircuitBreakerState::default();
        new_state.account_id = account_id.to_owned();

        // Initialize portfolio value
        self.refresh_portfolio_value(&mut new_state).await?;

        state_map.insert(account_id.to_owned(), new_state.clone());
        Ok(new_state)
    }

    /// Check if portfolio should be refreshed
    async fn should_refresh_portfolio(&self, state: &CircuitBreakerState) -> RiskResult<bool> {
        let refresh_map = self.last_portfolio_refresh.read().await;

        if let Some(last_refresh) = refresh_map.get(&state.account_id) {
            let elapsed = Utc::now().signed_duration_since(*last_refresh);
            Ok(elapsed.num_seconds() >= self.config.portfolio_refresh_interval_secs as i64)
        } else {
            Ok(true) // Refresh if never refreshed
        }
    }

    /// Refresh portfolio value from broker
    async fn refresh_portfolio_value(&self, state: &mut CircuitBreakerState) -> RiskResult<()> {
        debug!(
            "Refreshing portfolio value for account: {}",
            state.account_id
        );

        // Get portfolio value from broker
        let portfolio_value = self
            .broker_service
            .get_portfolio_value(&state.account_id)
            .await?;
        let daily_pnl = self.broker_service.get_daily_pnl(&state.account_id).await?;

        state.portfolio_value = portfolio_value.into();
        state.current_daily_loss = if daily_pnl < Decimal::ZERO {
            daily_pnl.abs().into()
        } else {
            Price::ZERO
        };

        let daily_loss_percentage_decimal = self
            .config
            .daily_loss_percentage
            .to_decimal()
            .map_err(|_| RiskError::TypeConversion {
                from_type: "Price".to_owned(),
                to_type: "Decimal".to_owned(),
                reason: "daily loss percentage conversion failed".to_owned(),
            })
            .unwrap_or_else(|e| {
                warn!(
                    "Daily loss percentage conversion failed: {}, using default 2%",
                    e
                );
                Decimal::from(2) // 2% default
            });

        state.daily_loss_limit =
            ((portfolio_value * daily_loss_percentage_decimal) / Decimal::from(100)).into();
        state.last_updated = Utc::now();

        // Update refresh timestamp
        {
            let mut refresh_map = self.last_portfolio_refresh.write().await;
            refresh_map.insert(state.account_id.clone(), Utc::now());
        }

        debug!(
            "Portfolio refreshed - Value: {}, Daily Loss: {}, Limit: {}",
            state.portfolio_value, state.current_daily_loss, state.daily_loss_limit
        );

        Ok(())
    }

    /// Activate circuit breaker
    async fn activate_circuit_breaker(
        &self,
        state: &mut CircuitBreakerState,
        reason: String,
    ) -> RiskResult<()> {
        warn!(
            "\u{1f6a8} ACTIVATING CIRCUIT BREAKER for account {}: {}",
            state.account_id, reason
        );

        state.is_active = true;
        state.activation_reason = Some(reason.clone());
        state.activated_at = Some(Utc::now());
        state.consecutive_violations += 1;
        state.last_updated = Utc::now();

        // Update global violation counter
        self.consecutive_violations.fetch_add(1, Ordering::SeqCst);

        // Persist to Redis
        if let Err(e) = self.persist_state_to_redis(state).await {
            error!("Failed to persist circuit breaker state to Redis: {}", e);
        }

        // Update in-memory state
        {
            let mut state_map = self.state.write().await;
            state_map.insert(state.account_id.clone(), state.clone());
        }

        warn!(
            "\u{26d4} Circuit breaker ACTIVE - Trading halted for account {}",
            state.account_id
        );
        Ok(())
    }

    /// Load state from Redis
    async fn load_state_from_redis(
        &self,
        account_id: &str,
    ) -> RiskResult<Option<CircuitBreakerState>> {
        let Some(ref client) = self.redis_client else {
            return Ok(None);
        };

        match client.get_multiplexed_async_connection().await {
            Ok(mut conn) => {
                let key = format!("{}:{}", self.config.redis_key_prefix, account_id);
                match conn.get::<_, Option<String>>(&key).await {
                    Ok(Some(json_data)) => {
                        match serde_json::from_str::<CircuitBreakerState>(&json_data) {
                            Ok(state) => Ok(Some(state)),
                            Err(e) => {
                                warn!(
                                    "Failed to deserialize circuit breaker state from Redis: {}",
                                    e
                                );
                                Ok(None)
                            },
                        }
                    },
                    Ok(None) => Ok(None),
                    Err(e) => {
                        warn!("Failed to load circuit breaker state from Redis: {}", e);
                        Ok(None)
                    },
                }
            },
            Err(e) => {
                warn!("Failed to connect to Redis for state loading: {}", e);
                Ok(None)
            },
        }
    }

    /// Persist state to Redis
    async fn persist_state_to_redis(&self, state: &CircuitBreakerState) -> RiskResult<()> {
        let Some(ref client) = self.redis_client else {
            return Ok(()); // No Redis client, skip persistence
        };

        match client.get_multiplexed_async_connection().await {
            Ok(mut conn) => {
                let key = format!("{}:{}", self.config.redis_key_prefix, state.account_id);
                let json_data = serde_json::to_string(state)?;

                // Set with expiration (24 hours)
                let _: RedisResult<()> = conn.set_ex(&key, json_data, 86400).await;

                debug!(
                    "Persisted circuit breaker state to Redis for account {}",
                    state.account_id
                );
                Ok(())
            },
            Err(e) => {
                warn!("Failed to connect to Redis for state persistence: {}", e);
                Ok(()) // Don't fail the operation if Redis is unavailable
            },
        }
    }

    /// Get circuit breaker metrics
    pub async fn get_metrics(&self) -> HashMap<String, f64> {
        let mut metrics = HashMap::new();

        let state_map = self.state.read().await;
        let active_count = state_map.values().filter(|s| s.is_active).count();
        let total_violations = self.consecutive_violations.load(Ordering::SeqCst);

        metrics.insert("active_circuit_breakers".to_owned(), active_count as f64);
        metrics.insert("total_violations".to_owned(), f64::from(total_violations));
        metrics.insert("accounts_monitored".to_owned(), state_map.len() as f64);

        metrics
    }

    /// Health check for circuit breaker
    pub async fn health_check(&self) -> bool {
        // Check Redis connectivity if enabled
        if let Some(ref client) = self.redis_client {
            match client.get_multiplexed_async_connection().await {
                Ok(mut conn) => (redis::cmd("PING").query_async::<String>(&mut conn).await).is_ok(),
                Err(_) => false,
            }
        } else {
            true // Always healthy if Redis not configured
        }
    }
}

// REAL BROKER CLIENT - NO MOCKS IN PRODUCTION CODE
/// Real broker client implementation for production use
pub struct RealBrokerClient {
    /// HTTP endpoint URL for the broker service
    endpoint: String,
}

impl RealBrokerClient {
    #[must_use]
    pub const fn new(endpoint: String) -> Self {
        Self { endpoint }
    }
}

#[async_trait]
impl BrokerAccountService for RealBrokerClient {
    async fn get_portfolio_value(&self, account_id: &str) -> RiskResult<Decimal> {
        // Real HTTP call to broker service
        let client = reqwest::Client::new();
        let response = client
            .get(format!(
                "{}/accounts/{}/portfolio/value",
                self.endpoint, account_id
            ))
            .send()
            .await
            .map_err(|e| RiskError::BrokerError(format!("Portfolio value request failed: {e}")))?;

        if !response.status().is_success() {
            return Err(RiskError::BrokerError(format!(
                "Broker returned error: {}",
                response.status()
            )));
        }

        let data: serde_json::Value = response
            .json()
            .await
            .map_err(|e| RiskError::BrokerError(format!("Invalid response format: {e}")))?;

        let portfolio_value = data
            .get("portfolio_value")
            .and_then(serde_json::Value::as_f64)
            .ok_or_else(|| {
                RiskError::BrokerError("Missing portfolio_value in response".to_owned())
            })?;

        Decimal::try_from(portfolio_value).map_err(|e| RiskError::TypeConversion {
            from_type: "f64".to_owned(),
            to_type: "Decimal".to_owned(),
            reason: format!("portfolio value conversion failed: {e}"),
        })
    }

    async fn get_daily_pnl(&self, account_id: &str) -> RiskResult<Decimal> {
        // Real HTTP call to broker service
        let client = reqwest::Client::new();
        let response = client
            .get(format!(
                "{}/accounts/{}/pnl/daily",
                self.endpoint, account_id
            ))
            .send()
            .await
            .map_err(|e| RiskError::BrokerError(format!("Daily PnL request failed: {e}")))?;

        if !response.status().is_success() {
            return Err(RiskError::BrokerError(format!(
                "Broker returned error: {}",
                response.status()
            )));
        }

        let data: serde_json::Value = response
            .json()
            .await
            .map_err(|e| RiskError::BrokerError(format!("Invalid response format: {e}")))?;

        let daily_pnl = data
            .get("daily_pnl")
            .and_then(serde_json::Value::as_f64)
            .ok_or_else(|| RiskError::BrokerError("Missing daily_pnl in response".to_owned()))?;

        Decimal::try_from(daily_pnl).map_err(|e| RiskError::TypeConversion {
            from_type: "f64".to_owned(),
            to_type: "Decimal".to_owned(),
            reason: format!("daily PnL conversion failed: {e}"),
        })
    }

    async fn get_positions(&self, account_id: &str) -> RiskResult<Vec<Position>> {
        // Real HTTP call to broker service
        let client = reqwest::Client::new();
        let response = client
            .get(format!(
                "{}/accounts/{}/positions",
                self.endpoint, account_id
            ))
            .send()
            .await
            .map_err(|e| RiskError::BrokerError(format!("Positions request failed: {e}")))?;

        if !response.status().is_success() {
            return Err(RiskError::BrokerError(format!(
                "Broker returned error: {}",
                response.status()
            )));
        }

        let data: serde_json::Value = response
            .json()
            .await
            .map_err(|e| RiskError::BrokerError(format!("Invalid response format: {e}")))?;

        // Parse positions array from real broker response
        let positions_array = data
            .get("positions")
            .and_then(|v| v.as_array())
            .ok_or_else(|| {
                RiskError::BrokerError("Missing positions array in response".to_owned())
            })?;

        let mut positions = Vec::new();
        for pos_data in positions_array {
            let symbol = pos_data["symbol"]
                .as_str()
                .ok_or_else(|| RiskError::BrokerError("Missing symbol in position".to_owned()))?;

            let quantity_raw = pos_data["quantity"]
                .as_f64()
                .ok_or_else(|| RiskError::BrokerError("Missing quantity in position".to_owned()))?;

            let market_value_raw = pos_data["market_value"].as_f64().ok_or_else(|| {
                RiskError::BrokerError("Missing market_value in position".to_owned())
            })?;

            let quantity = Decimal::try_from(quantity_raw).map_err(|_| {
                RiskError::CalculationError("Failed to convert quantity_raw to decimal".to_owned())
            })?;
            let market_value = Decimal::try_from(market_value_raw).map_err(|_| {
                RiskError::CalculationError(
                    "Failed to convert market_value_raw to decimal".to_owned(),
                )
            })?;

            // Use Position::new constructor for consistency
            let mut position = Position::new(
                symbol.to_owned(),
                quantity,
                market_value / quantity.abs().max(Decimal::ONE), // Derive avg_price from market_value
            );

            // Update market_value to match the actual market value from broker
            position.market_value = market_value;
            position.last_updated = Utc::now();
            positions.push(position);
        }

        Ok(positions)
    }
}

#[cfg(test)]
#[allow(clippy::str_to_string)]
mod tests {
    use super::*;
    use std::sync::Arc;
    use tokio;
    // CANONICAL TYPE IMPORTS - Use types::prelude for dec! macro

    /// Mock broker service for unit tests that don't require a real broker connection
    struct MockBrokerService {
        portfolio_value: Decimal,
        daily_pnl: Decimal,
    }

    impl MockBrokerService {
        fn with_zero_portfolio() -> Self {
            Self {
                portfolio_value: Decimal::ZERO,
                daily_pnl: Decimal::ZERO,
            }
        }
    }

    #[async_trait]
    impl BrokerAccountService for MockBrokerService {
        async fn get_portfolio_value(&self, _account_id: &str) -> RiskResult<Decimal> {
            Ok(self.portfolio_value)
        }

        async fn get_daily_pnl(&self, _account_id: &str) -> RiskResult<Decimal> {
            Ok(self.daily_pnl)
        }

        async fn get_positions(&self, _account_id: &str) -> RiskResult<Vec<Position>> {
            Ok(Vec::new())
        }
    }

    fn create_test_config() -> Result<CircuitBreakerConfig, Box<dyn std::error::Error>> {
        Ok(CircuitBreakerConfig {
            enabled: true,
            daily_loss_percentage: Price::from_f64(2.00)?, // 2%
            position_limit_percentage: Price::from_f64(5.00)?, // 5%
            redis_url: {
                let host = std::env::var("REDIS_HOST")
                    .unwrap_or_else(|_| "localhost".to_string());
                format!("redis://{}:6379", host) // Default Redis URL for tests
            },
            ..Default::default()
        })
    }

    #[tokio::test]
    async fn test_circuit_breaker_creation() -> Result<(), Box<dyn std::error::Error>> {
        let config = create_test_config()?;
        let broker_service = Arc::new(RealBrokerClient::new(
            std::env::var("FOXHUNT_BROKER_SERVICE_ENDPOINT").unwrap_or_else(|_| {
                let service_host =
                    std::env::var("SERVICE_HOST").unwrap_or_else(|_| "localhost".to_string());
                format!("http://{}:50054", service_host)
            }), // Real broker service endpoint
        ));

        // Circuit breaker creation might fail if Redis is not available, which is fine for tests
        let _result = RealCircuitBreaker::new(config, broker_service).await;
        // Don't assert success since Redis might not be available in test environment
        // Debug output removed for production
        Ok(())
    }

    #[tokio::test]
    async fn test_circuit_breaker_daily_loss_check() -> Result<(), Box<dyn std::error::Error>> {
        let config = create_test_config()?;
        let broker_service = Arc::new(RealBrokerClient::new({
            let host = std::env::var("SERVICE_HOST")
                .unwrap_or_else(|_| "localhost".to_string());
            format!("http://{}:50054", host) // Real broker service endpoint
        }));

        // Skip test if broker service is not available
        if let Ok(circuit_breaker) = RealCircuitBreaker::new(config, broker_service).await {
            let account_id = "TEST_ACCOUNT";
            let result = circuit_breaker.check_circuit_breaker(account_id).await;

            match result {
                Ok(_should_trigger) => {
                    // Debug output removed for production
                },
                Err(_e) => {
                    // Debug output removed for production
                },
            }
        } else {
            // Debug output removed for production
        }
        Ok(())
    }

    #[tokio::test]
    async fn test_circuit_breaker_disabled() -> Result<(), Box<dyn std::error::Error>> {
        let mut config = create_test_config()?;
        config.enabled = false;
        let broker_service = Arc::new(RealBrokerClient::new("http://localhost:50054".to_string()));

        if let Ok(circuit_breaker) = RealCircuitBreaker::new(config, broker_service).await {
            let account_id = "TEST_ACCOUNT";
            let result = circuit_breaker.check_circuit_breaker(account_id).await?;
            assert!(!result, "Circuit breaker should not trigger when disabled");
        }
        Ok(())
    }

    #[tokio::test]
    async fn test_circuit_breaker_position_limit_zero_portfolio(
    ) -> Result<(), Box<dyn std::error::Error>> {
        let config = create_test_config()?;
        let broker_service: Arc<dyn BrokerAccountService> =
            Arc::new(MockBrokerService::with_zero_portfolio());

        if let Ok(circuit_breaker) = RealCircuitBreaker::new(config, broker_service).await {
            let account_id = "TEST_ACCOUNT";
            let symbol = Symbol::from("AAPL");
            let quantity = Quantity::from_f64(100.0)?;

            let result = circuit_breaker
                .check_position_limit(account_id, &symbol, quantity)
                .await?;
            assert!(
                !result,
                "Should block positions when portfolio value is zero"
            );
        }
        Ok(())
    }

    #[tokio::test]
    async fn test_circuit_breaker_consecutive_violations() -> Result<(), Box<dyn std::error::Error>>
    {
        let config = create_test_config()?;
        let broker_service = Arc::new(RealBrokerClient::new("http://localhost:50054".to_string()));

        if let Ok(circuit_breaker) = RealCircuitBreaker::new(config, broker_service).await {
            circuit_breaker.record_violation("Test violation 1").await;
            circuit_breaker.record_violation("Test violation 2").await;
            circuit_breaker.record_violation("Test violation 3").await;

            let metrics = circuit_breaker.get_metrics().await;
            assert_eq!(metrics.get("total_violations").copied(), Some(3.0));
        }
        Ok(())
    }

    #[tokio::test]
    async fn test_circuit_breaker_reset() -> Result<(), Box<dyn std::error::Error>> {
        let config = create_test_config()?;
        let broker_service = Arc::new(RealBrokerClient::new("http://localhost:50054".to_string()));

        if let Ok(circuit_breaker) = RealCircuitBreaker::new(config, broker_service).await {
            let account_id = "TEST_ACCOUNT";

            // Manually activate by setting state (would normally be done through check_circuit_breaker)
            let mut state = CircuitBreakerState::default();
            state.account_id = account_id.to_string();
            state.is_active = true;
            state.consecutive_violations = 3;

            // Reset the circuit breaker
            circuit_breaker
                .reset_circuit_breaker(account_id, "Manual reset for testing".to_string())
                .await?;

            // Verify it was reset
            assert!(!circuit_breaker.is_active(account_id).await);
        }
        Ok(())
    }

    #[tokio::test]
    async fn test_circuit_breaker_health_check() -> Result<(), Box<dyn std::error::Error>> {
        let config = create_test_config()?;
        let broker_service = Arc::new(RealBrokerClient::new("http://localhost:50054".to_string()));

        if let Ok(circuit_breaker) = RealCircuitBreaker::new(config, broker_service).await {
            // Health check might pass or fail depending on Redis availability
            let _ = circuit_breaker.health_check().await;
        }
        Ok(())
    }
}
