//! Liquidation Oracles for DeFi Lending
//!
//! Specialized price feeds optimized for lending protocols with:
//! - Tighter freshness requirements
//! - Liquidation-specific price calculations
//! - Collateral factor management
//! - Health factor monitoring

use crate::error::{EconomicsError, Result};
use crate::oracle::{PriceOracle, PriceSource};
use crate::SynorDecimal;
use chrono::{DateTime, Utc};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Collateral asset configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CollateralAsset {
    /// Asset symbol
    pub symbol: String,
    /// Collateral factor (e.g., 0.75 = 75% LTV)
    pub collateral_factor: Decimal,
    /// Liquidation threshold (e.g., 0.80 = liquidate at 80%)
    pub liquidation_threshold: Decimal,
    /// Liquidation bonus (e.g., 0.05 = 5% bonus for liquidators)
    pub liquidation_bonus: Decimal,
    /// Maximum supply cap
    pub supply_cap: Option<SynorDecimal>,
    /// Is borrowing enabled
    pub borrow_enabled: bool,
    /// Reserve factor (protocol fee)
    pub reserve_factor: Decimal,
    /// Price oracle volatility multiplier (for risky assets)
    pub volatility_multiplier: Decimal,
}

impl CollateralAsset {
    /// Create a standard collateral configuration
    pub fn standard(symbol: impl Into<String>) -> Self {
        Self {
            symbol: symbol.into(),
            collateral_factor: Decimal::new(75, 2),      // 75%
            liquidation_threshold: Decimal::new(80, 2),  // 80%
            liquidation_bonus: Decimal::new(5, 2),       // 5%
            supply_cap: None,
            borrow_enabled: true,
            reserve_factor: Decimal::new(10, 2),         // 10%
            volatility_multiplier: Decimal::ONE,
        }
    }

    /// Create for stablecoin (higher collateral factor)
    pub fn stablecoin(symbol: impl Into<String>) -> Self {
        Self {
            symbol: symbol.into(),
            collateral_factor: Decimal::new(90, 2),      // 90%
            liquidation_threshold: Decimal::new(95, 2),  // 95%
            liquidation_bonus: Decimal::new(2, 2),       // 2%
            supply_cap: None,
            borrow_enabled: true,
            reserve_factor: Decimal::new(5, 2),          // 5%
            volatility_multiplier: Decimal::ONE,
        }
    }

    /// Create for volatile asset (lower collateral factor)
    pub fn volatile(symbol: impl Into<String>) -> Self {
        Self {
            symbol: symbol.into(),
            collateral_factor: Decimal::new(50, 2),      // 50%
            liquidation_threshold: Decimal::new(65, 2),  // 65%
            liquidation_bonus: Decimal::new(10, 2),      // 10%
            supply_cap: None,
            borrow_enabled: true,
            reserve_factor: Decimal::new(20, 2),         // 20%
            volatility_multiplier: Decimal::new(12, 1),  // 1.2x
        }
    }
}

/// User position in the lending protocol
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LendingPosition {
    /// User account ID
    pub account_id: String,
    /// Collateral deposits by asset
    pub collateral: HashMap<String, SynorDecimal>,
    /// Borrowings by asset
    pub borrows: HashMap<String, SynorDecimal>,
    /// Interest owed by asset
    pub interest_owed: HashMap<String, SynorDecimal>,
    /// Last update timestamp
    pub last_update: DateTime<Utc>,
}

impl LendingPosition {
    pub fn new(account_id: impl Into<String>) -> Self {
        Self {
            account_id: account_id.into(),
            collateral: HashMap::new(),
            borrows: HashMap::new(),
            interest_owed: HashMap::new(),
            last_update: Utc::now(),
        }
    }

    /// Add collateral deposit
    pub fn deposit(&mut self, asset: impl Into<String>, amount: SynorDecimal) {
        let asset = asset.into();
        *self.collateral.entry(asset).or_insert(Decimal::ZERO) += amount;
        self.last_update = Utc::now();
    }

    /// Borrow against collateral
    pub fn borrow(&mut self, asset: impl Into<String>, amount: SynorDecimal) {
        let asset = asset.into();
        *self.borrows.entry(asset).or_insert(Decimal::ZERO) += amount;
        self.last_update = Utc::now();
    }

