import asyncio
import logging
import os
import sys
import json
import time
import torch
from pathlib import Path
from typing import Optional, Dict
import inspect
import signal
from decimal import Decimal

# Import optimized components
from solana_rust_bot import TradeParams, TradingCore
from solana_cli import SolanaCLI, WSOLState, TokenBalances
from advanced_trading import AdvancedTrading, TradingMetrics
from price_monitor import RaydiumMonitor
from price_impact import EnhancedImpactCalculator
from mev_protection import EnhancedMevProtection, MevConfig
from system_manager import SystemManager
from helius_client import HeliusWebSocketClient
from vector_store import EnhancedVectorStore, VectorConfig, PoolPattern  # Import the necessary vector classes
from trading_config import TradingConfig
from mempool_analyzer import EnhancedMempoolAnalyzer
from gpu_accelerator import GPUAccelerator, AcceleratorConfig

# Configure logging
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S',
    handlers=[logging.StreamHandler()]
)
logger = logging.getLogger(__name__)

class RaydiumTrader:
    def __init__(self, config_path: str):
        print("[INFO] Initializing RaydiumTrader...")
        self.start_time = time.time()
        self.load_config(config_path)

        # Initialize state variables
        self.active = False
        self.trading_active = False
        self.last_trade_time = 0
        self.trade_count = 0

        # Metrics tracking
        self.metrics = TradingMetrics()

        # Initialize Vector Store with configuration
        self.vector_config = VectorConfig()  # Initialize VectorConfig
        self.vector_store = EnhancedVectorStore(self.config)  # Initialize vector store.

        # Initialize GPU
        self._init_gpu()

        # Initialize components that don't require async
        self.trading_core = None
        self.system = None
        self.price_monitor = None
        self.advanced_trading = None
        self.impact_calculator = None
        self.mev_protection = None
        self.mempool_analyzer = None
        self.solana_cli = None
        
        # Add signal handlers for graceful shutdown
        signal.signal(signal.SIGINT, self._handle_shutdown)
        signal.signal(signal.SIGTERM, self._handle_shutdown)
        
        print("[INFO] RaydiumTrader initialized")

    def load_config(self, config_path: str):
        """Load trading configuration from file."""
        try:
            with open(config_path, 'r') as f:
                self.config = json.load(f)
            print("[INFO] Configuration loaded successfully")
            logger.info("Configuration loaded successfully")
        except Exception as e:
            print(f"[ERROR] Error loading configuration: {e}")
            logger.error(f"Error loading configuration: {e}")
            raise

    def _init_gpu(self):
        """Initialize GPU with optimal settings."""
        print("[INFO] Initializing GPU...")
        if torch.cuda.is_available():
            gpu_config = self.config["system"]["gpu"]
            optimization_config = gpu_config["optimization"]

            # Set CUDA optimization flags
            torch.backends.cudnn.benchmark = optimization_config.get("cudnn_benchmark", True)
            torch.backends.cuda.matmul.allow_tf32 = optimization_config.get("allow_tf32", True)
            torch.backends.cuda.matmul.allow_fp16_reduction = optimization_config.get("allow_fp16_reduction", True)

            accelerator_config = AcceleratorConfig(
                memory_fraction=gpu_config["memory_fraction"],
                enable_tensor_cores=gpu_config["use_tensor_cores"],
                batch_size=gpu_config["batch_size"],
                precision=gpu_config["precision"]
            )
            self.gpu_accelerator = GPUAccelerator(accelerator_config)
            self.gpu_accelerator.initialize()

            gpu_info = torch.cuda.get_device_properties(0)
            print(f"[INFO] GPU Initialized: {gpu_info.name} ({gpu_info.total_memory / 1024**3:.2f}GB)")
            logger.info(f"GPU Initialized: {gpu_info.name} ({gpu_info.total_memory / 1024**3:.2f}GB)")
        else:
            print("[WARNING] No GPU available, using CPU")
            logger.warning("No GPU available, using CPU")

    async def initialize(self) -> bool:
        """Initialize all trading components with proper error handling."""
        print("[INFO] Starting system initialization...")
        try:
            # Initialize WebSocket client
            self.ws_client = HeliusWebSocketClient(self.config)
            await self.ws_client.start()
            print("[INFO] Helius WebSocket Client initialized")
            logger.info("Helius WebSocket Client initialized")

            # Initialize system manager first
            self.system = SystemManager()
            await self.system.start()
            print("[INFO] SystemManager started")

            # Create standardized trading config
            trading_config = TradingConfig(
                min_profit=self.config["trading"]["limits"]["min_profit_pct"],
                max_position=self.config["trading"]["position"]["max_size"],
                risk_level=self.config["trading"]["limits"]["max_price_impact"]
            )

            # Initialize TradingCore (Rust)
            self.trading_core = TradingCore(
                rpc_url=self.config["rpc"]["helius"]["endpoint"],
                config_json=json.dumps(trading_config.__dict__)
            )
            print("[INFO] TradingCore (Rust) initialized successfully")
            logger.info("TradingCore (Rust) initialized successfully")

            # Initialize MEV protection with correct config
            mev_config = MevConfig(
                max_fee_multiplier=self.config["execution"]["fee_multiplier"],
                min_priority_fee=self.config["execution"]["min_priority_fee"],
                max_priority_fee=self.config["execution"]["max_priority_fee"],
                compute_budget=self.config["execution"]["compute_limit"],
                slippage=self.config["execution"]["slippage"]
            )
            self.mev_protection = EnhancedMevProtection(mev_config)
            print("[INFO] MEV protection initialized")
            logger.info("MEV protection initialized")

            # Initialize other components
            self.impact_calculator = EnhancedImpactCalculator(self.trading_core, self.config)
            print("[INFO] Impact calculator initialized")
            logger.info("Impact calculator initialized")

            self.advanced_trading = AdvancedTrading(self, self.config)
            await self.advanced_trading.start()
            print("[INFO] Advanced trading initialized")
            logger.info("Advanced trading initialized")

            self.price_monitor = RaydiumMonitor(self.config)
            print("[INFO] Price monitor initialized")
            logger.info("Price monitor initialized")

            self.mempool_analyzer = EnhancedMempoolAnalyzer(
                self.trading_core,
                self.config["vector_store"]["cache_size"],
                self.config["vector_store"]["update_interval"]
            )
            print("[INFO] Mempool analyzer initialized")
            logger.info("Mempool analyzer initialized")

            # Initialize SolanaCLI
            self.solana_cli = SolanaCLI(self.trading_core)
            await self.solana_cli.start_monitoring()
            print("[INFO] SolanaCLI initialized")
            logger.info("SolanaCLI initialized")

            # Get initial balance
            try:
                raw_balance = await self.trading_core.get_balance()
                if raw_balance is not None:
                    sol_balance = int(raw_balance) / 1_000_000_000
                    print(f"[INFO] Initial SOL Balance: {sol_balance:.6f} SOL")
                    logger.info(f"Initial SOL Balance: {sol_balance:.6f} SOL")
            except Exception as e:
                print(f"[WARNING] Could not get initial balance: {e}")
                logger.warning(f"Could not get initial balance: {e}")

            self.active = True
            self.trading_active = True
            print("[INFO] All components initialized successfully")
            return True

        except Exception as e:
            print(f"[ERROR] Initialization error: {e}")
            logger.error(f"Initialization error: {e}")
            raise

    async def start_trading(self):
        """Start all trading processes."""
        print("[INFO] Starting trading processes...")
        try:
            # Create monitoring tasks
            tasks = [
                asyncio.create_task(self.price_monitor.start_monitoring()),
                asyncio.create_task(self.monitor_opportunities()),
                asyncio.create_task(self.system.monitor_loop()),
                asyncio.create_task(self.process_ws_messages()),
                asyncio.create_task(self.update_token_balances()),
                asyncio.create_task(self.monitor_system_metrics()),
                asyncio.create_task(self.mempool_analyzer.start_monitoring()),
                asyncio.create_task(self.vector_store.start_pattern_monitoring())
            ]

            # Wait for any task to complete or fail
            done, pending = await asyncio.wait(
                tasks,
                return_when=asyncio.FIRST_EXCEPTION
            )

            # Check for exceptions
            for task in done:
                if task.exception():
                    raise task.exception()

        except Exception as e:
            print(f"[ERROR] Trading error: {e}")
            logger.error(f"Trading error: {e}")
            await self.cleanup()
            raise

    async def process_ws_messages(self):
        """Process WebSocket messages."""
        while self.trading_active:
            try:
                if not self.ws_client.process_queue.empty():
                    message = await self.ws_client.process_queue.get()
                    print(f"[INFO] Processing WS message: {message}")
                    await self.advanced_trading.process_market_update(message)
                await asyncio.sleep(0.001)  # Yield control to event loop
            except Exception as e:
                print(f"[ERROR] WS message processing error: {e}")
                logger.error(f"WS message processing error: {e}")

    async def monitor_opportunities(self):
        """Monitor for trading opportunities."""
        print("[INFO] Starting opportunity monitoring...")
        
        while self.trading_active:
            try:
                # Check health status
                if not self.system.is_healthy():
                    print("[WARNING] System health check failed, pausing trading")
                    logger.warning("System health check failed, pausing trading")
                    await asyncio.sleep(5)
                    continue

                # Process opportunities
                opportunity = await self.advanced_trading.get_next_opportunity()
                if opportunity:
                    print(f"[INFO] Found trading opportunity: {opportunity}")
                    await self.process_opportunity(opportunity)

                await asyncio.sleep(0.001)  # 1ms delay

            except Exception as e:
                print(f"[ERROR] Opportunity monitoring error: {e}")
                logger.error(f"Opportunity monitoring error: {e}")
                await asyncio.sleep(1)

    async def process_opportunity(self, opportunity: Dict):
        """Process trading opportunity."""
        try:
            if not self.trading_active:
                return
            
            # Update metrics for opportunity processing
            self.metrics.opportunities_found += 1
            print(f"[INFO] Processing opportunity: {opportunity}")

            # Validate opportunity
            valid, metrics = await self.impact_calculator.validate_trade(
                opportunity['pool'],
                opportunity['amount_in'],
                opportunity['min_amount_out'],
                opportunity['type']
            )

            if valid:
                # Extract a vector; here we're simulating it, replace it with actual vector extraction logic.
                pattern_vector = torch.randn(self.vector_store.config.vector_dim).to(self.vector_store.device)
                timestamp = time.time()  # Current time

                # Create and store a pool pattern
                pattern = PoolPattern(vector=pattern_vector, timestamp=timestamp, metadata=opportunity)
                await self.vector_store.store_pattern(opportunity['pool'], pattern_vector, opportunity)

                # Update metrics for successful trades
                self.metrics.successful_trades += 1
                self.metrics.total_profit += Decimal(str(opportunity['profit']))
                
                if opportunity['type'] == 'sandwich':
                    self.metrics.sandwich_profits += Decimal(str(opportunity['profit']))
                    print(f"[INFO] Sandwich profit: {opportunity['profit']}")
                else:
                    self.metrics.impact_profits += Decimal(str(opportunity['profit']))
                    print(f"[INFO] Impact profit: {opportunity['profit']}")

                # Execute trade with trade parameters
                trade_params = TradeParams(
                    pool_address=opportunity["pool"],
                    amount_in=opportunity["amount_in"],
                    min_amount_out=opportunity["min_amount_out"],
                    wsol_account=opportunity.get("wsol_account", "")
                )
                
                # Trade execution with MEV protection
                protected_tx = await self.mev_protection.protect_transaction(opportunity)
                if protected_tx:
                    print(f"[INFO] Executing trading opportunity: {protected_tx}")
                    result = await self.advanced_trading.execute_opportunity(protected_tx)
                    if result:
                        self.trade_count += 1
                        self.last_trade_time = time.time()
                        print(f"[INFO] Trade executed successfully: {result}")

        except Exception as e:
            print(f"[ERROR] Opportunity processing error: {e}")
            logger.error(f"Opportunity processing error: {e}")

    async def update_token_balances(self):
        """Update token balances."""
        try:
            balances = await self.solana_cli.get_token_balances()
            self.token_balances = TokenBalances(
                wsol=WSOLState(
                    balance=balances['wsol'],
                    last_update=time.time()
                ),
                tokens=balances['tokens']
            )
            print(f"[INFO] Token balances updated: {balances}")
        except Exception as e:
            print(f"[ERROR] Balance update error: {e}")
            logger.error(f"Balance update error: {e}")

    async def monitor_system_metrics(self):
        """Monitor system performance metrics."""
        print("[INFO] Starting system metrics monitoring...")
        while self.trading_active:
            try:
                metrics = {
                    'gpu_utilization': self.gpu_accelerator.get_utilization(),
                    'memory_usage': self.system.get_memory_usage(),
                    'trade_count': self.metrics.successful_trades,
                    'total_profit': float(self.metrics.total_profit),
                    'uptime_hours': self.get_runtime_hours()
                }
                await self.system.update_metrics(metrics)
                print(f"[INFO] System metrics updated: {metrics}")
                await asyncio.sleep(1)
            except Exception as e:
                print(f"[ERROR] Metrics monitoring error: {e}")
                logger.error(f"Metrics monitoring error: {e}")

    def _handle_shutdown(self, signum, frame):
        """Handle shutdown signals."""
        print("[INFO] Shutdown signal received")
        logger.info("Shutdown signal received")
        asyncio.create_task(self.cleanup())

    async def cleanup(self):
        """Clean up all resources properly."""
        print("[INFO] Cleaning up trading system...")
        try:
            self.active = False
            self.trading_active = False
        
            # Cleanup WebSocket client
            if hasattr(self, 'ws_client'):
                await self.ws_client.cleanup()
                print("[INFO] WebSocket client cleaned up")
        
            cleanup_tasks = []
        
            # Cleanup components
            components = [
                self.advanced_trading,
                self.impact_calculator,
                self.price_monitor,
                self.system,
                self.mev_protection,
                self.mempool_analyzer,
                self.vector_store,
                self.solana_cli,
                self.gpu_accelerator
            ]
        
            for component in components:
                if component and hasattr(component, "cleanup"):
                    try:
                        cleanup_method = component.cleanup()
                        if inspect.iscoroutine(cleanup_method):
                            cleanup_tasks.append(asyncio.create_task(cleanup_method))
                            print(f"[INFO] Cleaning up component: {component.__class__.__name__}")
                    except Exception as e:
                        print(f"[ERROR] Error cleaning up component: {e}")
                        logger.error(f"Error cleaning up component: {e}")
        
            if cleanup_tasks:
                await asyncio.wait(cleanup_tasks, return_when=asyncio.ALL_COMPLETED)
            
            # Clear GPU memory if it exists
            if hasattr(self, 'gpu_accelerator') and self.gpu_accelerator:
                try:
                    await self.gpu_accelerator.cleanup()
                    print("[INFO] GPU accelerator cleaned up")
                except Exception as e:
                    print(f"[ERROR] GPU cleanup error: {e}")

            # Clear remaining GPU cache
            if torch.cuda.is_available():
                try:
                    torch.cuda.empty_cache()
                    print("[INFO] Cleared GPU memory cache")
                except Exception as e:
                    print(f"[ERROR] Error clearing GPU cache: {e}")
        
            runtime_hours = self.get_runtime_hours()
            print(f"[INFO] Trading session complete. Runtime: {runtime_hours:.2f} hours")
            logger.info(f"Trading session complete. Runtime: {runtime_hours:.2f} hours")
        
        except Exception as e:
            print(f"[ERROR] Cleanup error: {e}")
            logger.error(f"Cleanup error: {e}")
        finally:
            self.active = False
            self.trading_active = False

    def get_runtime_hours(self) -> float:
        """Calculate runtime in hours."""
        return (time.time() - self.start_time) / 3600

async def main():
    """Main entry point with enhanced error handling."""
    bot = None
    
    try:
        print("[INFO] Starting Raydium Trading Bot...")
        
        # Initialize bot with proper path resolution
        config_path = str(Path(__file__).parent / "config.json")
        bot = RaydiumTrader(config_path)
        
        # Initialize components
        await bot.initialize()
        
        # Start trading
        await bot.start_trading()
        
    except KeyboardInterrupt:
        print("[INFO] Keyboard interrupt received")
        logger.info("Keyboard interrupt received")
    except Exception as e:
        print(f"[ERROR] Critical system error: {e}")
        logger.error(f"Critical system error: {e}")
    finally:
        if bot:
            await bot.cleanup()

if __name__ == "__main__":
    if sys.platform == 'win32':
        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
    
    # Create a new event loop
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    
    try:
        print("[INFO] Running the event loop...")
        loop.run_until_complete(main())
    except KeyboardInterrupt:
        print("[INFO] Shutdown signal received")
        logger.info("Shutdown signal received")
    finally:
        loop.close() add extreme full knowledge debugg logging print to terminal please