import asyncio
import websockets
import json
import base64
import logging
import signal
import time
from solana_rust_bot import TradingCore
from collections import defaultdict
import torch
import numpy as np
from typing import Dict, Optional
import aiohttp
from heapq import nlargest

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s.%(msecs)03d | %(levelname)s | %(message)s',
    datefmt='%H:%M:%S'
)

class TokenMetrics:
    def __init__(self):
        self.buyer_percent = 0.0
        self.volume_5min = 0.0
        self.last_update = time.time()
        self.price = 0.0
        self.liquidity = 0.0
        self.scalp_count = 0
        self.last_trade_time = 0.0

class TradeMonitor:
    def __init__(self):
        self.total_trades = 0
        self.winning_trades = 0
        self.total_profit = 0.0
        self.token_metrics = {}
        
    def update(self, token: str, profit: float, success: bool):
        self.total_trades += 1
        if success:
            self.winning_trades += 1
        self.total_profit += profit
        
        if self.total_trades % 10 == 0:
            self.print_stats()
    
    def print_stats(self):
        if self.total_trades == 0:
            return
            
        winrate = (self.winning_trades / self.total_trades) * 100
        print("\n" + "="*50)
        print("🎯 PERFORMANCE SUMMARY:")
        print(f"💫 Total Trades: {self.total_trades}")
        print(f"✅ Winning Trades: {self.winning_trades}")
        print(f"📊 Win Rate: {winrate:.2f}%")
        print(f"💰 Total Profit: {self.total_profit:.2f} SOL")
        print("="*50 + "\n")

