import asyncio
import base64
import json
import logging
import time
import websockets

from typing import Dict, List, Optional, Set
from decimal import Decimal
from dataclasses import dataclass, field

import torch

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@dataclass
class TradingMetrics:
    messages_received: int = 0
    opportunities_found: int = 0
    successful_trades: int = 0
    total_profit: Decimal = Decimal('0')
    start_time: float = field(default_factory=time.time)
    sandwich_profits: Decimal = Decimal('0')
    arbitrage_profits: Decimal = Decimal('0')
    failed_attempts: Dict[str, int] = field(default_factory=dict)
    active_paths: Set[str] = field(default_factory=set)

class AdvancedTrading:
    def __init__(self, trader, config):
        self.trader = trader
        self.config = config
        self.active = False
        self.metrics = TradingMetrics()

        # Initialize CUDA device and streams if available
        if torch.cuda.is_available():
            self.device = torch.device('cuda')
            self.streams = {
                'arbitrage': torch.cuda.Stream(),
                'sandwich': torch.cuda.Stream(),
                'direct': torch.cuda.Stream(),
                'compute': torch.cuda.Stream()
            }
            self._setup_processing_buffers()
            logger.info("Advanced trading initialized with GPU acceleration")
        else:
            self.device = torch.device('cpu')
            self.streams = {}
            logger.warning("CUDA not available. Running on CPU.")

    def _setup_processing_buffers(self):
        """Initialize GPU processing buffers"""
        try:
            with torch.cuda.stream(self.streams['compute']):
                self.opportunity_buffer = torch.zeros(
                    (1024, 128),
                    device=self.device,
                    dtype=torch.float16
                ).pin_memory()

                self.price_buffer = torch.zeros(
                    (512, 2),
                    device=self.device,
                    dtype=torch.float32
                ).pin_memory()

                logger.debug("GPU buffers initialized")
        except Exception as e:
            logger.error(f"Buffer initialization error: {e}")
            raise

    async def start_websocket_monitoring(self):
        """Start monitoring WebSocket for opportunities"""
        self.active = True
        logger.info("Starting advanced trading monitor...")

        while self.active:
            try:
                async with websockets.connect(
                    self.config['rpc']['helius']['websocket'],
                    ping_interval=self.config['system']['websocket']['ping_interval'],
                    ping_timeout=self.config['system']['websocket']['ping_timeout'],
                    close_timeout=self.config['system']['websocket']['close_timeout'],
                    max_size=self.config['system']['websocket']['max_message_size']
                ) as ws:
                    await self._subscribe_to_logs(ws)

                    while self.active:
                        msg = await ws.recv()
                        self.metrics.messages_received += 1

                        # Process opportunities
                        await self._process_message(msg)

                        # Log metrics periodically
                        if self.metrics.messages_received % 100 == 0:
                            self._log_metrics()

            except websockets.exceptions.ConnectionClosedError:
                logger.warning("WebSocket connection closed. Reconnecting in 1 second...")
                await asyncio.sleep(1)
            except Exception as e:
                logger.error(f"WebSocket error: {e}. Reconnecting in 1 second...")
                await asyncio.sleep(1)

    async def _subscribe_to_logs(self, ws):
        """Subscribe to logs via WebSocket"""
        try:
            subscribe_message = {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "logsSubscribe",
                "params": [
                    {"mentions": [self.config['programs']['raydium']]},
                    {"commitment": "processed"}
                ]
            }
            await ws.send(json.dumps(subscribe_message))
            logger.debug("Subscribed to logs")
        except Exception as e:
            logger.error(f"Subscription error: {e}")

    async def _process_message(self, msg: str):
        """Process incoming WebSocket message with strategy checking"""
        try:
            data = json.loads(msg)
            if 'params' not in data:
                return

            logs = data['params'].get('result', {}).get('value', {}).get('logs', [])
            for log in logs:
                if "Program log:" not in log:
                    continue

                # Decode and process the log data
                raw_data = base64.b64decode(log.split("Program log: ")[1].strip())
                self.metrics.opportunities_found += 1

                # Check all strategies in parallel
                results = await asyncio.gather(
                    self._check_sandwich_opportunity(raw_data),
                    self._check_triangle_arbitrage(raw_data),
                    self._check_multi_hop_opportunity(raw_data),
                    self._check_direct_swap(raw_data),
                    return_exceptions=True
                )

                valid_opportunities = [
                    r for r in results if isinstance(r, dict) and r.get('profit', 0) > 0
                ]

                if valid_opportunities:
                    # Select the best opportunity based on profit/risk ratio
                    best_opp = max(
                        valid_opportunities,
                        key=lambda x: x['profit'] * x.get('confidence', 1.0) / x.get('impact', 1.0)
                    )

                    logger.info(f"Found opportunity: {best_opp['type']} with profit {best_opp['profit']:.4f}%")
                    await self._execute_opportunity(best_opp)

        except json.JSONDecodeError:
            logger.error("JSON decode error in message processing")
        except Exception as e:
            logger.error(f"Message processing error: {e}")

    async def _check_sandwich_opportunity(self, raw_data: bytes) -> Optional[Dict]:
        """Check for sandwich opportunities"""
        try:
            # Decode transaction data
            victim_tx = await self._decode_victim_transaction(raw_data)
            if not victim_tx:
                return None

            pool_address = victim_tx['pool']

            # Simulate front-run and back-run trades
            front_run = await self._simulate_front_run(victim_tx)
            back_run = await self._simulate_back_run(victim_tx, front_run)

            if not front_run or not back_run:
                return None

            # Calculate expected profit
            profit = back_run['amount_out'] - front_run['amount_in']

            if profit <= 0:
                return None

            return {
                'type': 'sandwich',
                'profit': float(profit),
                'front_run': front_run,
                'back_run': back_run,
                'victim': victim_tx,
                'pool': pool_address,
                'confidence': 0.9,  # High confidence for sandwich
                'impact': (front_run['price_impact'] + back_run['price_impact']) / 2  # Average impact
            }

        except Exception as e:
            logger.error(f"Sandwich check error: {e}")
            return None

    async def _decode_victim_transaction(self, raw_data: bytes) -> Optional[Dict]:
        """Decode victim transaction from raw data"""
        try:
            # Assuming raw_data is JSON-encoded transaction data
            victim_tx = json.loads(raw_data.decode('utf-8'))
            return victim_tx
        except Exception as e:
            logger.error(f"Error decoding victim transaction: {e}")
            return None

    async def _simulate_front_run(self, victim_tx: Dict) -> Optional[Dict]:
        """Simulate front-run trade"""
        try:
            amount_in = victim_tx['amount']
            price_impact = self.config['execution']['front_run_price_impact']  # Configured price impact for front-run
            amount_out = amount_in * (1 - price_impact)

            return {
                'amount_in': amount_in,
                'amount_out': amount_out,
                'price_impact': price_impact,
                'min_amount_out': amount_out * (1 - self.config['execution']['slippage'])
            }
        except Exception as e:
            logger.error(f"Front-run simulation error: {e}")
            return None

    async def _simulate_back_run(self, victim_tx: Dict, front_run: Dict) -> Optional[Dict]:
        """Simulate back-run trade"""
        try:
            amount_in = front_run['amount_out']
            price_impact = self.config['execution']['back_run_price_impact']  # Configured price impact for back-run
            amount_out = amount_in * (1 + price_impact)

            return {
                'amount_in': amount_in,
                'amount_out': amount_out,
                'price_impact': price_impact,
                'min_amount_out': amount_out * (1 - self.config['execution']['slippage'])
            }
        except Exception as e:
            logger.error(f"Back-run simulation error: {e}")
            return None

    async def _check_triangle_arbitrage(self, raw_data: bytes) -> Optional[Dict]:
        """Check for triangle arbitrage opportunities"""
        try:
            # Decode token information
            token_info = await self._decode_token_info(raw_data)
            if not token_info:
                return None

            # Find arbitrage paths
            paths = await self.trader.arbitrage_finder.find_triangle_paths(token_info['token_in'], token_info['token_out'])
            if not paths:
                return None

            best_path = None
            best_profit = 0
            best_amounts = None
            best_impact = 0

            for path in paths:
                amounts, impact, profit = await self._simulate_arbitrage_path(path)
                if profit > best_profit:
                    best_path = path
                    best_profit = profit
                    best_amounts = amounts
                    best_impact = impact

            if best_profit <= 0:
                return None

            return {
                'type': 'triangle',
                'profit': float(best_profit),
                'path': best_path,
                'amounts': best_amounts,
                'impact': best_impact,
                'confidence': 0.8  # Confidence for triangle arbitrage
            }

        except Exception as e:
            logger.error(f"Triangle arbitrage error: {e}")
            return None

    async def _simulate_arbitrage_path(self, path: List[str]) -> (List[float], float, float):
        """Simulate arbitrage path"""
        try:
            amounts = [self.config['trading']['position']['size']]
            impact = 0
            for i in range(len(path) - 1):
                amount_in = amounts[-1]
                amount_out = amount_in * (1 - self.config['execution']['path_fee'])  # Configured fee for each hop
                amounts.append(amount_out)
                impact += self.config['execution']['path_fee']  # Accumulate fee impact

            profit = amounts[-1] - amounts[0]
            return amounts, impact, profit
        except Exception as e:
            logger.error(f"Arbitrage path simulation error: {e}")
            return [], 0, 0

    async def _decode_token_info(self, raw_data: bytes) -> Optional[Dict]:
        """Decode token information from raw data"""
        try:
            # Assuming raw_data is JSON-encoded token info
            token_info = json.loads(raw_data.decode('utf-8'))
            return token_info
        except Exception as e:
            logger.error(f"Error decoding token info: {e}")
            return None

    async def _check_multi_hop_opportunity(self, raw_data: bytes) -> Optional[Dict]:
        """Check for multi-hop opportunities"""
        try:
            # Decode token information
            token_info = await self._decode_token_info(raw_data)
            if not token_info:
                return None

            # Find optimal route
            optimal_route = await self.trader.route_optimizer.find_optimal_route(
                token_info['token_in'],
                token_info['token_out'],
                self.config['trading']['position']['size']
            )

            if not optimal_route or optimal_route['profit'] <= 0:
                return None

            return {
                'type': 'multi_hop',
                'profit': float(optimal_route['profit']),
                'path': optimal_route['path'],
                'amounts': optimal_route['amounts'],
                'impact': optimal_route['impact'],
                'confidence': 0.75  # Confidence for multi-hop
            }

        except Exception as e:
            logger.error(f"Multi-hop check error: {e}")
            return None

    async def _check_direct_swap(self, raw_data: bytes) -> Optional[Dict]:
        """Check for direct swap opportunities"""
        try:
            # Decode market data
            market_data = await self._decode_market_data(raw_data)
            if not market_data:
                return None

            amount_in = self.config['trading']['position']['size']
            amount_out = amount_in * market_data['price']
            impact = market_data['price_impact']

            profit = amount_out - amount_in
            if profit <= 0:
                return None

            return {
                'type': 'direct',
                'profit': float(profit),
                'amount_in': amount_in,
                'amount_out': amount_out,
                'impact': impact,
                'confidence': 0.7  # Confidence for direct swap
            }

        except Exception as e:
            logger.error(f"Direct swap check error: {e}")
            return None

    async def _decode_market_data(self, raw_data: bytes) -> Optional[Dict]:
        """Decode market data from raw data"""
        try:
            # Assuming raw_data is JSON-encoded market data
            market_data = json.loads(raw_data.decode('utf-8'))
            return market_data
        except Exception as e:
            logger.error(f"Error decoding market data: {e}")
            return None

    async def _execute_opportunity(self, opportunity: Dict) -> bool:
        """Execute trading opportunity"""
        try:
            logger.info(f"Executing {opportunity['type']} opportunity...")

            if opportunity['type'] == 'sandwich':
                result = await self._execute_sandwich_trade(opportunity)
            elif opportunity['type'] == 'triangle':
                result = await self._execute_triangle_trade(opportunity)
            elif opportunity['type'] == 'multi_hop':
                result = await self._execute_multi_hop_trade(opportunity)
            else:  # direct swap
                result = await self._execute_direct_trade(opportunity)

            if result:
                self.metrics.successful_trades += 1
                profit = Decimal(str(opportunity['profit']))
                self.metrics.total_profit += profit

                if opportunity['type'] == 'sandwich':
                    self.metrics.sandwich_profits += profit
                elif opportunity['type'] in ['triangle', 'multi_hop']:
                    self.metrics.arbitrage_profits += profit

                logger.info(f"Trade executed successfully. Total profit: {float(self.metrics.total_profit):.6f} SOL")

            return result

        except Exception as e:
            logger.error(f"Trade execution error: {e}")
            return False

    async def _execute_sandwich_trade(self, opportunity: Dict) -> bool:
        """Execute sandwich trade"""
        try:
            # Prepare and send front-run transaction
            front_run_tx = await self.trader.trading_core.prepare_trade(
                pool_address=opportunity['pool'],
                amount_in=opportunity['front_run']['amount_in'],
                min_amount_out=opportunity['front_run']['min_amount_out']
            )
            front_run_result = await self.trader.trading_core.send_transaction(front_run_tx)

            if not front_run_result:
                return False

            # Wait for victim transaction to be processed
            await asyncio.sleep(self.config['execution']['victim_delay'])

            # Prepare and send back-run transaction
            back_run_tx = await self.trader.trading_core.prepare_trade(
                pool_address=opportunity['pool'],
                amount_in=opportunity['back_run']['amount_in'],
                min_amount_out=opportunity['back_run']['min_amount_out']
            )
            back_run_result = await self.trader.trading_core.send_transaction(back_run_tx)

            return back_run_result

        except Exception as e:
            logger.error(f"Sandwich execution error: {e}")
            return False

    async def _execute_triangle_trade(self, opportunity: Dict) -> bool:
        """Execute triangle arbitrage trade"""
        try:
            for i, pool in enumerate(opportunity['path']):
                result = await self.trader.trading_core.execute_trade(
                    pool_address=pool,
                    amount_in=opportunity['amounts'][i],
                    min_amount_out=opportunity['amounts'][i+1] * (1 - self.config['execution']['slippage'])
                )

                if not result:
                    return False

                # Small delay between hops
                await asyncio.sleep(self.config['execution']['hop_delay'])

            return True

        except Exception as e:
            logger.error(f"Triangle trade execution error: {e}")
            return False

    async def _execute_multi_hop_trade(self, opportunity: Dict) -> bool:
        """Execute multi-hop trade"""
        try:
            for i, pool in enumerate(opportunity['path']):
                result = await self.trader.trading_core.execute_trade(
                    pool_address=pool,
                    amount_in=opportunity['amounts'][i],
                    min_amount_out=opportunity['amounts'][i+1] * (1 - self.config['execution']['slippage'])
                )

                if not result:
                    return False

                # Small delay between hops
                await asyncio.sleep(self.config['execution']['hop_delay'])

            return True

        except Exception as e:
            logger.error(f"Multi-hop trade execution error: {e}")
            return False

    async def _execute_direct_trade(self, opportunity: Dict) -> bool:
        """Execute direct swap trade"""
        try:
            result = await self.trader.trading_core.execute_trade(
                pool_address=self.config['trading']['sol_pool'],
                amount_in=opportunity['amount_in'],
                min_amount_out=opportunity['amount_out'] * (1 - self.config['execution']['slippage'])
            )

            return result

        except Exception as e:
            logger.error(f"Direct trade execution error: {e}")
            return False

    def _log_metrics(self):
        """Log detailed performance metrics"""
        elapsed_time = time.time() - self.metrics.start_time
        messages_per_second = self.metrics.messages_received / elapsed_time
        opportunities_per_minute = (self.metrics.opportunities_found / elapsed_time) * 60
        trades_per_minute = (self.metrics.successful_trades / elapsed_time) * 60

        logger.info(f"""
Performance Metrics:
Messages/sec: {messages_per_second:.2f}
Opportunities/min: {opportunities_per_minute:.2f}
Trades/min: {trades_per_minute:.2f}
Total Messages: {self.metrics.messages_received}
Total Opportunities: {self.metrics.opportunities_found}
Successful Trades: {self.metrics.successful_trades}
Total Profit: {float(self.metrics.total_profit):.6f} SOL
Sandwich Profits: {float(self.metrics.sandwich_profits):.6f} SOL
Arbitrage Profits: {float(self.metrics.arbitrage_profits):.6f} SOL
""")

    async def stop(self):
        """Stop advanced trading"""
        self.active = False
        logger.info("Advanced trading stopped.")

        # Clean up resources
        try:
            if torch.cuda.is_available():
                torch.cuda.empty_cache()
                for stream in self.streams.values():
                    stream.synchronize()
                logger.info("GPU resources cleaned up.")
        except Exception as e:
            logger.error(f"Cleanup error: {e}")async def stop(self):
    """Stop advanced trading"""
    self.active = False
    logger.info("Stopping advanced trading...")

    # Clean up resources
    try:
        if torch.cuda.is_available():
            torch.cuda.empty_cache()
            for stream in self.streams.values():
                stream.synchronize()
            logger.info("GPU resources cleaned up.")
    except Exception as e:
        logger.error(f"Cleanup error: {e}")