    /// Repay borrowed amount
    pub fn repay(&mut self, asset: impl Into<String>, amount: SynorDecimal) {
        let asset = asset.into();
        if let Some(borrowed) = self.borrows.get_mut(&asset) {
            *borrowed = (*borrowed - amount).max(Decimal::ZERO);
        }
        self.last_update = Utc::now();
    }

    /// Withdraw collateral
    pub fn withdraw(&mut self, asset: impl Into<String>, amount: SynorDecimal) -> Result<()> {
        let asset = asset.into();
        let current = self.collateral.get(&asset).copied().unwrap_or(Decimal::ZERO);
        if amount > current {
            return Err(EconomicsError::InsufficientFunds {
                required: amount,
                available: current,
            });
        }
        *self.collateral.entry(asset).or_insert(Decimal::ZERO) -= amount;
        self.last_update = Utc::now();
        Ok(())
    }
}

/// Health status of a position
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthStatus {
    /// Health factor (> 1.0 is healthy, < 1.0 is liquidatable)
    pub health_factor: Decimal,
    /// Total collateral value in USD
    pub total_collateral_usd: SynorDecimal,
    /// Risk-adjusted collateral value
    pub adjusted_collateral_usd: SynorDecimal,
    /// Total borrow value in USD
    pub total_borrow_usd: SynorDecimal,
    /// Borrow utilization rate
    pub utilization_rate: Decimal,
    /// Maximum additional borrow capacity in USD
    pub borrow_capacity_usd: SynorDecimal,
    /// Is position liquidatable
    pub is_liquidatable: bool,
    /// Timestamp of calculation
    pub timestamp: DateTime<Utc>,
    /// Price sources used
    pub price_sources: Vec<String>,
}

/// Liquidation event
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LiquidationEvent {
    /// Position being liquidated
    pub account_id: String,
    /// Debt asset being repaid
    pub debt_asset: String,
    /// Debt amount repaid
    pub debt_amount: SynorDecimal,
    /// Collateral asset seized
    pub collateral_asset: String,
    /// Collateral amount seized
    pub collateral_amount: SynorDecimal,
    /// Liquidator address
    pub liquidator: String,
    /// Liquidation bonus earned
    pub bonus_amount: SynorDecimal,
    /// Timestamp
    pub timestamp: DateTime<Utc>,
    /// Price used for liquidation
    pub price_at_liquidation: SynorDecimal,
}

/// Liquidation price feed with strict freshness
#[derive(Debug, Clone)]
pub struct LiquidationPrice {
    /// Asset symbol
    pub asset: String,
    /// Current price
    pub price: SynorDecimal,
    /// Price source
    pub source: PriceSource,
    /// Timestamp
    pub timestamp: DateTime<Utc>,
    /// Confidence score
    pub confidence: f64,
    /// Is price fresh enough for liquidations
    pub is_fresh: bool,
    /// Time until stale (seconds)
    pub freshness_remaining: i64,
}

/// Configuration for liquidation oracle
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LiquidationOracleConfig {
    /// Maximum price age for liquidations (seconds)
    pub max_price_age: i64,
    /// Minimum confidence required
    pub min_confidence: f64,
    /// Minimum price sources required
    pub min_sources: usize,
    /// Grace period before liquidation (seconds)
    pub liquidation_grace_period: i64,
    /// Minimum liquidation amount (USD)
    pub min_liquidation_amount: SynorDecimal,
    /// Maximum liquidation percentage per transaction
    pub max_liquidation_pct: Decimal,
    /// Enable partial liquidations
    pub partial_liquidation: bool,
    /// Close factor (max debt repayable at once)
    pub close_factor: Decimal,
}

impl Default for LiquidationOracleConfig {
    fn default() -> Self {
        Self {
            max_price_age: 60,        // 1 minute (stricter than general oracle)
            min_confidence: 0.9,
            min_sources: 2,
            liquidation_grace_period: 300, // 5 minutes
            min_liquidation_amount: Decimal::new(10, 0), // $10
            max_liquidation_pct: Decimal::new(50, 2),    // 50% at a time
            partial_liquidation: true,
            close_factor: Decimal::new(50, 2), // 50%
        }
    }
}