class QuantumScalper:
    def __init__(self):
        logging.info("\n🚀 INITIALIZING QUANTUM SCALPER...")
        
        self.load_config()
        self.core = TradingCore(json.dumps(self.config))
        self.monitor = TradeMonitor()
        
        # Updated WebSocket setup
        self.ws_url = "wss://marga-e56m84-fast-mainnet.helius-rpc.com/"
        self.helius_api_key = self.config['helius']['api_key']
        self.ws_headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {self.helius_api_key}"
        }
        
        # Trading state
        self.token_data = defaultdict(TokenMetrics)
        self.active_trades = {}
        self.top_tokens = set()
        self.running = True
        self.last_trade_time = 0
        
        # Performance tracking
        self.successful_trades = 0
        self.total_trades = 0
        
        # WebSocket setup
        self.ws = None
        self.setup_complete = False
        
        logging.info("✨ Initialization complete!")

    def load_config(self):
        with open("config.json") as f:
            self.config = json.load(f)
            
        # Trading parameters
        self.PARAMS = {
            'max_scalps_per_token': 4,
            'position_size': self.config['trading']['position_size'],
            'take_profit': self.config['trading']['take_profit'],
            'stop_loss': self.config['trading']['stop_loss'],
            'max_trade_time': self.config['trading']['max_trade_time'],
            'min_volume': self.config['trading']['min_volume'],
            'min_liquidity': self.config['trading']['min_liquidity'],
            'trades_per_second': self.config['trading']['trades_per_second']
        }
        
        # Constants
        self.RAYDIUM_PROGRAM = self.config['raydium']['program_id']
        self.WS_URL = self.config['helius']['mainnet_url']
        
        logging.info("📊 Configuration loaded")
        self._log_parameters()

    def _log_parameters(self):
        logging.info("\n=== TRADING PARAMETERS ===")
        logging.info(f"💰 Position Size: {self.PARAMS['position_size']} SOL")
        logging.info(f"📈 Take Profit: {self.PARAMS['take_profit']}%")
        logging.info(f"🛑 Stop Loss: {self.PARAMS['stop_loss']}%")
        logging.info(f"⏱️ Max Trade Time: {self.PARAMS['max_trade_time']}s")
        logging.info(f"📊 Min Volume: {self.PARAMS['min_volume']} SOL")
        logging.info("=" * 25)

    def decode_pool_data(self, data: str) -> Optional[dict]:
        try:
            raw = base64.b64decode(data)
            
            # Extract pool data
            base_reserve = int.from_bytes(raw[344:352], 'little')
            quote_reserve = int.from_bytes(raw[352:360], 'little')
            base_target = int.from_bytes(raw[336:344], 'little')
            quote_target = int.from_bytes(raw[328:336], 'little')
            
            # Convert to SOL units
            base_reserve = base_reserve / 1e9
            quote_reserve = quote_reserve / 1e9
            base_target = base_target / 1e9
            quote_target = quote_target / 1e9
            
            # Calculate metrics
            total_volume = abs(base_target - base_reserve) + abs(quote_target - quote_reserve)
            buyer_volume = abs(quote_target - quote_reserve)
            
            buyer_percent = (buyer_volume / total_volume * 100) if total_volume > 0 else 0
            price = quote_reserve / base_reserve if base_reserve > 0 else 0
            
            return {
                "volume": total_volume,
                "buyer_percent": buyer_percent,
                "liquidity": quote_reserve * 2,
                "price": price
            }
            
        except Exception as e:
            logging.error(f"Pool decoding error: {str(e)}")
            return None

    def should_trade(self, token: str, metrics: dict) -> bool:
        if len(self.active_trades) >= 10:  # Max concurrent trades
            return False
            
        if token in self.active_trades:
            return False
            
        current_time = time.time()
        if current_time - self.last_trade_time < (1.0 / self.PARAMS['trades_per_second']):
            return False
            
        # Check token metrics
        token_data = self.token_data[token]
        if token_data.scalp_count >= self.PARAMS['max_scalps_per_token']:
            return False
            
        # Validate metrics
        return all([
            metrics['volume'] >= self.PARAMS['min_volume'],
            metrics['buyer_percent'] >= 52.0,  # Min buyer percentage
            metrics['liquidity'] >= self.PARAMS['min_liquidity'],
            token in self.top_tokens
        ])

    async def execute_buy(self, token: str, metrics: dict):
        """Execute buy order with async handling"""
        try:
            if token in self.active_trades:
                return False
                
            logging.info(f"\n🚀 EXECUTING BUY:")
            logging.info(f"Token: {token}")
            logging.info(f"Size: {self.PARAMS['position_size']} SOL")
            logging.info(f"Price: {metrics['price']:.8f}")
            
            # Convert the buy_token call to coroutine if it isn't already
            if asyncio.iscoroutinefunction(self.core.buy_token):
                result = await self.core.buy_token(token, self.PARAMS['position_size'])
            else:
                # If it's not async, run in executor
                result = await asyncio.get_event_loop().run_in_executor(
                    None,
                    self.core.buy_token,
                    token,
                    self.PARAMS['position_size']
                )
            
            if result:
                self.active_trades[token] = {
                    'entry_price': metrics['price'],
                    'entry_time': time.time(),
                    'size': self.PARAMS['position_size']
                }
                self.last_trade_time = time.time()
                self.token_data[token].scalp_count += 1
                logging.info("✅ Buy order successful!")
                return True
                
            return False
            
        except Exception as e:
            logging.error(f"Buy execution error: {str(e)}")
            return False

    async def get_price(self, token: str) -> Optional[float]:
        for retry in range(3):  # Try up to 3 times
            try:
                # Try primary price source
                if asyncio.iscoroutinefunction(self.core.get_price):
                    price_data = await self.core.get_price(token)
                else:
                    price_data = await asyncio.get_event_loop().run_in_executor(
                        None, 
                        self.core.get_price,
                       token
                    )
            
                # Handle different price data formats
                if isinstance(price_data, (int, float)):
                    price = float(price_data)
                elif isinstance(price_data, dict) and 'price' in price_data:
                    price = float(price_data['price'])
                else:
                    continue
                
                # Validate price
                if price > 0:
                    return price
                
                await asyncio.sleep(0.5 * (retry + 1))  # Exponential backoff
            
            except Exception:
                await asyncio.sleep(0.5 * (retry + 1))
                continue
            
        # Use cached price as fallback
        if token in self.token_data and self.token_data[token].price > 0:
            return self.token_data[token].price
        
        return None
    async def check_exit_conditions(self):
        """Monitor trades with improved price validation"""
        current_time = time.time()
        
        for token in list(self.active_trades.keys()):
            try:
                trade = self.active_trades[token]
                current_price = await self.get_price(token)
                
                if current_price is None:
                    logging.warning(f"Unable to get current price for {token}, skipping exit check")
                    continue
                
                if current_price <= 0:
                    logging.warning(f"Invalid price (0 or negative) for {token}, skipping exit check")
                    continue
                
                profit_pct = ((current_price - trade['entry_price']) / trade['entry_price']) * 100
                time_in_trade = current_time - trade['entry_time']
                
                logging.info(f"\n📊 Trade Status for {token}:")
                logging.info(f"  • Entry: {trade['entry_price']:.8f}")
                logging.info(f"  • Current: {current_price:.8f}")
                logging.info(f"  • P&L: {profit_pct:.2f}%")
                logging.info(f"  • Time: {time_in_trade:.1f}s")
                
                # Add minimum trade time before allowing stop loss
                min_trade_time = 5  # 5 seconds minimum
                
                # Check exit conditions with validation
                if profit_pct >= self.PARAMS['take_profit']:
                    await self.close_trade(token, "TAKE PROFIT", profit_pct)
                elif profit_pct <= -self.PARAMS['stop_loss'] and time_in_trade >= min_trade_time:
                    await self.close_trade(token, "STOP LOSS", profit_pct)
                elif time_in_trade >= self.PARAMS['max_trade_time']:
                    await self.close_trade(token, "TIME EXIT", profit_pct)
                    
            except Exception as e:
                logging.error(f"Exit check error for {token}: {str(e)}")
                continue

    async def close_trade(self, token: str, reason: str, profit_pct: float):
        """Close trade with additional validation"""
        try:
            if token not in self.active_trades:
                return
                
            trade = self.active_trades[token]
            
            # Validate profit calculation
            if abs(profit_pct) > 100:
                logging.warning(f"Suspicious P&L ({profit_pct}%) for {token}, validating price...")
                current_price = await self.get_price(token)
                if current_price and current_price > 0:
                    profit_pct = ((current_price - trade['entry_price']) / trade['entry_price']) * 100
            
            logging.info(f"\n🔄 CLOSING TRADE:")
            logging.info(f"Token: {token}")
            logging.info(f"Reason: {reason}")
            logging.info(f"P&L: {profit_pct:.2f}%")
            
            if asyncio.iscoroutinefunction(self.core.sell_token):
                result = await self.core.sell_token(token, trade['size'])
            else:
                result = await asyncio.get_event_loop().run_in_executor(
                    None,
                    self.core.sell_token,
                    token,
                    trade['size']
                )
            
            if result:
                self.monitor.update(token, profit_pct, profit_pct > 0)
                del self.active_trades[token]
                
                if profit_pct > 0:
                    self.successful_trades += 1
                    
                if self.successful_trades % 5 == 0:
                    new_size = min(self.PARAMS['position_size'] + 0.002, 0.05)  # Cap at 0.05 SOL
                    self.PARAMS['position_size'] = new_size
                    logging.info(f"📈 Position size increased to {self.PARAMS['position_size']:.3f} SOL")
                    
        except Exception as e:
            logging.error(f"Trade closing error: {str(e)}")
    async def process_pool_data(self, account_data: str, token: str) -> Optional[dict]:
        """Process pool data with improved error handling"""
        try:
            decoded_data = base64.b64decode(account_data)
            
            with self.processing_lock:
                reserves = torch.tensor([
                    int.from_bytes(decoded_data[329:337], 'little'),
                    int.from_bytes(decoded_data[337:345], 'little'),
                ], dtype=torch.float32, device='cpu')  # Use CPU for initial processing
                
                targets = torch.tensor([
                    int.from_bytes(decoded_data[345:353], 'little'),
                    int.from_bytes(decoded_data[353:361], 'little'),
                ], dtype=torch.float32, device='cpu')
                
                price = await self.get_price(token)
                
                if not price:
                    return None
                    
                base_values = reserves[0] * price
                quote_values = reserves[1]
                
                total_volume = torch.abs(targets - reserves).sum().item() / 1e9
                buyer_percent = (torch.abs(targets[1] - reserves[1]).item() / (total_volume * 1e9)) * 100 if total_volume > 0 else 0
                
                return {
                    'buyer_percent': buyer_percent,
                    'volume_5min': total_volume * 12,  # Extrapolate to 5 minutes
                    'liquidity': (quote_values / 1e9).item() * 2,
                    'price': price,
                    'is_buy': buyer_percent > 52.0
                }
                
        except Exception as e:
            logging.error(f"Pool data processing error: {str(e)}")
            return None

    async def update_top_tokens(self):
        """Update the set of top 25 tokens based on metrics"""
        try:
            # Sort tokens by buyer percentage and volume
            sorted_tokens = sorted(
                self.token_data.items(),
                key=lambda x: (x[1].buyer_percent * x[1].volume_5min, x[1].volume_5min),
                reverse=True
            )
            
            # Update top tokens set
            self.top_tokens = set(token for token, _ in sorted_tokens[:25])
            
        except Exception as e:
            logging.error(f"Error updating top tokens: {str(e)}")

    async def start_websocket(self):
        retry_count = 0
        max_retries = 5
        
        while self.running and retry_count < max_retries:
            try:
                logging.info("\n🔌 Connecting to Helius WebSocket...")
                
                async with websockets.connect(
                    self.ws_url,
                    extra_headers=self.ws_headers,
                    ping_interval=30,
                    ping_timeout=10,
                    close_timeout=10
                ) as websocket:
                    self.ws = websocket
                    logging.info("✅ Connected!")
                    
                    # Updated subscription message
                    subscription = {
                        "jsonrpc": "2.0",
                        "id": "quantum-scalper",
                        "method": "programSubscribe",
                        "params": [
                            self.RAYDIUM_PROGRAM,
                            {"encoding": "base64", "commitment": "processed"}
                        ]
                    }
                    
                    await websocket.send(json.dumps(subscription))
                    logging.info("📡 Subscription active!")
                    
                    # Process messages
                    while self.running:
                        try:
                            message = await asyncio.wait_for(websocket.recv(), timeout=60)
                            data = json.loads(message)
                            
                            if "params" in data and "result" in data["params"]:
                                result = data["params"]["result"]
                                if "value" in result:
                                    value = result["value"]
                                    if "account" in value and "data" in value["account"]:
                                        # Process pool data
                                        pool_data = self.decode_pool_data(
                                            value["account"]["data"][0]
                                        )
                                        
                                        if pool_data:
                                            token = value["pubkey"]
                                            metrics = pool_data
                                            
                                            # Update token data
                                            self.token_data[token].buyer_percent = metrics['buyer_percent']
                                            self.token_data[token].volume_5min = metrics['volume']
                                            self.token_data[token].liquidity = metrics['liquidity']
                                            self.token_data[token].price = metrics['price']
                                            
                                            # Update top tokens list
                                            await self.update_top_tokens()
                                            
                                            # Check if we should trade
                                            if self.should_trade(token, metrics):
                                                await self.execute_buy(token, metrics)
                                                
                            # Check exit conditions periodically
                            await self.check_exit_conditions()
                            
                        except asyncio.TimeoutError:
                            continue
                        except Exception as e:
                            logging.error(f"Message processing error: {str(e)}")
                            continue
                            
            except Exception as e:
                retry_count += 1
                logging.error(f"WebSocket error (attempt {retry_count}/{max_retries}): {str(e)}")
                if retry_count < max_retries and self.running:
                    await asyncio.sleep(5)
                continue

    async def cleanup(self):
        """Cleanup resources"""
        logging.info("\n🧹 Starting cleanup...")
        self.running = False
        
        # Close active trades
        for token in list(self.active_trades.keys()):
            logging.info(f"Closing trade: {token}")
            await self.close_trade(token, "SHUTDOWN", 0.0)
            
        if self.ws and not self.ws.closed:
            await self.ws.close()
            
        logging.info("✅ Cleanup complete")

