# raydium_monitor.py
import asyncio
import logging
import json
import websockets
import base64
import time
import traceback
from typing import Dict, Optional, List

logger = logging.getLogger(__name__)

class RaydiumMonitor:
    def __init__(self, config: dict, trading_core):
        self.config = config
        self.trading = trading_core
        self._shutdown = False
        
        # Raydium Program IDs
        self.RAYDIUM_V4_PROGRAM = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"
        self.WSOL_MINT = "So11111111111111111111111111111111111111112"
        
        # Primary SOL/WSOL pools
        self.SOL_POOLS = [
            "58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2",  # Main SOL-WSOL
            "EGZ7tiLeH62TPV1gL8WwbXGzEPa9zmcpVnnkPKKnrE2U"   # Secondary SOL-WSOL
        ]
        
        # State tracking
        self.active_pools = {}
        self.last_update = {}
        self.tps_counter = 0
        self.last_tps_reset = time.time()

    def decode_pool_data(self, log_data: str) -> Optional[Dict]:
        """Decode Raydium pool update data"""
        try:
            # Clean and decode base64 log
            clean_log = log_data.replace("Program log: ", "")
            raw_data = base64.b64decode(clean_log)
            
            if len(raw_data) < 32:
                return None
                
            # Extract pool metrics
            base_offset = 8  # Skip discriminator
            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')
            
            # Convert to SOL units
            SOL_DECIMALS = 10**9
            base_sol = base_reserve / SOL_DECIMALS
            quote_sol = quote_reserve / SOL_DECIMALS
            
            # Validate pool size
            if base_sol > 10_000 or quote_sol > 10_000:  
                logger.debug(f"Pool too large: {base_sol:.2f} SOL, {quote_sol:.2f} WSOL")
                return None
                
            if base_sol < 10 or quote_sol < 10:
                logger.debug(f"Pool too small: {base_sol:.2f} SOL, {quote_sol:.2f} WSOL")
                return None
            
            return {
                'base_reserve': base_reserve,
                'quote_reserve': quote_reserve,
                'base_sol': base_sol,
                'quote_sol': quote_sol,
                'timestamp': time.time()
            }
            
        except Exception as e:
            logger.error(f"Pool decode error: {str(e)}")
            return None

    async def process_transaction(self, tx_data: dict):
        """Process incoming transaction data"""
        try:
            # Extract transaction data
            tx_result = tx_data.get("result", {})
            if not tx_result:
                return

            # Check for Raydium logs
            logs = tx_result.get('meta', {}).get('logMessages', [])
            if not any(self.RAYDIUM_V4_PROGRAM in log for log in logs):
                return

            signature = tx_result.get('transaction', {}).get('signatures', ['unknown'])[0]
            logger.info(f"Processing Raydium tx: {signature[:8]}...")
            
            for log in logs:
                if "Program log: " in log and self.RAYDIUM_V4_PROGRAM in log:
                    pool_data = self.decode_pool_data(log)
                    if pool_data:
                        # Get pool address from logs
                        pool_address = None
                        for acc_log in logs:
                            if any(pool in acc_log for pool in self.SOL_POOLS):
                                pool_address = next((p for p in self.SOL_POOLS if p in acc_log), None)
                                break
                        
                        if pool_address:
                            pool_data['token_mint'] = pool_address
                            pool_data['last_update'] = time.time()
                            
                            # Calculate metrics
                            base_usd = pool_data['base_sol'] * 110  # Approx SOL price
                            logger.info(f"Pool Update: {pool_address[:8]}")
                            logger.info(f"Liquidity: ${base_usd:,.2f}")
                            logger.info(f"Base: {pool_data['base_sol']:.2f} SOL")
                            logger.info(f"Quote: {pool_data['quote_sol']:.2f} WSOL")
                            
                            self.active_pools[pool_address] = pool_data

        except Exception as e:
            logger.error(f"Process error: {str(e)}")
            logger.debug(f"Error details: {traceback.format_exc()}")

    async def start_monitoring(self):
        """Start monitoring Raydium pools"""
        logger.info("Starting Raydium pool monitor...")
        
        # Get websocket URL from Helius endpoint
        ws_url = self.config['rpc']['primary']['endpoint'].replace('https', 'wss')
        logger.info(f"Connecting to websocket: {ws_url}")
        
        retry_count = 0
        
        while not self._shutdown:
            try:
                async with websockets.connect(ws_url) as ws:
                    # Subscribe to program logs
                    subscribe_msg = {
                        "jsonrpc": "2.0",
                        "id": 1,
                        "method": "logsSubscribe",
                        "params": [
                            {"mentions": [self.RAYDIUM_V4_PROGRAM]},
                            {"commitment": "processed"}
                        ]
                    }
                    
                    logger.info("Sending subscription request...")
                    await ws.send(json.dumps(subscribe_msg))
                    response = await ws.recv()
                    response_data = json.loads(response)
                    
                    if "error" in response_data:
                        raise Exception(f"Subscription error: {response}")
                    
                    logger.info("Connected to Helius websocket")
                    logger.info("Monitoring Raydium pools:")
                    for pool in self.SOL_POOLS:
                        logger.info(f"- {pool}")
                    
                    retry_count = 0
                    
                    while not self._shutdown:
                        msg = await ws.recv()
                        msg_data = json.loads(msg)
                        
                        # Debug log raw message
                        logger.debug(f"Raw message: {json.dumps(msg_data, indent=2)}")
                        
                        # Process transaction
                        await self.process_transaction(msg_data)
                        
            except Exception as e:
                retry_count += 1
                wait_time = min(2 ** retry_count, 60)
                logger.error(f"Websocket error: {str(e)}\n{traceback.format_exc()}")
                
                if not self._shutdown:
                    logger.info(f"Retrying in {wait_time} seconds...")
                    await asyncio.sleep(wait_time)
    async def process_transaction(self, tx_data: dict):
        """Process incoming transaction data"""
        try:
             # Extract transaction data
            tx_result = tx_data.get("result", {})
            if not tx_result:
                logger.debug("No result in transaction data")
                return
                
            # Check for Raydium logs
            logs = tx_result.get('meta', {}).get('logMessages', [])
            if not logs:
                logger.debug("No logs in transaction")
                return
                
            if any(self.RAYDIUM_V4_PROGRAM in log for log in logs):
                signature = tx_result.get('transaction', {}).get('signatures', ['unknown'])[0]
                logger.info(f"Found Raydium transaction: {signature[:8]}...")
                    
                    # Log all program mentions for debugging
                    programs = set()
                    for log in logs:
                        if "Program " in log:
                            program = log.split("Program ")[1].split()[0]
                            programs.add(program)
                    logger.debug(f"Programs in transaction: {list(programs)}")
                    
                    for log in logs:
                        if "Program log: " in log and self.RAYDIUM_V4_PROGRAM in log:
                            pool_data = self.decode_pool_data(log)
                            if pool_data:
                                # Get pool address
                                pool_address = self._extract_pool_address(logs)
                                if pool_address:
                                    self._update_pool_data(pool_address, pool_data)
                    
            except Exception as e:
                logger.error(f"Process error: {str(e)}\n{traceback.format_exc()}")
    
    def _extract_pool_address(self, logs: List[str]) -> Optional[str]:
            """Extract pool address from logs"""
            try:
                for log in logs:
                    for pool in self.SOL_POOLS:
                        if pool in log:
                            logger.info(f"Found pool: {pool[:8]}")
                            return pool
                return None
            except Exception as e:
                logger.error(f"Pool address extraction error: {e}")
                return None
    
    def _update_pool_data(self, pool_address: str, pool_data: dict):
            """Update pool data with logging"""
            try:
                pool_data['token_mint'] = pool_address
                pool_data['last_update'] = time.time()
                
                # Calculate metrics
                base_usd = pool_data['base_sol'] * 110  # Approx SOL price
                logger.info("Pool Update:")
                logger.info(f"Pool: {pool_address[:8]}")
                logger.info(f"Liquidity: ${base_usd:,.2f}")
                logger.info(f"Base: {pool_data['base_sol']:.2f} SOL")
                logger.info(f"Quote: {pool_data['quote_sol']:.2f} WSOL")
                logger.info(f"SOL/WSOL Ratio: {pool_data['quote_sol']/pool_data['base_sol']:.4f}")
                
                self.active_pools[pool_address] = pool_data
                self.last_update[pool_address] = time.time()
                
            except Exception as e:
                logger.error(f"Pool data update error: {e}")
    
    def get_active_pools(self) -> List[Dict]:
        """Get currently active pools"""
        current_time = time.time()
        active_pools = []
        
        for token_mint, pool_data in self.active_pools.items():
            last_update = self.last_update.get(token_mint, 0)
            if current_time - last_update <= 300:  # 5 minutes
                active_pools.append({
                    'token_mint': token_mint,
                    **pool_data
                })
                
        return active_pools

    def stop(self):
        """Stop monitoring"""
        self._shutdown = True
        logger.info("Stopping Raydium monitor...") write this without the spacing issues please add EXTREMELY PROFESSIONAL GRADE LOGGING THROUGHOUT