"""
Pair Scanner for Multi-Pair Divergence Strategy.

Generates all possible pairs from asset universe and checks tradeability.
"""
from dataclasses import dataclass
from itertools import combinations
from typing import Optional

import ccxt

from engine.logging_config import get_logger
from .config import MultiPairConfig

logger = get_logger(__name__)


@dataclass
class TradingPair:
    """
    Represents a tradeable pair for spread analysis.
    
    Attributes:
        base_asset: First asset in the pair (numerator)
        quote_asset: Second asset in the pair (denominator)
        pair_id: Unique identifier for the pair
        is_direct: Whether pair can be traded directly on exchange
        exchange_symbol: Symbol for direct trading (if available)
    """
    base_asset: str
    quote_asset: str
    pair_id: str
    is_direct: bool = False
    exchange_symbol: Optional[str] = None
    
    @property
    def name(self) -> str:
        """Human-readable pair name."""
        return f"{self.base_asset}/{self.quote_asset}"
    
    def __hash__(self):
        return hash(self.pair_id)
    
    def __eq__(self, other):
        if not isinstance(other, TradingPair):
            return False
        return self.pair_id == other.pair_id


class PairScanner:
    """
    Scans and generates tradeable pairs from asset universe.
    
    Checks OKX for directly tradeable cross-pairs and generates
    synthetic pairs via USDT for others.
    """
    
    def __init__(self, config: MultiPairConfig):
        self.config = config
        self.exchange: Optional[ccxt.Exchange] = None
        self._available_markets: set[str] = set()
    
    def _init_exchange(self) -> None:
        """Initialize exchange connection for market lookup."""
        if self.exchange is None:
            exchange_class = getattr(ccxt, self.config.exchange_id)
            self.exchange = exchange_class({'enableRateLimit': True})
            self.exchange.load_markets()
            self._available_markets = set(self.exchange.symbols)
            logger.info(
                "Loaded %d markets from %s",
                len(self._available_markets), 
                self.config.exchange_id
            )
    
    def generate_pairs(self, check_exchange: bool = True) -> list[TradingPair]:
        """
        Generate all unique pairs from asset universe.
        
        Args:
            check_exchange: Whether to check OKX for direct trading
            
        Returns:
            List of TradingPair objects
        """
        if check_exchange:
            self._init_exchange()
        
        pairs = []
        assets = self.config.assets
        
        for base, quote in combinations(assets, 2):
            pair_id = f"{base}__{quote}"
            
            # Check if directly tradeable as cross-pair on OKX
            is_direct = False
            exchange_symbol = None
            
            if check_exchange:
                # Check perpetual cross-pair (e.g., ETH/BTC:BTC)
                # OKX perpetuals are typically quoted in USDT
                # Cross-pairs like ETH/BTC are less common
                cross_symbol = f"{base.replace('-USDT', '')}/{quote.replace('-USDT', '')}:USDT"
                if cross_symbol in self._available_markets:
                    is_direct = True
                    exchange_symbol = cross_symbol
            
            pair = TradingPair(
                base_asset=base,
                quote_asset=quote,
                pair_id=pair_id,
                is_direct=is_direct,
                exchange_symbol=exchange_symbol
            )
            pairs.append(pair)
        
        # Log summary
        direct_count = sum(1 for p in pairs if p.is_direct)
        logger.info(
            "Generated %d pairs: %d direct, %d synthetic",
            len(pairs), direct_count, len(pairs) - direct_count
        )
        
        return pairs
    
    def get_required_symbols(self, pairs: list[TradingPair]) -> list[str]:
        """
        Get list of symbols needed to calculate all pair spreads.
        
        For synthetic pairs, we need both USDT pairs.
        For direct pairs, we still load USDT pairs for simplicity.
        
        Args:
            pairs: List of trading pairs
            
        Returns:
            List of unique symbols to load (e.g., ['BTC-USDT', 'ETH-USDT'])
        """
        symbols = set()
        for pair in pairs:
            symbols.add(pair.base_asset)
            symbols.add(pair.quote_asset)
        return list(symbols)
    
    def filter_by_assets(
        self, 
        pairs: list[TradingPair], 
        exclude_assets: list[str]
    ) -> list[TradingPair]:
        """
        Filter pairs that contain any of the excluded assets.
        
        Args:
            pairs: List of trading pairs
            exclude_assets: Assets to exclude
            
        Returns:
            Filtered list of pairs
        """
        if not exclude_assets:
            return pairs
        
        exclude_set = set(exclude_assets)
        return [
            p for p in pairs
            if p.base_asset not in exclude_set 
            and p.quote_asset not in exclude_set
        ]
