import logging
import asyncio
from datetime import datetime
from typing import Optional
from sqlalchemy.ext.asyncio import AsyncSession

from app.data_layer.binance_client import binance_client
from app.models.trade import Trade, TradeMode, TradeSignal, TradeStatus

logger = logging.getLogger(__name__)

MAX_RETRIES = 3
RETRY_DELAY = 2  # seconds


class ExecutionEngine:
    """
    Places REAL orders on Binance Spot via CCXT.
    Retries up to MAX_RETRIES times on transient errors.
    """

    async def open_trade(
        self,
        db: AsyncSession,
        pair: str,
        signal: str,
        entry_price: float,
        stop_loss: float,
        take_profit_levels: list,
        quantity: float,
        strategy_snapshot: dict,
    ) -> Optional[Trade]:
        """Place a market order and record the trade in the database."""
        side = "buy" if signal == "BUY" else "sell"

        order = await self._place_order_with_retry(pair, side, quantity)
        if order is None:
            logger.error(f"Failed to place {side} order for {pair} after {MAX_RETRIES} attempts")
            return None

        # Use actual filled price if available
        filled_price = order.get("average") or order.get("price") or entry_price

        trade = Trade(
            pair=pair,
            mode=TradeMode.REAL,
            signal=TradeSignal(signal),
            entry_price=filled_price,
            current_price=filled_price,
            stop_loss=stop_loss,
            take_profit_levels=take_profit_levels,
            tp_hits=[False] * len(take_profit_levels),
            quantity=quantity,
            opened_at=datetime.utcnow(),
            status=TradeStatus.OPEN,
            strategy_snapshot=strategy_snapshot,
        )

        db.add(trade)
        await db.commit()
        await db.refresh(trade)
        logger.info(f"REAL trade opened: {pair} {signal} @ {filled_price} (id={trade.id})")
        return trade

    async def _place_order_with_retry(
        self, pair: str, side: str, quantity: float
    ) -> Optional[dict]:
        exchange = binance_client.get_exchange()
        for attempt in range(1, MAX_RETRIES + 1):
            try:
                order = exchange.create_market_order(pair, side, quantity)
                logger.info(f"Order attempt {attempt} success: {order.get('id')}")
                return order
            except Exception as e:
                logger.warning(f"Order attempt {attempt}/{MAX_RETRIES} failed: {e}")
                if attempt < MAX_RETRIES:
                    await asyncio.sleep(RETRY_DELAY)
        return None


execution_engine = ExecutionEngine()
