import asyncio
import json
import time
import logging
import websockets
from collections import defaultdict
from typing import Dict, Optional
from solana_rust_bot import TradingCore
import base64
import traceback
from datetime import datetime

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s.%(msecs)03d - %(levelname)s - %(message)s',
    datefmt='%H:%M:%S',
    handlers=[
        logging.FileHandler('scanner_debug.log', mode='w', encoding='utf-8'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger()

class TokenScanner:
    def __init__(self, config_path: str = "config.json"):
        logger.info("\n🚀 INITIALIZING SCALING SCALPER...")
        
        with open(config_path) as f:
            self.config = json.load(f)
            
        self.trading_core = TradingCore(json.dumps(self.config))
        
        # Override config with optimized trading parameters
        self.PARAMS = self.config['trading']
        self.PARAMS.update({
            'take_profit': 0.04,      # 4% take profit
            'stop_loss': 0.03,        # 3% stop loss
            'min_bs_ratio': 6.5,      # High B/S ratio requirement
            'stale_timeout': 45,      # Exit if no movement in 45s
            'min_price_move': 0.005,  # Minimum 0.5% price movement expected
            'max_active_trades': 10
        })
        
        # Rate limiting for QuickNode (15 TPS)
        self.last_request_time = time.time()
        self.min_request_interval = 1.0 / 15
        
        # Token tracking
        self.tokens = defaultdict(lambda: {
            'buys_5m': [],
            'sells_5m': [],
            'total_volume_5m': 0.0,
            'bs_ratio': 0.0,
            'last_price': 0.0,
            'in_position': False,
            'entry_price': 0.0,
            'position_size': 0.0,
            'entry_time': 0,
            'wins': 0,
            'total_trades': 0
        })
        
        # Active trades and scaling
        self.active_trades = {}
        self.wins_to_scale = self.PARAMS['trades_to_scale']
        self.current_position_size = self.PARAMS['initial_size']
        
        self.print_settings()
        logger.info("✨ Scanner initialized!")

    def print_settings(self):
        logger.info("\n=== SCALING SCALPER SETTINGS ===")
        logger.info(f"💰 Initial Position: {self.PARAMS['initial_size']*100}%")
        logger.info(f"📈 Take Profit: 4.0%")
        logger.info(f"🛑 Stop Loss: 3.0%")
        logger.info(f"⏱️ Stale Timeout: 45s")
        logger.info(f"🎯 Scale After: {self.PARAMS['trades_to_scale']} wins")
        logger.info(f"💎 Min Volume: {self.PARAMS['min_volume']} SOL")
        logger.info(f"📊 Min B/S Ratio: {self.PARAMS['min_bs_ratio']}")
        logger.info(f"⚡ Max Active Trades: {self.PARAMS['max_active_trades']}")
        logger.info("================================\n")

    async def rate_limit_wait(self):
        """Ensure we don't exceed QuickNode TPS limits"""
        current_time = time.time()
        time_since_last = current_time - self.last_request_time
        if time_since_last < self.min_request_interval:
            await asyncio.sleep(self.min_request_interval - time_since_last)
        self.last_request_time = time.time()

    def parse_pool_data(self, data: str) -> Optional[Dict]:
        """Parse Raydium AMM pool data with correct offsets"""
        try:
            raw_data = base64.b64decode(data)
        
            # Raydium AMM account data layout:
            # [0-31]: AMM key
            # [32-63]: Token A account
            # [64-95]: Token B account (SPL token)
            # [344-352]: Price data
            # [360-368]: Volume data
            # [372]: Trade direction
        
            token_mint = raw_data[64:95].hex()
            price = int.from_bytes(raw_data[344:352], 'little') / 1e9  # To SOL
            volume = int.from_bytes(raw_data[360:368], 'little') / 1e9  # To SOL
            is_buy = bool(raw_data[372:373])  # Corrected this line
        
            logger.info("\n📊 PARSED POOL DATA:")
            logger.info(f"Token: {token_mint[:6]}...{token_mint[-4:]}")
            logger.info(f"Price: {price:.6f} SOL")
            logger.info(f"Volume: {volume:.4f} SOL")
            logger.info(f"Type: {'BUY 🟢' if is_buy else 'SELL 🔴'}")
        
            return {
                'token_mint': token_mint,
                'price': price,
                'volume': volume,
                'is_buy': is_buy,
                'timestamp': time.time()
            }
        except Exception as e:
            logger.error(f"Pool parsing error: {str(e)}")
            logger.debug(f"Raw data hex: {raw_data.hex()[:100]}")
            return None
    def update_metrics(self, token_mint: str, data: Dict) -> Dict:
        """Update token metrics"""
        token = self.tokens[token_mint]
        current_time = time.time()
        
        if data['is_buy']:
            token['buys_5m'].append((data['volume'], current_time))
        else:
            token['sells_5m'].append((data['volume'], current_time))
            
        # Clean old data (>5min)
        token['buys_5m'] = [(v,t) for v,t in token['buys_5m'] if current_time - t <= 300]
        token['sells_5m'] = [(v,t) for v,t in token['sells_5m'] if current_time - t <= 300]
        
        buy_volume = sum(v for v,_ in token['buys_5m'])
        sell_volume = sum(v for v,_ in token['sells_5m'])
        token['bs_ratio'] = buy_volume / max(sell_volume, 1e-8)
        token['total_volume_5m'] = buy_volume + sell_volume
        token['last_price'] = data['price']
        
        return {
            'bs_ratio': token['bs_ratio'],
            'volume': token['total_volume_5m']
        }

    async def execute_trade(self, token_mint: str, metrics: Dict):
        """Execute trade if conditions are met"""
        if len(self.active_trades) >= self.PARAMS['max_active_trades']:
            return
            
        token = self.tokens[token_mint]
        if token['in_position']:
            await self.manage_position(token_mint)
            return
            
        if (metrics['bs_ratio'] >= self.PARAMS['min_bs_ratio'] and 
            metrics['volume'] >= self.PARAMS['min_volume']):
            
            logger.info(f"\n🎯 TRADE OPPORTUNITY:")
            logger.info(f"Token: {token_mint[:6]}...{token_mint[-4:]}")
            logger.info(f"B/S Ratio: {metrics['bs_ratio']:.2f}")
            logger.info(f"Volume: {metrics['volume']:.2f} SOL")
            
            # Validate token
            await self.rate_limit_wait()
            token_info = await self.trading_core.get_token_info(token_mint)
            
            if (token_info['liquidity_locked'] >= self.PARAMS['liquidity_lock_min'] and
                token_info['largest_holder_pct'] <= self.PARAMS['max_holder_pct']):
                
                await self.rate_limit_wait()
                result = await self.trading_core.buy_token(token_mint, self.current_position_size)
                
                if 'quote' in result:
                    price = float(result['price'])
                    logger.info(f"\n✅ ENTRY EXECUTED:")
                    logger.info(f"Price: {price:.6f} SOL")
                    logger.info(f"Size: {self.current_position_size:.3%}")
                    
                    token.update({
                        'in_position': True,
                        'entry_price': price,
                        'position_size': self.current_position_size,
                        'entry_time': time.time()
                    })
                    self.active_trades[token_mint] = token

    async def manage_position(self, token_mint: str):
        """Manage open position"""
        token = self.tokens[token_mint]
        current_time = time.time()
        time_in_trade = current_time - token['entry_time']
        
        await self.rate_limit_wait()
        price_info = await self.trading_core.get_price(token_mint)
        current_price = float(price_info['price'])
        
        pnl = (current_price - token['entry_price']) / token['entry_price']
        
        logger.info(f"\n📊 POSITION UPDATE:")
        logger.info(f"Token: {token_mint[:6]}...{token_mint[-4:]}")
        logger.info(f"P&L: {pnl:.2%}")
        logger.info(f"Time in trade: {time_in_trade:.1f}s")
        
        # Exit conditions with wider stop
        exit_reason = None
        if pnl >= self.PARAMS['take_profit']:
            exit_reason = f"Take profit hit: {pnl:.1%}"
        elif pnl <= -self.PARAMS['stop_loss']:
            exit_reason = f"Stop loss hit: {pnl:.1%}"
        elif time_in_trade >= self.PARAMS['stale_timeout']:
            price_change = abs(current_price - token['entry_price']) / token['entry_price']
            if price_change < self.PARAMS['min_price_move']:
                exit_reason = "Position stale - no significant movement"
        
        if exit_reason:
            await self.close_position(token_mint, exit_reason, pnl)

    async def close_position(self, token_mint: str, reason: str, pnl: float):
        """Close position and update scaling"""
        token = self.tokens[token_mint]
        
        await self.rate_limit_wait()
        result = await self.trading_core.sell_token(token_mint, token['position_size'])
        
        if 'quote' in result:
            logger.info(f"\n🚪 POSITION CLOSED - {reason}")
            logger.info(f"Token: {token_mint[:6]}...{token_mint[-4:]}")
            logger.info(f"Final P&L: {pnl:.2%}")
            
            # Update stats and scale position
            token['total_trades'] += 1
            if pnl > 0:
                token['wins'] += 1
                if token['wins'] >= self.wins_to_scale:
                    old_size = self.current_position_size
                    self.current_position_size = min(
                        self.current_position_size + self.PARAMS['position_increment'],
                        self.PARAMS['max_position']
                    )
                    logger.info(f"\n📈 SCALING UP:")
                    logger.info(f"Old size: {old_size:.3%}")
                    logger.info(f"New size: {self.current_position_size:.3%}")
                    token['wins'] = 0
            
            token.update({
                'in_position': False,
                'entry_price': 0.0,
                'position_size': 0.0
            })
            del self.active_trades[token_mint]

    async def monitor_pools(self):
        """Monitor Raydium pools"""
        while True:
            try:
                ws_url = self.config['rpc']['websocket']['endpoint']
                async with websockets.connect(ws_url) as ws:
                    logger.info("📡 Connected to Marga Fast Mainnet")
                    
                    for pool_id in self.config['raydium']['pool_ids']:
                        await ws.send(json.dumps({
                            "jsonrpc": "2.0",
                            "id": 1,
                            "method": "accountSubscribe",
                            "params": [
                                pool_id,
                                {"encoding": "base64", "commitment": "processed"}
                            ]
                        }))
                        logger.info(f"✅ Subscribed to pool: {pool_id[:8]}")
                    
                    while True:
                        msg = await ws.recv()
                        data = json.loads(msg)
                        
                        if 'params' in data:
                            account_data = data['params']['result']['value']
                            
                            if 'data' in account_data:
                                pool_data = self.parse_pool_data(account_data['data'][0])
                                
                                if pool_data:
                                    metrics = self.update_metrics(
                                        pool_data['token_mint'],
                                        pool_data
                                    )
                                    await self.execute_trade(
                                        pool_data['token_mint'],
                                        metrics
                                    )
                        
            except websockets.exceptions.ConnectionClosed:
                logger.error("⚠️ Websocket connection closed")
                await asyncio.sleep(1)
            except Exception as e:
                logger.error(f"Connection error: {str(e)}")
                logger.error(traceback.format_exc())
                await asyncio.sleep(1)

    async def run(self):
        """Start the scaling scalper"""
        try:
            logger.info("\n=== SCALING SCALPER v1.0 ===")
            await self.monitor_pools()
        except KeyboardInterrupt:
            logger.info("\n👋 Shutting down...")
        except Exception as e:
            logger.error(f"Fatal error: {str(e)}")
            logger.error(traceback.format_exc())

if __name__ == "__main__":
    try:
        scanner = TokenScanner()
        asyncio.run(scanner.run())
    except KeyboardInterrupt:
        logger.info("\n👋 Shutting down gracefully...")
    except Exception as e:
        logger.error(f"Startup error: {str(e)}")
        logger.error(traceback.format_exc()) add extreme logging to this script to make sure we find out why its not parsing properly please thank you