async def _execute_triangle_trade(self, opportunity: Dict) -> bool:
    """Execute triangle arbitrage trade"""
    try:
        for i, pool in enumerate(opportunity['path']):
            result = await self.trader.trading_core.execute_trade(
                pool_address=pool,
                amount_in=opportunity['amounts'][i],
                min_amount_out=opportunity['amounts'][i+1] * (1 - self.config['execution']['slippage'])
            )
            if not result:
                logger.error(f"Triangle trade failed at hop {i}")
                return False
            await asyncio.sleep(0.1)  # Small delay between hops

        self.metrics.arbitrage_profits += Decimal(str(opportunity['profit']))
        return True

    except Exception as e:
        logger.error(f"Triangle trade execution error: {e}")
        return False

async def _execute_multi_hop_trade(self, opportunity: Dict) -> bool:
    """Execute multi-hop trade"""
    try:
        for i, pool in enumerate(opportunity['path']):
            result = await self.trader.trading_core.execute_trade(
                pool_address=pool,
                amount_in=opportunity['amounts'][i],
                min_amount_out=opportunity['amounts'][i+1] * (1 - self.config['execution']['slippage'])
            )
            if not result:
                logger.error(f"Multi-hop trade failed at hop {i}")
                return False
            await asyncio.sleep(0.1)  # Small delay between hops

        self.metrics.arbitrage_profits += Decimal(str(opportunity['profit']))
        return True

    except Exception as e:
        logger.error(f"Multi-hop trade execution error: {e}")
        return False