/// Liquidation oracle for DeFi lending
pub struct LiquidationOracle {
    config: LiquidationOracleConfig,
    /// Base price oracle
    price_oracle: PriceOracle,
    /// Collateral asset configurations
    assets: HashMap<String, CollateralAsset>,
    /// User positions
    positions: HashMap<String, LendingPosition>,
    /// Liquidation events history
    liquidation_history: Vec<LiquidationEvent>,
    /// Cached liquidation prices
    price_cache: HashMap<String, LiquidationPrice>,
}

impl LiquidationOracle {
    pub fn new(price_oracle: PriceOracle) -> Self {
        Self::with_config(price_oracle, LiquidationOracleConfig::default())
    }

    pub fn with_config(price_oracle: PriceOracle, config: LiquidationOracleConfig) -> Self {
        Self {
            config,
            price_oracle,
            assets: HashMap::new(),
            positions: HashMap::new(),
            liquidation_history: Vec::new(),
            price_cache: HashMap::new(),
        }
    }

    /// Register a collateral asset
    pub fn register_asset(&mut self, asset: CollateralAsset) {
        self.assets.insert(asset.symbol.clone(), asset);
    }

    /// Get or create a user position
    pub fn get_position(&self, account_id: &str) -> Option<&LendingPosition> {
        self.positions.get(account_id)
    }

    /// Create a new position
    pub fn create_position(&mut self, account_id: impl Into<String>) -> &mut LendingPosition {
        let account_id = account_id.into();
        self.positions.entry(account_id.clone())
            .or_insert_with(|| LendingPosition::new(account_id))
    }

    /// Get liquidation-safe price for an asset
    pub fn get_liquidation_price(&mut self, asset: &str) -> Result<LiquidationPrice> {
        // Check cache first
        if let Some(cached) = self.price_cache.get(asset) {
            if cached.is_fresh {
                return Ok(cached.clone());
            }
        }

        // Get fresh price from oracle
        let aggregated = self.price_oracle.get_aggregated_price(asset, "USD")?;

        let age = (Utc::now() - aggregated.last_update).num_seconds();
        let is_fresh = age < self.config.max_price_age
            && aggregated.confidence >= self.config.min_confidence
            && aggregated.source_count >= self.config.min_sources;

        let liq_price = LiquidationPrice {
            asset: asset.to_string(),
            price: aggregated.twap, // Use TWAP for liquidations
            source: PriceSource::Aggregated,
            timestamp: aggregated.last_update,
            confidence: aggregated.confidence,
            is_fresh,
            freshness_remaining: self.config.max_price_age - age,
        };

        self.price_cache.insert(asset.to_string(), liq_price.clone());
        Ok(liq_price)
    }

    /// Calculate health status for a position
    pub fn calculate_health(&mut self, account_id: &str) -> Result<HealthStatus> {
        // Clone position data to avoid borrow conflicts with get_liquidation_price
        let (collateral, borrows, interest_owed) = {
            let position = self.positions.get(account_id)
                .ok_or_else(|| EconomicsError::AccountNotFound(account_id.to_string()))?;
            (
                position.collateral.clone(),
                position.borrows.clone(),
                position.interest_owed.clone(),
            )
        };

        let mut total_collateral_usd = Decimal::ZERO;
        let mut adjusted_collateral_usd = Decimal::ZERO;
        let mut total_borrow_usd = Decimal::ZERO;
        let mut price_sources = Vec::new();

        // Calculate collateral value
        for (asset, amount) in &collateral {
            let price = self.get_liquidation_price(asset)?;
            if !price.is_fresh {
                return Err(EconomicsError::StalePrice {
                    asset: asset.clone(),
                    age_seconds: -price.freshness_remaining,
                });
            }

            let asset_config = self.assets.get(asset)
                .ok_or_else(|| EconomicsError::InvalidPrice(format!("Unknown asset: {}", asset)))?;

            let value = *amount * price.price;
            total_collateral_usd += value;
            adjusted_collateral_usd += value * asset_config.liquidation_threshold;
            price_sources.push(format!("{}:{}", asset, price.source));
        }

        // Calculate borrow value
        for (asset, amount) in &borrows {
            let price = self.get_liquidation_price(asset)?;
            if !price.is_fresh {
                return Err(EconomicsError::StalePrice {
                    asset: asset.clone(),
                    age_seconds: -price.freshness_remaining,
                });
            }

            let value = *amount * price.price;

            // Add interest owed
            let interest = interest_owed.get(asset).copied().unwrap_or(Decimal::ZERO);
            total_borrow_usd += value + (interest * price.price);
        }

        // Calculate health factor
        let health_factor = if total_borrow_usd == Decimal::ZERO {
            Decimal::new(999, 0) // Effectively infinite
        } else {
            adjusted_collateral_usd / total_borrow_usd
        };

        // Calculate utilization
        let utilization_rate = if adjusted_collateral_usd == Decimal::ZERO {
            Decimal::ZERO
        } else {
            total_borrow_usd / adjusted_collateral_usd
        };

        // Calculate remaining borrow capacity
        let borrow_capacity_usd = (adjusted_collateral_usd - total_borrow_usd).max(Decimal::ZERO);

        Ok(HealthStatus {
            health_factor,
            total_collateral_usd,
            adjusted_collateral_usd,
            total_borrow_usd,
            utilization_rate,
            borrow_capacity_usd,
            is_liquidatable: health_factor < Decimal::ONE,
            timestamp: Utc::now(),
            price_sources,
        })
    }