async def main():
    trader = None
    try:
        # Initialize trader
        trader = QuantumScalper()
        
        def handle_shutdown(sig, frame):
            if trader:
                logging.info("\n⚡ Shutdown signal received")
                asyncio.create_task(trader.cleanup())
                
        signal.signal(signal.SIGINT, handle_shutdown)
        signal.signal(signal.SIGTERM, handle_shutdown)
        
        # Start trading
        await trader.start_websocket()
        
    except KeyboardInterrupt:
        logging.info("\n💫 Shutting down gracefully...")
    except Exception as e:
        logging.error(f"Fatal error: {str(e)}")
    finally:
        if trader:
            await trader.cleanup()

if __name__ == "__main__":
    try:
        # Set process priority
        import psutil
        p = psutil.Process()
        p.nice(psutil.HIGH_PRIORITY_CLASS)
        
        # Configure additional logging
        logging.getLogger('websockets').setLevel(logging.WARNING)
        logging.getLogger('asyncio').setLevel(logging.WARNING)
        
        # Initialize and run event loop with error handling
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        
        try:
            loop.run_until_complete(main())
        except KeyboardInterrupt:
            pass
        finally:
            # Clean up pending tasks
            pending = asyncio.all_tasks(loop)
            loop.run_until_complete(asyncio.gather(*pending))
            loop.close()
            
    except Exception as e:
        logging.error(f"Startup error: {str(e)}")
        
    finally:
        logging.info("Bot shutdown complete") please add the most best most extreme debug logging and logging tracing throughout the entire script please write all of it dont lessen or weaken or shorten my script just add the debugging and logging to it thank you.