async def _execute_direct_trade(self, opportunity: Dict) -> bool:
    """Execute direct swap"""
    try:
        result = await self.trader.trading_core.execute_trade(
            pool_address=self.config['accounts']['sol_wsol']['amm_id'],
            amount_in=opportunity['amount_in'],
            min_amount_out=opportunity['amount_out'] * (1 - self.config['execution']['slippage'])
        )
        return result

    except Exception as e:
        logger.error(f"Direct trade execution error: {e}")
        return False

async def _safe_execute_opportunity(self, opportunity: Dict) -> bool:
    """Execute opportunity with safety checks and retries"""
    try:
        # Pre-trade validation
        if not await self._validate_pool_state(opportunity['pool'], opportunity['impact']):
            logger.warning(f"Pool state validation failed for {opportunity['pool']}")
            return False

        # Execute with retry logic
        return await self._execute_with_retry(
            lambda: self._execute_opportunity_with_timeout(opportunity),
            max_retries=self.config['execution']['retry_count']
        )

    except Exception as e:
        logger.error(f"Safe execution error: {e}")
        return False

async def _execute_with_retry(self, execute_func, max_retries: int = 3) -> bool:
    """Execute with exponential backoff retry"""
    retry_count = 0
    base_delay = 0.1  # Initial delay of 100ms

    while retry_count < max_retries:
        try:
            result = await execute_func()
            if result:
                return True
            delay = base_delay * (2 ** retry_count)
            logger.warning(f"Execution failed, retrying in {delay:.2f}s")
            await asyncio.sleep(delay)
            retry_count += 1

        except Exception as e:
            logger.error(f"Execution attempt {retry_count+1} failed: {e}")
            await asyncio.sleep(base_delay)
            retry_count += 1

    return False