    /// Check if a position can be liquidated
    pub fn can_liquidate(&mut self, account_id: &str) -> Result<bool> {
        let health = self.calculate_health(account_id)?;
        Ok(health.is_liquidatable)
    }

    /// Calculate liquidation amounts
    pub fn calculate_liquidation(
        &mut self,
        account_id: &str,
        debt_asset: &str,
        collateral_asset: &str,
    ) -> Result<LiquidationCalculation> {
        let health = self.calculate_health(account_id)?;
        if !health.is_liquidatable {
            return Err(EconomicsError::InvalidPrice("Position is healthy".into()));
        }

        let position = self.positions.get(account_id)
            .ok_or_else(|| EconomicsError::AccountNotFound(account_id.to_string()))?;

        let debt_amount = position.borrows.get(debt_asset).copied().unwrap_or(Decimal::ZERO);
        let collateral_amount = position.collateral.get(collateral_asset).copied().unwrap_or(Decimal::ZERO);

        if debt_amount == Decimal::ZERO {
            return Err(EconomicsError::InvalidPrice("No debt to repay".into()));
        }
        if collateral_amount == Decimal::ZERO {
            return Err(EconomicsError::InvalidPrice("No collateral to seize".into()));
        }

        let debt_price = self.get_liquidation_price(debt_asset)?;
        let collateral_price = self.get_liquidation_price(collateral_asset)?;

        let collateral_config = self.assets.get(collateral_asset)
            .ok_or_else(|| EconomicsError::InvalidPrice(format!("Unknown asset: {}", collateral_asset)))?;

        // Max debt repayable = close_factor * total_debt
        let max_debt_repay = debt_amount * self.config.close_factor;
        let max_debt_usd = max_debt_repay * debt_price.price;

        // Calculate collateral to seize with bonus
        let bonus_multiplier = Decimal::ONE + collateral_config.liquidation_bonus;
        let collateral_to_seize_usd = max_debt_usd * bonus_multiplier;
        let collateral_to_seize = collateral_to_seize_usd / collateral_price.price;

        // Cap at available collateral
        let actual_collateral_seized = collateral_to_seize.min(collateral_amount);
        let actual_debt_repaid = if actual_collateral_seized < collateral_to_seize {
            // Partial liquidation
            (actual_collateral_seized * collateral_price.price) / (bonus_multiplier * debt_price.price)
        } else {
            max_debt_repay
        };

        let bonus_amount = actual_collateral_seized * collateral_config.liquidation_bonus / bonus_multiplier;

        Ok(LiquidationCalculation {
            account_id: account_id.to_string(),
            debt_asset: debt_asset.to_string(),
            debt_to_repay: actual_debt_repaid,
            collateral_asset: collateral_asset.to_string(),
            collateral_to_seize: actual_collateral_seized,
            liquidation_bonus: bonus_amount,
            debt_price: debt_price.price,
            collateral_price: collateral_price.price,
            health_factor_before: health.health_factor,
        })
    }

