"""Slippage modeling for realistic backtesting."""

from decimal import Decimal
from typing import Dict, Optional, Any
from src.core.logger import get_logger
from src.exchanges.base import BaseExchangeAdapter

logger = get_logger(__name__)


class SlippageModel:
    """Models slippage for realistic backtesting."""
    
    def __init__(self, slippage_rate: float = 0.001):
        """Initialize slippage model.
        
        Args:
            slippage_rate: Slippage rate (0.001 = 0.1%)
        """
        self.slippage_rate = slippage_rate
        self.logger = get_logger(__name__)
    
    def calculate_fill_price(
        self,
        order_price: Decimal,
        side: str,  # "buy" or "sell"
        order_type: str,  # "market" or "limit"
        market_price: Decimal,
        volume: Decimal = Decimal(0)
    ) -> Decimal:
        """Calculate fill price with slippage.
        
        Args:
            order_price: Order price
            side: Buy or sell
            order_type: Market or limit
            market_price: Current market price
            volume: Order volume (for market impact)
            
        Returns:
            Fill price with slippage
        """
        if order_type == "limit":
            # Limit orders fill at order price (if market reaches it)
            return order_price
        
        # Market orders have slippage
        if side == "buy":
            # Buy orders pay more (slippage up)
            slippage = market_price * Decimal(str(self.slippage_rate))
            # Add market impact based on volume
            impact = market_price * Decimal(str(volume)) * Decimal("0.0001")  # Simplified
            return market_price + slippage + impact
        else:
            # Sell orders receive less (slippage down)
            slippage = market_price * Decimal(str(self.slippage_rate))
            impact = market_price * Decimal(str(volume)) * Decimal("0.0001")
            return market_price - slippage - impact


class FeeModel:
    """Models exchange fees with support for dynamic fee retrieval."""
    
    def __init__(
        self,
        maker_fee: Optional[float] = None,
        taker_fee: Optional[float] = None,
        exchange_adapter: Optional[BaseExchangeAdapter] = None,
        minimum_fee: float = 0.0
    ):
        """Initialize fee model.
        
        Args:
            maker_fee: Maker fee rate (if None, retrieved from exchange or default)
            taker_fee: Taker fee rate (if None, retrieved from exchange or default)
            exchange_adapter: Exchange adapter for dynamic fee retrieval
            minimum_fee: Minimum fee amount
        """
        self.exchange_adapter = exchange_adapter
        self.minimum_fee = Decimal(str(minimum_fee))
        self.logger = get_logger(__name__)
        
        # Set fees from parameters or retrieve from exchange
        if maker_fee is not None and taker_fee is not None:
            self.maker_fee = maker_fee
            self.taker_fee = taker_fee
        else:
            fee_structure = self._get_fee_structure()
            self.maker_fee = maker_fee if maker_fee is not None else fee_structure.get('maker', 0.001)
            self.taker_fee = taker_fee if taker_fee is not None else fee_structure.get('taker', 0.001)
    
    def _get_fee_structure(self) -> Dict[str, Any]:
        """Get fee structure from exchange adapter or defaults.
        
        Returns:
            Fee structure dictionary
        """
        if self.exchange_adapter:
            try:
                return self.exchange_adapter.get_fee_structure()
            except Exception as e:
                self.logger.warning(f"Failed to get fee structure from exchange: {e}")
        
        return {
            'maker': 0.001,  # 0.1%
            'taker': 0.001,  # 0.1%
            'minimum': 0.0
        }
    
    def calculate_fee(
        self,
        quantity: Decimal,
        price: Decimal,
        is_maker: bool = False
    ) -> Decimal:
        """Calculate trading fee.
        
        Args:
            quantity: Trade quantity
            price: Trade price
            is_maker: True if maker order
            
        Returns:
            Trading fee
        """
        if quantity <= 0 or price <= 0:
            return Decimal(0)
        
        trade_value = quantity * price
        fee_rate = self.maker_fee if is_maker else self.taker_fee
        fee = trade_value * Decimal(str(fee_rate))
        
        # Apply minimum fee
        if self.minimum_fee > 0 and fee < self.minimum_fee:
            fee = self.minimum_fee
        
        return fee
    
    def estimate_round_trip_fee(
        self,
        quantity: Decimal,
        price: Decimal
    ) -> Decimal:
        """Estimate total fees for a round-trip trade (buy + sell).
        
        Args:
            quantity: Trade quantity
            price: Trade price
            
        Returns:
            Total estimated round-trip fee
        """
        buy_fee = self.calculate_fee(quantity, price, is_maker=False)
        sell_fee = self.calculate_fee(quantity, price, is_maker=False)
        return buy_fee + sell_fee
    
    def get_minimum_profit_threshold(
        self,
        quantity: Decimal,
        price: Decimal,
        multiplier: float = 2.0
    ) -> Decimal:
        """Calculate minimum profit threshold needed to break even after fees.
        
        Args:
            quantity: Trade quantity
            price: Trade price
            multiplier: Multiplier for minimum profit (default 2.0 = 2x fees)
            
        Returns:
            Minimum profit threshold
        """
        round_trip_fee = self.estimate_round_trip_fee(quantity, price)
        return round_trip_fee * Decimal(str(multiplier))