async def _execute_opportunity_with_timeout(self, opportunity: Dict) -> bool:
    """Execute opportunity with timeout protection"""
    try:
        timeout = self.config['system']['websocket'].get('timeout', 5.0)
        async with asyncio.timeout(timeout):
            if opportunity['type'] == 'sandwich':
                return await self._execute_sandwich_trade(opportunity)
            elif opportunity['type'] in ['triangle', 'multi_hop']:
                return await self._execute_triangle_or_multi_hop_trade(opportunity)
            else:
                return await self._execute_direct_trade(opportunity)

    except asyncio.TimeoutError:
        logger.error(f"Execution timeout for {opportunity['type']} trade")
        return False
    except Exception as e:
        logger.error(f"Execution error: {e}")
        return False

async def _execute_sandwich_trade(self, opportunity: Dict) -> bool:
    """Execute sandwich trade"""
    try:
        # Execute front-run
        front_run_result = await self.trader.trading_core.execute_trade(
            pool_address=opportunity['pool'],
            amount_in=opportunity['front_run']['amount'],
            min_amount_out=opportunity['front_run']['min_out']
        )
        if not front_run_result:
            logger.error("Front-run failed")
            return False

        # Wait for victim transaction
        await asyncio.sleep(0.1)

        # Execute back-run
        back_run_result = await self.trader.trading_core.execute_trade(
            pool_address=opportunity['pool'],
            amount_in=opportunity['back_run']['amount'],
            min_amount_out=opportunity['back_run']['min_out']
        )
        return bool(back_run_result)

    except Exception as e:
        logger.error(f"Sandwich trade execution error: {e}")
        return False

