"""Portfolio rebalancing engine."""

from decimal import Decimal
from typing import Dict, List, Optional
from datetime import datetime
from sqlalchemy.orm import Session
from src.core.database import get_database, RebalancingEvent
from src.core.logger import get_logger
from src.portfolio.tracker import get_portfolio_tracker
from src.trading.engine import get_trading_engine

logger = get_logger(__name__)


class RebalancingEngine:
    """Portfolio rebalancing engine."""
    
    def __init__(self):
        """Initialize rebalancing engine."""
        self.db = get_database()
        self.tracker = get_portfolio_tracker()
        self.trading_engine = get_trading_engine()
        self.logger = get_logger(__name__)
    
    def rebalance(
        self,
        target_allocations: Dict[str, float],
        exchange_id: int,
        paper_trading: bool = True
    ) -> bool:
        """Rebalance portfolio to target allocations.
        
        Args:
            target_allocations: Dictionary of symbol -> target percentage
            exchange_id: Exchange ID
            paper_trading: Paper trading flag
            
        Returns:
            True if rebalancing successful
        """
        try:
            # Get current portfolio
            portfolio = self.tracker.get_current_portfolio(paper_trading)
            total_value = portfolio['performance']['current_value']
            
            # Calculate current allocations
            current_allocations = {}
            for pos in portfolio['positions']:
                pos_value = pos['quantity'] * pos['current_price']
                current_allocations[pos['symbol']] = float(pos_value / total_value) if total_value > 0 else 0.0
            
            # Get exchange adapter for fee calculations
            adapter = await self.trading_engine.get_exchange_adapter(exchange_id)
            
            # Calculate required trades, factoring in fees
            orders = []
            from src.trading.fee_calculator import get_fee_calculator
            fee_calculator = get_fee_calculator()
            
            # Get fee threshold from config (default 0.5% to account for round-trip fees)
            fee_threshold = Decimal(str(self.tracker.db.get_session().query(
                # Get from config
            ))) if False else Decimal("0.005")  # 0.5% default threshold
            
            for symbol, target_pct in target_allocations.items():
                current_pct = current_allocations.get(symbol, 0.0)
                deviation = target_pct - current_pct
                
                # Only rebalance if deviation exceeds fee threshold
                # Default threshold is 1%, but we'll use a configurable fee-aware threshold
                min_deviation = max(Decimal("0.01"), fee_threshold)  # At least 1% or fee threshold
                
                if abs(deviation) > min_deviation:
                    target_value = total_value * Decimal(str(target_pct))
                    current_value = Decimal(str(current_allocations.get(symbol, 0.0))) * total_value
                    trade_value = target_value - current_value
                    
                    # Get current price
                    if adapter:
                        ticker = await adapter.get_ticker(symbol)
                        price = ticker.get('last', Decimal(0))
                        
                        if price > 0:
                            # Estimate fee for this trade
                            estimated_quantity = abs(trade_value / price)
                            estimated_fee = fee_calculator.estimate_round_trip_fee(
                                quantity=estimated_quantity,
                                price=price,
                                exchange_adapter=adapter
                            )
                            
                            # Adjust trade value to account for fees
                            # For buy: reduce quantity to account for fee
                            # For sell: fee comes from proceeds
                            if trade_value > 0:  # Buy
                                # Reduce trade value by estimated fee
                                adjusted_trade_value = trade_value - estimated_fee
                                quantity = adjusted_trade_value / price if price > 0 else Decimal(0)
                            else:  # Sell
                                # Fee comes from proceeds, so quantity stays the same
                                quantity = abs(trade_value / price)
                            
                            if quantity > 0:
                                side = 'buy' if trade_value > 0 else 'sell'
                                orders.append({
                                    'symbol': symbol,
                                    'side': side,
                                    'quantity': quantity,
                                    'price': price,
                                })
            
            # Execute rebalancing orders
            executed_orders = []
            for order in orders:
                from src.core.database import OrderSide, OrderType
                side = OrderSide.BUY if order['side'] == 'buy' else OrderSide.SELL
                
                result = await self.trading_engine.execute_order(
                    exchange_id=exchange_id,
                    strategy_id=None,
                    symbol=order['symbol'],
                    side=side,
                    order_type=OrderType.MARKET,
                    quantity=order['quantity'],
                    paper_trading=paper_trading
                )
                
                if result:
                    executed_orders.append(result.id)
            
            # Record rebalancing event
            self._record_rebalancing_event(
                'manual',
                target_allocations,
                current_allocations,
                executed_orders
            )
            
            return True
        except Exception as e:
            logger.error(f"Failed to rebalance portfolio: {e}")
            return False
    
    def _record_rebalancing_event(
        self,
        trigger_type: str,
        target_allocations: Dict[str, float],
        before_allocations: Dict[str, float],
        orders_placed: List[int]
    ):
        """Record rebalancing event in database.
        
        Args:
            trigger_type: Trigger type
            target_allocations: Target allocations
            before_allocations: Allocations before rebalancing
            orders_placed: List of order IDs
        """
        session = self.db.get_session()
        try:
            # Get after allocations
            portfolio = self.tracker.get_current_portfolio()
            total_value = portfolio['performance']['current_value']
            after_allocations = {}
            for pos in portfolio['positions']:
                pos_value = pos['quantity'] * pos['current_price']
                after_allocations[pos['symbol']] = float(pos_value / total_value) if total_value > 0 else 0.0
            
            event = RebalancingEvent(
                trigger_type=trigger_type,
                target_allocations=target_allocations,
                before_allocations=before_allocations,
                after_allocations=after_allocations,
                orders_placed=orders_placed,
                timestamp=datetime.utcnow()
            )
            session.add(event)
            session.commit()
        except Exception as e:
            session.rollback()
            logger.error(f"Failed to record rebalancing event: {e}")
        finally:
            session.close()


# Global rebalancing engine
_rebalancing_engine: Optional[RebalancingEngine] = None


def get_rebalancing_engine() -> RebalancingEngine:
    """Get global rebalancing engine instance."""
    global _rebalancing_engine
    if _rebalancing_engine is None:
        _rebalancing_engine = RebalancingEngine()
    return _rebalancing_engine