    /// Execute a liquidation
    pub fn execute_liquidation(
        &mut self,
        account_id: &str,
        debt_asset: &str,
        collateral_asset: &str,
        liquidator: &str,
    ) -> Result<LiquidationEvent> {
        let calc = self.calculate_liquidation(account_id, debt_asset, collateral_asset)?;

        // Update position
        let position = self.positions.get_mut(account_id)
            .ok_or_else(|| EconomicsError::AccountNotFound(account_id.to_string()))?;

        // Reduce debt
        if let Some(debt) = position.borrows.get_mut(debt_asset) {
            *debt = (*debt - calc.debt_to_repay).max(Decimal::ZERO);
        }

        // Reduce collateral
        if let Some(collateral) = position.collateral.get_mut(collateral_asset) {
            *collateral = (*collateral - calc.collateral_to_seize).max(Decimal::ZERO);
        }

        let event = LiquidationEvent {
            account_id: account_id.to_string(),
            debt_asset: debt_asset.to_string(),
            debt_amount: calc.debt_to_repay,
            collateral_asset: collateral_asset.to_string(),
            collateral_amount: calc.collateral_to_seize,
            liquidator: liquidator.to_string(),
            bonus_amount: calc.liquidation_bonus,
            timestamp: Utc::now(),
            price_at_liquidation: calc.collateral_price,
        };

        self.liquidation_history.push(event.clone());
        Ok(event)
    }

    /// Get positions at risk of liquidation
    pub fn get_at_risk_positions(&mut self, threshold: Decimal) -> Vec<(String, HealthStatus)> {
        let account_ids: Vec<_> = self.positions.keys().cloned().collect();
        let mut at_risk = Vec::new();

        for account_id in account_ids {
            if let Ok(health) = self.calculate_health(&account_id) {
                if health.health_factor < threshold {
                    at_risk.push((account_id, health));
                }
            }
        }

        // Sort by health factor (most at risk first)
        at_risk.sort_by(|a, b| a.1.health_factor.cmp(&b.1.health_factor));
        at_risk
    }

    /// Get liquidation history
    pub fn get_liquidation_history(&self) -> &[LiquidationEvent] {
        &self.liquidation_history
    }

    /// Calculate protocol reserves from liquidations
    pub fn calculate_reserves(&self) -> HashMap<String, SynorDecimal> {
        let mut reserves: HashMap<String, SynorDecimal> = HashMap::new();

        for event in &self.liquidation_history {
            // Protocol gets a portion of the liquidation bonus
            if let Some(asset_config) = self.assets.get(&event.collateral_asset) {
                let protocol_share = event.bonus_amount * asset_config.reserve_factor;
                *reserves.entry(event.collateral_asset.clone()).or_insert(Decimal::ZERO) += protocol_share;
            }
        }

        reserves
    }

    /// Get statistics
    pub fn get_stats(&self) -> LiquidationStats {
        let total_positions = self.positions.len();
        let total_liquidations = self.liquidation_history.len();

        let total_debt_liquidated: SynorDecimal = self.liquidation_history.iter()
            .map(|e| e.debt_amount)
            .sum();

        let total_collateral_seized: SynorDecimal = self.liquidation_history.iter()
            .map(|e| e.collateral_amount)
            .sum();

        let unique_liquidated: std::collections::HashSet<_> = self.liquidation_history.iter()
            .map(|e| &e.account_id)
            .collect();

        LiquidationStats {
            total_positions,
            total_liquidations,
            unique_accounts_liquidated: unique_liquidated.len(),
            total_debt_liquidated,
            total_collateral_seized,
        }
    }
}

/// Calculated liquidation parameters
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LiquidationCalculation {
    pub account_id: String,
    pub debt_asset: String,
    pub debt_to_repay: SynorDecimal,
    pub collateral_asset: String,
    pub collateral_to_seize: SynorDecimal,
    pub liquidation_bonus: SynorDecimal,
    pub debt_price: SynorDecimal,
    pub collateral_price: SynorDecimal,
    pub health_factor_before: Decimal,
}