async def _execute_triangle_or_multi_hop_trade(self, opportunity: Dict) -> bool:
    """Execute triangle or multi-hop trade"""
    try:
        for i, pool in enumerate(opportunity['path']):
            result = await self.trader.trading_core.execute_trade(
                pool_address=pool,
                amount_in=opportunity['amounts'][i],
                min_amount_out=opportunity['amounts'][i+1] * (1 - self.config['execution']['slippage'])
            )
            if not result:
                logger.error(f"Trade failed at hop {i}")
                return False
            await asyncio.sleep(0.1)  # Small delay between hops

        return True

    except Exception as e:
        logger.error(f"Triangle/Multi-hop trade execution error: {e}")
        return False

async def _validate_pool_state(self, pool_address: str, expected_impact: float) -> bool:
    """Validate pool state before execution"""
    try:
        current_state = await self.trader.trading_core.get_pool_state(pool_address)
        if not current_state:
            return False

        # Check if price impact is still acceptable
        impact_buffer = 1.2  # Allow 20% variance
        max_impact = self.config['trading']['limits']['max_price_impact']
        if expected_impact * impact_buffer > max_impact:
            logger.warning(f"Price impact too high for {pool_address}")
            return False

        return True

    except Exception as e:
        logger.error(f"Pool validation error: {e}")
        return False

async def _update_metrics(self, opportunity: Dict, success: bool):
    """Update trading metrics after execution"""
    try:
        if success:
            self.metrics.successful_trades += 1
            profit = Decimal(str(opportunity['profit']))
            self.metrics.total_profit += profit

            if opportunity['type'] == 'sandwich':
                self.metrics.sandwich_profits += profit
            elif opportunity['type'] in ['triangle', 'multi_hop']:
                self.metrics.arbitrage_profits += profit

        else:
            self.metrics.failed_attempts[opportunity['type']] = \
                self.metrics.failed_attempts.get(opportunity['type'], 0) + 1

    except Exception as e:
        logger.error(f"Metrics update error: {e}")

def main():
    # Initialize trader and config (implementation-specific)
    trader = Trader()
    config = {
        # ... configuration data ...
    }

    advanced_trading = AdvancedTrading(trader, config)
    asyncio.run(advanced_trading.start_websocket_monitoring())

if __name__ == "__main__":
    main() can you please write the full script here in the proper order with proper spacing with proper logging so i know whats going on please