from __future__ import annotations

import asyncio
import logging
import time

from binance.client import Client
from binance.enums import ORDER_TYPE_MARKET, SIDE_BUY, SIDE_SELL
from binance.exceptions import BinanceAPIException, BinanceRequestException

from config.settings import settings
from price.cache import price_cache

logger = logging.getLogger(__name__)


class TradeExecutor:
    """ينفذ الصفقات على Binance Spot أو يحاكيها حسب الوضع."""

    def __init__(self) -> None:
        self._client: Client | None = None

    def _get_client(self) -> Client:
        if self._client is None:
            if settings.TRADING_MODE == "LIVE":
                if not settings.BINANCE_API_KEY or not settings.BINANCE_API_SECRET:
                    raise RuntimeError(
                        "BINANCE_API_KEY و BINANCE_API_SECRET مطلوبان في وضع LIVE"
                    )
                self._client = Client(settings.BINANCE_API_KEY, settings.BINANCE_API_SECRET)
            else:
                self._client = Client("", "")
        return self._client

    def _get_cached_price(self, symbol: str) -> float:
        if price_cache.is_stale(symbol, max_age_seconds=10):
            raise ValueError(f"سعر {symbol} قديم أو غير متوفر في الذاكرة")
        price = price_cache.get(symbol)
        if price is None:
            raise ValueError(f"لا يوجد سعر مخزَّن لـ {symbol}")
        return price

    async def execute(self, signal: dict) -> dict:
        symbol: str = signal["symbol"]
        action: str = signal["action"]
        quantity: float = signal["quantity"]

        try:
            price = self._get_cached_price(symbol)
        except ValueError as e:
            logger.error(f"فشل التنفيذ — {e}")
            return {
                "status": "failed",
                "symbol": symbol,
                "action": action,
                "quantity": quantity,
                "price": None,
                "order_id": None,
                "reason": str(e),
            }

        if settings.TRADING_MODE == "SIMULATION":
            return await self._simulate(symbol, action, quantity, price)
        return await self._execute_live(symbol, action, quantity, price)

    async def _simulate(
        self, symbol: str, action: str, quantity: float, price: float
    ) -> dict:
        notional = quantity * price
        logger.info(
            f"[SIMULATION] {action} {quantity} {symbol} @ {price:.2f} | notional={notional:.2f} USDT"
        )
        return {
            "status": "simulated",
            "symbol": symbol,
            "action": action,
            "quantity": quantity,
            "price": price,
            "order_id": None,
            "reason": None,
        }

    async def _execute_live(
        self, symbol: str, action: str, quantity: float, price: float
    ) -> dict:
        side = SIDE_BUY if action == "BUY" else SIDE_SELL
        try:
            order = await self._place_order_with_retry(symbol, side, quantity)
            filled_price = float(order.get("fills", [{}])[0].get("price", price))
            order_id = str(order.get("orderId", ""))
            logger.info(
                f"[LIVE] أمر منفَّذ: id={order_id} side={action} qty={quantity} {symbol} filled@{filled_price:.2f}"
            )
            return {
                "status": "executed",
                "symbol": symbol,
                "action": action,
                "quantity": quantity,
                "price": filled_price,
                "order_id": order_id,
                "reason": None,
            }
        except Exception as e:
            logger.error(f"[LIVE] فشل تنفيذ {action} {symbol}: {e}")
            return {
                "status": "failed",
                "symbol": symbol,
                "action": action,
                "quantity": quantity,
                "price": price,
                "order_id": None,
                "reason": str(e),
            }

    async def _place_order_with_retry(
        self, symbol: str, side: str, quantity: float
    ) -> dict:
        client = self._get_client()
        last_error: Exception | None = None

        for attempt in range(1, settings.MAX_RETRIES + 1):
            try:
                order = await asyncio.to_thread(
                    client.create_order,
                    symbol=symbol,
                    side=side,
                    type=ORDER_TYPE_MARKET,
                    quantity=quantity,
                )
                return order
            except BinanceAPIException as e:
                # HTTP 400 يعني خطأ منطقي (رصيد غير كافٍ وغيره) — لا داعي لإعادة المحاولة
                if e.status_code == 400:
                    raise
                last_error = e
                logger.warning(f"محاولة {attempt}/{settings.MAX_RETRIES} فشلت: {e}")
            except BinanceRequestException as e:
                last_error = e
                logger.warning(f"محاولة {attempt}/{settings.MAX_RETRIES} — خطأ شبكة: {e}")

            if attempt < settings.MAX_RETRIES:
                await asyncio.sleep(settings.RETRY_DELAY)

        raise last_error or RuntimeError("فشلت جميع المحاولات")


executor = TradeExecutor()
