import ccxt
import logging
from typing import Optional
from app.config import settings

logger = logging.getLogger(__name__)


class BinanceClient:
    """Wrapper around CCXT Binance Spot exchange."""

    def __init__(self):
        self._exchange: Optional[ccxt.binance] = None

    def get_exchange(self) -> ccxt.binance:
        """Returns a singleton CCXT Binance exchange instance."""
        if self._exchange is None:
            self._exchange = ccxt.binance({
                "apiKey": settings.BINANCE_API_KEY,
                "secret": settings.BINANCE_SECRET_KEY,
                "enableRateLimit": True,
                "options": {
                    "defaultType": "spot",
                },
            })
        return self._exchange

    def get_readonly_exchange(self) -> ccxt.binance:
        """Returns an exchange instance without API keys (for public endpoints)."""
        return ccxt.binance({
            "enableRateLimit": True,
            "options": {
                "defaultType": "spot",
            },
        })

    async def fetch_ticker(self, symbol: str) -> dict:
        """Fetch the latest ticker for a trading pair."""
        exchange = self.get_readonly_exchange()
        try:
            ticker = exchange.fetch_ticker(symbol)
            return ticker
        except ccxt.BaseError as e:
            logger.error(f"Error fetching ticker for {symbol}: {e}")
            raise

    async def fetch_balance(self) -> dict:
        """Fetch account balance (requires API keys)."""
        exchange = self.get_exchange()
        try:
            balance = exchange.fetch_balance()
            return balance
        except ccxt.BaseError as e:
            logger.error(f"Error fetching balance: {e}")
            raise

    async def create_market_order(
        self, symbol: str, side: str, amount: float
    ) -> dict:
        """Place a market order on Binance Spot."""
        exchange = self.get_exchange()
        try:
            order = exchange.create_market_order(symbol, side, amount)
            logger.info(f"Market order placed: {side} {amount} {symbol} -> {order}")
            return order
        except ccxt.BaseError as e:
            logger.error(f"Error placing market order {side} {symbol}: {e}")
            raise


binance_client = BinanceClient()
