"""Abstract base class for exchange adapters."""

from abc import ABC, abstractmethod
from typing import Optional, List, Dict, Any
from dataclasses import dataclass


@dataclass
class OHLCV:
    """OHLCV candle data."""
    timestamp: float
    open: float
    high: float
    low: float
    close: float
    volume: float


@dataclass
class Order:
    """Order representation."""
    order_id: str
    symbol: str
    side: str  # 'buy' or 'sell'
    type: str  # 'market', 'limit', etc.
    amount: float
    price: Optional[float] = None
    filled_amount: float = 0.0
    filled_price: float = 0.0
    status: str = "open"
    timestamp: float = 0


@dataclass
class Balance:
    """Account balance."""
    currency: str
    total: float
    available: float
    used: float = 0.0


@dataclass
class Ticker:
    """Current ticker data."""
    symbol: str
    bid: float
    ask: float
    last: float
    high_24h: float
    low_24h: float
    volume_24h: float
    timestamp: float


class ExchangeAdapter(ABC):
    """Abstract base class for exchange adapters.
    
    All exchange implementations must implement these methods.
    This provides a unified interface for the bot regardless of exchange.
    """
    
    def __init__(self, config: Dict[str, Any]):
        """Initialize adapter with configuration.
        
        Args:
            config: Exchange configuration including API credentials
        """
        self.config = config
        self.name = config.get('name', 'unknown')
        self._connected = False
    
    @abstractmethod
    async def connect(self) -> bool:
        """Establish connection to exchange.
        
        Returns:
            True if connection successful
        """
        pass
    
    @abstractmethod
    async def disconnect(self) -> None:
        """Close connection to exchange."""
        pass
    
    @abstractmethod
    async def fetch_ohlcv(
        self,
        symbol: str,
        timeframe: str = "1m",
        limit: int = 100
    ) -> List[OHLCV]:
        """Fetch OHLCV (candlestick) data.
        
        Args:
            symbol: Trading pair (e.g., 'XBT/USD')
            timeframe: Candle timeframe (e.g., '1m', '5m', '1h', '1d')
            limit: Number of candles to fetch
            
        Returns:
            List of OHLCV data points
        """
        pass
    
    @abstractmethod
    async def fetch_ticker(self, symbol: str) -> Ticker:
        """Fetch current ticker data.
        
        Args:
            symbol: Trading pair
            
        Returns:
            Current ticker information
        """
        pass
    
    @abstractmethod
    async def fetch_balance(self, currency: Optional[str] = None) -> List[Balance]:
        """Fetch account balances.
        
        Args:
            currency: Optional specific currency to fetch
            
        Returns:
            List of balances
        """
        pass
    
    @abstractmethod
    async def create_order(
        self,
        symbol: str,
        side: str,
        order_type: str,
        amount: float,
        price: Optional[float] = None
    ) -> Order:
        """Create a new order.
        
        Args:
            symbol: Trading pair
            side: 'buy' or 'sell'
            order_type: 'market', 'limit', etc.
            amount: Amount to buy/sell
            price: Limit price (for limit orders)
            
        Returns:
            Created order
        """
        pass
    
    @abstractmethod
    async def cancel_order(self, order_id: str) -> bool:
        """Cancel an open order.
        
        Args:
            order_id: Order ID to cancel
            
        Returns:
            True if order cancelled successfully
        """
        pass
    
    @abstractmethod
    async def get_order(self, order_id: str) -> Optional[Order]:
        """Get order status.
        
        Args:
            order_id: Order ID to fetch
            
        Returns:
            Order if found, None otherwise
        """
        pass
    
    @abstractmethod
    async def get_open_orders(self, symbol: Optional[str] = None) -> List[Order]:
        """Get all open orders.
        
        Args:
            symbol: Optional symbol filter
            
        Returns:
            List of open orders
        """
        pass
    
    @abstractmethod
    async def fetch_trades(
        self,
        symbol: str,
        limit: int = 100
    ) -> List[Dict[str, Any]]:
        """Fetch recent trades (fill history).
        
        Args:
            symbol: Trading pair
            limit: Number of trades to fetch
            
        Returns:
            List of trade records
        """
        pass
    
    async def test_connection(self) -> bool:
        """Test exchange connection.
        
        Returns:
            True if connection and authentication work
        """
        try:
            await self.connect()
            # Try a simple authenticated request
            balances = await self.fetch_balance()
            return len(balances) > 0
        except Exception as e:
            print(f"Connection test failed: {e}")
            return False
    
    @property
    def is_connected(self) -> bool:
        """Check if adapter is connected."""
        return self._connected
