import asyncio
import logging
import json
import websockets
import base64
import time
import torch
from pathlib import Path
from typing import Dict, Optional, Set
from dataclasses import dataclass
from solana_rust_bot import TradingCore, TradingConfig

logging.basicConfig(
   level=logging.INFO, 
   format='%(asctime)s | %(levelname)s | %(message)s',
   handlers=[
       logging.FileHandler("trading.log", encoding='utf-8'),
       logging.StreamHandler()
   ]
)
logger = logging.getLogger(__name__)

@dataclass
class Position:
   token_mint: str
   pool_address: str
   entry_price: float
   size: float
   entry_time: float
   take_profit: float
   stop_loss: float
   attempts: int = 0
   max_attempts: int = 3

class ParallelTrader:
   def __init__(self, config_path: str):
       self.config = self._load_config(config_path)
       self.setup_gpu()
       self.setup_trading()
       
       self.RAYDIUM_V4 = self.config['programs']['raydium']
       self.WSOL_MINT = "So11111111111111111111111111111111111111112"
       
       # Parallel trading params
       self.position_size = 0.015
       self.max_positions = 5  # 5 parallel positions
       self.take_profit = 0.065  # 6.5%
       self.stop_loss = 0.0325  # 3.25%
       self.timeout = 10 # 10s max per trade
       
       # Risk params
       self.min_volume = 3500    # $5000 min volume
       self.max_impact = 0.0005  # 0.05% max impact
       
       # Track positions
       self.positions: Dict[str, Position] = {}
       self._active_tokens: Set[str] = set()
       self._position_lock = asyncio.Lock()
       
       # Performance tracking
       self.total_profit = 0.0
       self.wins = 0
       self.total_trades = 0
       self.start_time = time.time()

   def _load_config(self, path: str) -> dict:
       with open(Path(path)) as f:
           return json.load(f)

   def setup_gpu(self):
       if torch.cuda.is_available():
           self.device = torch.device("cuda")
           torch.backends.cudnn.benchmark = True
           self.price_tensor = torch.zeros(1000, device=self.device, dtype=torch.float32)
           logger.info(f"Using GPU: {torch.cuda.get_device_name()}")
       else:
           self.device = torch.device("cpu")
           logger.warning("GPU not available")

   def setup_trading(self):
       config = TradingConfig()
       config.helius_endpoint = self.config['rpc']['helius']['endpoint']
       config.helius_api_key = self.config['rpc']['helius']['api_key']
       config.priority_fee_type = "helius"
       config.compute_limit = str(self.config['execution']['compute_limit'])
       config.skip_preflight = "true"
       self.trading = TradingCore(config)

   async def analyze_market(self, pool_data: dict) -> dict:
       try:
           with torch.cuda.stream(torch.cuda.Stream()):
               self.price_tensor = torch.roll(self.price_tensor, -1)
               self.price_tensor[-1] = pool_data['price']
               
               last_5min = self.price_tensor[-300:]
               price_changes = last_5min[1:] - last_5min[:-1]
               
               buy_pressure = torch.sum(price_changes[price_changes > 0])
               sell_pressure = torch.abs(torch.sum(price_changes[price_changes < 0]))
               
               return {
                   'buy_pressure': float(buy_pressure),
                   'sell_pressure': float(sell_pressure),
                   'pressure_ratio': float(buy_pressure / max(sell_pressure, 1e-8))
               }
       except Exception as e:
           logger.error(f"Market analysis error: {e}")
           return {}

   async def should_enter_trade(self, pool_data: dict, analysis: dict) -> bool:
       try:
           if len(self.positions) >= self.max_positions:
               return False
               
           if pool_data['volume'] < self.min_volume:
               return False
               
           impact = (self.position_size * 1e9) / pool_data['base_reserve']
           if impact > self.max_impact:
               return False

           if analysis.get('pressure_ratio', 0) < 1.5:
               return False
               
           return True
       except Exception as e:
           logger.error(f"Entry validation error: {e}")
           return False

   async def execute_trade(self, pool_address: str, amount: float) -> Optional[str]:
       try:
           params = {
               "pool": pool_address,
               "amount": amount,
               "min_out": amount * 0.3  # 3.0% max slippage
           }
           return self.trading.execute_trade(params)
       except Exception as e:
           logger.error(f"Trade execution error: {e}")
           return None

   async def enter_position(self, pool_address: str, pool_data: dict) -> bool:
       try:
           analysis = await self.analyze_market(pool_data)
           if not await self.should_enter_trade(pool_data, analysis):
               return False

           signature = await self.execute_trade(pool_address, self.position_size)
           if signature:
               position = Position(
                   token_mint=pool_data.get('token_mint', pool_address),
                   pool_address=pool_address,
                   entry_price=pool_data['price'],
                   size=self.position_size,
                   entry_time=time.time(),
                   take_profit=pool_data['price'] * (1 + self.take_profit),
                   stop_loss=pool_data['price'] * (1 - self.stop_loss)
               )
               self.positions[pool_address] = position
               self._active_tokens.add(pool_address)
               logger.info(f"Entered {pool_address[:8]} at {pool_data['price']:.4f}")
               return True
           return False
       except Exception as e:
           logger.error(f"Entry error: {e}")
           return False

   async def exit_position(self, pool_address: str, current_price: float, reason: str):
       try:
           position = self.positions[pool_address]
           signature = await self.execute_trade(pool_address, position.size)
           
           if signature:
               pnl = (current_price - position.entry_price) * position.size
               self.total_profit += pnl
               self.total_trades += 1
               if pnl > 0:
                   self.wins += 1
               
               logger.info(f"""
               ✨ Exited {pool_address[:8]} ({reason}):
               PnL: {pnl:.6f} SOL (${pnl*110:.2f})
               Win Rate: {(self.wins/self.total_trades*100):.1f}%
               """)
               
               del self.positions[pool_address]
               self._active_tokens.remove(pool_address)
           else:
               position.attempts += 1
               if position.attempts >= position.max_attempts:
                   await self.emergency_exit(pool_address)
                   
       except Exception as e:
           logger.error(f"Exit error: {e}")

    async def emergency_exit(self, pool_address: str):
       try:
           position = self.positions[pool_address]
           signature = await self.execute_trade(
               pool_address, 
               position.size,
           )
           if signature:
               logger.warning(f"Emergency exit {pool_address[:8]}")
           self.positions.pop(pool_address, None)
           self._active_tokens.discard(pool_address)
       except Exception as e:
           logger.error(f"Emergency exit error: {e}")

    def decode_pool_data(self, log: str) -> Dict:
        try:
            if "ray_log: " not in log:
               return None
               
            ray_log = log.split("ray_log: ")[1].strip()
            raw_data = base64.b64decode(ray_log)
           
            if len(raw_data) < 32:
                return None

            base_offset = 208
            base_reserve = int.from_bytes(raw_data[base_offset:base_offset+8], 'little')
            quote_reserve = int.from_bytes(raw_data[base_offset+8:base_offset+16], 'little')
           
            SOL_DECIMALS = 1e9
            price = quote_reserve / base_reserve if base_reserve > 0 else 0
           
            return {
               'price': price,
               'base_reserve': base_reserve,
               'quote_reserve': quote_reserve,
               'liquidity': (base_reserve/SOL_DECIMALS) * 110,
               'timestamp': time.time()
           }
        except Exception as e:
            logger.error(f"Decode error: {str(e)}")
            return None
    async def process_transaction(self, tx_data: dict):
       try:
            logs = tx_data.get('result', {}).get('value', {}).get('logs', [])
           
           if any(self.RAYDIUM_V4 in log for log in logs):
               for log in logs:
                   if "Program log: " in log and self.RAYDIUM_V4 in log:
                       pool_data = self.decode_pool_data(log)
                       if pool_data and self.WSOL_MINT in log:
                           async with self._position_lock:
                               await self.update_positions(log, pool_data)
                               
       except Exception as e:
           logger.error(f"Transaction error: {e}")

   async def update_positions(self, log: str, pool_data: dict):
       try:
           current_price = pool_data['price']
           analysis = await self.analyze_market(pool_data)
           
           # Check exits
           for addr, pos in list(self.positions.items()):
               profit_pct = (current_price - pos.entry_price) / pos.entry_price
               time_in_trade = time.time() - pos.entry_time
               
               should_exit = (
                   profit_pct >= self.take_profit or
                   profit_pct <= -self.stop_loss or
                   time_in_trade > self.timeout or
                   analysis.get('pressure_ratio', 0) < 0.7
               )
               
               if should_exit:
                   await self.exit_position(addr, current_price,
                       "take_profit" if profit_pct >= self.take_profit else
                       "stop_loss" if profit_pct <= -self.stop_loss else
                       "timeout" if time_in_trade > self.timeout else
                       "momentum_shift"
                   )
                   
           # Look for entries
           if self.WSOL_MINT in log:
               await self.enter_position(self.WSOL_MINT, pool_data)
               
       except Exception as e:
           logger.error(f"Position update error: {e}")

   async def run(self):
       ws_url = self.config['rpc']['helius']['endpoint'].replace('https', 'wss')
       
       logger.info(f"""
       🚀 Starting Parallel Trader:
       Max Positions: {self.max_positions}
       Position Size: {self.position_size} SOL
       Take Profit: {self.take_profit*100}%
       Stop Loss: {self.stop_loss*100}%
       GPU: {torch.cuda.get_device_name() if torch.cuda.is_available() else "CPU"}
       """)

       while True:
           try:
               async with websockets.connect(ws_url) as ws:
                   subscribe_msg = {
                       "jsonrpc": "2.0",
                       "id": 1,
                       "method": "logsSubscribe",
                       "params": [
                           {"mentions": [self.RAYDIUM_V4]},
                           {"commitment": "processed"}
                       ]
                   }
                   
                   await ws.send(json.dumps(subscribe_msg))
                   logger.info("Connected to Raydium mempool")
                   
                   while True:
                       msg = await ws.recv()
                       await self.process_transaction(json.loads(msg))

           except Exception as e:
               logger.error(f"Connection error: {e}")
               await asyncio.sleep(1)

if __name__ == "__main__":
   try:
       trader = ParallelTrader("config.json")
       asyncio.run(trader.run())
   except KeyboardInterrupt:
       logger.info("Trader stopped")
   except Exception as e:
       logger.error(f"Fatal error: {e}") please write this script with professional grade logging please thank you