/// Liquidation statistics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LiquidationStats {
    pub total_positions: usize,
    pub total_liquidations: usize,
    pub unique_accounts_liquidated: usize,
    pub total_debt_liquidated: SynorDecimal,
    pub total_collateral_seized: SynorDecimal,
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::oracle::{OracleConfig, PriceOracle, TokenPrice};
    use rust_decimal_macros::dec;

    fn setup_oracle() -> LiquidationOracle {
        let mut price_oracle = PriceOracle::with_config(OracleConfig::default());

        // Add prices from multiple sources for test validity
        price_oracle.update_price(TokenPrice::new("ETH", "USD", dec!(2000), PriceSource::Internal)).unwrap();
        price_oracle.update_price(TokenPrice::new("ETH", "USD", dec!(2000), PriceSource::Aggregated)).unwrap();
        price_oracle.update_price(TokenPrice::new("SYNOR", "USD", dec!(1), PriceSource::Internal)).unwrap();
        price_oracle.update_price(TokenPrice::new("SYNOR", "USD", dec!(1), PriceSource::Aggregated)).unwrap();
        price_oracle.update_price(TokenPrice::new("USDC", "USD", dec!(1), PriceSource::Internal)).unwrap();
        price_oracle.update_price(TokenPrice::new("USDC", "USD", dec!(1), PriceSource::Aggregated)).unwrap();

        // Use test config with relaxed freshness requirements
        let test_config = LiquidationOracleConfig {
            max_price_age: 3600,        // 1 hour for tests
            min_confidence: 0.5,        // Lower confidence threshold
            min_sources: 1,             // Single source OK for tests
            ..Default::default()
        };

        let mut oracle = LiquidationOracle::with_config(price_oracle, test_config);

        // Register assets
        oracle.register_asset(CollateralAsset::volatile("ETH"));
        oracle.register_asset(CollateralAsset::standard("SYNOR"));
        oracle.register_asset(CollateralAsset::stablecoin("USDC"));

        oracle
    }

    #[test]
    fn test_collateral_asset_configs() {
        let standard = CollateralAsset::standard("ETH");
        assert_eq!(standard.collateral_factor, dec!(0.75));

        let stable = CollateralAsset::stablecoin("USDC");
        assert_eq!(stable.collateral_factor, dec!(0.90));

        let volatile = CollateralAsset::volatile("SHIB");
        assert_eq!(volatile.collateral_factor, dec!(0.50));
    }

    #[test]
    fn test_healthy_position() {
        let mut oracle = setup_oracle();

        // Create position with good health
        let pos = oracle.create_position("user1");
        pos.deposit("ETH", dec!(1));      // $2000 worth
        pos.borrow("USDC", dec!(500));    // Borrow $500

        let health = oracle.calculate_health("user1").unwrap();

        // ETH liquidation threshold = 65%, so adjusted = $2000 * 0.65 = $1300
        // Borrow = $500
        // Health = 1300 / 500 = 2.6
        assert!(health.health_factor > Decimal::ONE);
        assert!(!health.is_liquidatable);
    }

    #[test]
    fn test_liquidatable_position() {
        let mut oracle = setup_oracle();

        // Create position close to liquidation
        let pos = oracle.create_position("user2");
        pos.deposit("ETH", dec!(1));        // $2000 worth
        pos.borrow("USDC", dec!(1500));     // Borrow $1500

        let health = oracle.calculate_health("user2").unwrap();

        // ETH liquidation threshold = 65%, so adjusted = $2000 * 0.65 = $1300
        // Borrow = $1500
        // Health = 1300 / 1500 = 0.867
        assert!(health.health_factor < Decimal::ONE);
        assert!(health.is_liquidatable);
    }

    #[test]
    fn test_liquidation_calculation() {
        let mut oracle = setup_oracle();

        // Create unhealthy position
        let pos = oracle.create_position("user3");
        pos.deposit("ETH", dec!(1));
        pos.borrow("USDC", dec!(1500));

        let calc = oracle.calculate_liquidation("user3", "USDC", "ETH").unwrap();

        // Should be able to liquidate
        assert!(calc.debt_to_repay > Decimal::ZERO);
        assert!(calc.collateral_to_seize > Decimal::ZERO);
        assert!(calc.liquidation_bonus > Decimal::ZERO);
    }

    #[test]
    fn test_at_risk_positions() {
        let mut oracle = setup_oracle();

        // Create healthy position
        let pos1 = oracle.create_position("healthy");
        pos1.deposit("ETH", dec!(10));
        pos1.borrow("USDC", dec!(1000));

        // Create at-risk position
        let pos2 = oracle.create_position("risky");
        pos2.deposit("ETH", dec!(1));
        pos2.borrow("USDC", dec!(1200));

        let at_risk = oracle.get_at_risk_positions(dec!(1.5));

        // Only risky position should be flagged
        assert_eq!(at_risk.len(), 1);
        assert_eq!(at_risk[0].0, "risky");
    }
}
