import asyncio
import os
import sys
from pathlib import Path
from dotenv import load_dotenv
import torch
import signal
import logging
import logging.handlers

# Import components
from solana_rust_bot import TradingCore, TradeConfig
from system_manager import SystemManager
from price_monitor import RaydiumMonitor 
from vector_store import EnhancedVectorStore
from price_impact import EnhancedImpactCalculator
from mempool_analyzer import EnhancedMempoolAnalyzer
from helius_client import HeliusWebSocketClient
from trade_executor import TradeExecutor
from solana_cli import SolanaCLI
from config_loader import config

# Configure logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

file_handler = logging.handlers.RotatingFileHandler(
    filename='C:\\solana_rust_bot\\logs\\system\\system.log',
    maxBytes=1024*1024,  # 1 MB
    backupCount=2,
    encoding='utf-8'
)

stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.WARNING)

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
stream_handler.setFormatter(formatter)

logger.addHandler(file_handler)
logger.addHandler(stream_handler)

def setup_environment():
    """Setup environment variables and paths"""
    # Check if keys in system environment
    if os.environ.get('SOLANA_PRIVATE_KEY'):
        logger.info("Solana keys found in system environment variables")
        return

    # If not in system vars, try .env file
    script_dir = Path(__file__).resolve()
    project_root = script_dir.parent.parent
    possible_env_paths = [
        project_root / '.env',
        Path.cwd() / '.env',
        project_root / 'src' / '.env'
    ]
    
    for env_path in possible_env_paths:
        if env_path.exists():
            logger.info(f"Loading configuration from .env file at: {env_path}")
            load_dotenv(env_path)
            return
            
    logger.error("Could not find environment configuration")
    raise ValueError("Environment configuration not found")

class RaydiumTrader:
    def __init__(self):
        """Initialize trading system components"""
        try:
            # Setup environment
            setup_environment()

            # Setup GPU if available
            self._setup_gpu()

            # Initialize core components
            logger.info("Initializing TradingCore...")
            
            # Get private key and log safely
            private_key_str = os.environ.get('SOLANA_PRIVATE_KEY')
            logger.info(f"Raw key from environment: {private_key_str[:10]}...")
            
            try:
                # Try reading from id.json
                if not (private_key_str and private_key_str.startswith('[') and private_key_str.endswith(']')):
                    logger.info("Trying to read private key from id.json...")
                    try:
                        with open('id.json', 'r') as f:
                            key_data = f.read().strip()
                            private_key_str = key_data
                    except Exception as e:
                        logger.error(f"Failed to read id.json: {e}")
                
                # Convert string array to bytes
                if private_key_str.startswith('[') and private_key_str.endswith(']'):
                    numbers = [int(x.strip()) for x in private_key_str[1:-1].split(',')]
                    private_key_bytes = bytes(numbers)
                    logger.info(f"Converted private key to {len(private_key_bytes)} bytes")
                else:
                    raise ValueError("Private key not in correct format. Check environment variable or id.json")
                
                # Initialize with private key bytes
                self.trading_core = TradingCore(private_key_bytes)
                logger.info("Successfully initialized TradingCore with keypair")
                
            except Exception as e:
                logger.error(f"Failed to process keypair: {str(e)}")
                logger.error("Ensure SOLANA_PRIVATE_KEY is set to the array from id.json")
                raise
            
            # Initialize supporting components
            self.system_manager = SystemManager()
            self.solana_cli = SolanaCLI(trading_core=self.trading_core)
            self.vector_store = EnhancedVectorStore()
            self.impact_calculator = EnhancedImpactCalculator()
            self.mempool_analyzer = EnhancedMempoolAnalyzer(trading_core=self.trading_core)
            self.price_monitor = RaydiumMonitor()
            
            # Initialize WebSocket client
            self.ws_client = HeliusWebSocketClient(
                trading_core=self.trading_core,
                impact_calculator=self.impact_calculator,
                mempool_analyzer=self.mempool_analyzer
            )
            
            # Log WebSocket initialization
            logger.info(f"Initializing WebSocket connection to {os.getenv('HELIUS_WS_URL')}")
            logger.info(f"Monitoring Raydium program: {os.getenv('RAYDIUM_PROGRAM_ID')}")
            
            # Set up subscription parameters
            subscription_params = {
                "jsonrpc": "2.0",
                "id": "raydium_sub",
                "method": "logsSubscribe",
                "params": [
                    {
                        "mentions": [os.getenv('RAYDIUM_PROGRAM_ID')],
                        "commitment": "processed",
                        "encoding": "base64"
                    }
                ]
            }
            
            logger.info(f"Setting up Raydium subscription: {subscription_params}")
            
            # Add connection handlers
            os.environ['WEBSOCKET_PING_INTERVAL'] = '10'
            os.environ['WEBSOCKET_RECONNECT_INTERVAL'] = '5'
            os.environ['WEBSOCKET_MAX_MESSAGES_PER_SECOND'] = '1000'
            
            # Start subscription
            self.trading_core.subscribe_to_mempool()
            
            logger.info("WebSocket subscription initialized - waiting for messages...")
            
            # Log GPU memory usage
            if torch.cuda.is_available():
                logger.info(f"GPU Memory allocated: {torch.cuda.memory_allocated() / 1024**2:.2f} MB")
                logger.info(f"GPU Memory cached: {torch.cuda.memory_reserved() / 1024**2:.2f} MB")
            
            # Register signal handlers
            signal.signal(signal.SIGINT, self._handle_shutdown)
            signal.signal(signal.SIGTERM, self._handle_shutdown)

            # Initialize pool states
            self._initialize_pool_states()

            logger.info("Trading system initialized successfully")

        except Exception as e:
            logger.error(f"Initialization failed: {str(e)}")
            raise

    def _setup_gpu(self):
        """Configure GPU settings for optimal performance"""
        if torch.cuda.is_available():
            torch.cuda.set_device(0)
            torch.backends.cudnn.benchmark = True
            torch.backends.cuda.matmul.allow_tf32 = True
            torch.backends.cudnn.allow_tf32 = True
            logger.info(f"GPU Initialized: {torch.cuda.get_device_name()}")

    def _initialize_pool_states(self):
        """Initialize pool states from environment variables"""
        try:
            sol_wsol_pool = {
                "amm_id": os.getenv('SOL_WSOL_AMM_ID'),
                "amm_authority": os.getenv('SOL_WSOL_AUTHORITY'),
                "amm_open_orders": os.getenv('SOL_WSOL_OPEN_ORDERS'),
                # ... (rest of the pool state variables)
            }
            
            self.trading_core.update_pool(sol_wsol_pool)
            logger.info("Pool states initialized successfully")
        
        except Exception as e:
            logger.error(f"Failed to initialize pool states: {str(e)}")
            raise

    async def start(self):
        """Start system components"""
        try:
            await self.system_manager.start()
            
            # Run core systems
            await asyncio.gather(
                self.ws_client.start(),
                self.price_monitor.start_monitoring(),
                self._monitor_system_health()
            )
        
        except Exception as e:
            logger.error(f"Startup failed: {str(e)}")
            await self.shutdown()

    async def _monitor_system_health(self):
    """Monitor system health and WebSocket data periodically"""
    last_msg_count = 0
    while True:
        try:
            health_ok = await self.system_manager.check_health()
            if not health_ok:
                logger.error("System health check failed")
                await self.system_manager.cleanup()
            
            # Monitor WebSocket activity
            current_msg_count = self.ws_client.get_message_count() if hasattr(self.ws_client, 'get_message_count') else 0
            msg_rate = current_msg_count - last_msg_count
            last_msg_count = current_msg_count
            
            logger.info(f"WebSocket Status - Messages/sec: {msg_rate}")
            
            # Check mempool analyzer stats
            if hasattr(self.mempool_analyzer, 'get_stats'):
                stats = self.mempool_analyzer.get_stats()
                logger.info(f"Mempool Stats: {stats}")
            
            await asyncio.sleep(5)  # Check more frequently
            
        except Exception as e:
            logger.error(f"Health monitoring error: {str(e)}")
            await asyncio.sleep(1)

    def _handle_shutdown(self, signum, frame):
        """Handle shutdown signals"""
        logger.info(f"Received shutdown signal: {signum}")
        asyncio.create_task(self.shutdown())

    async def shutdown(self):
       """Clean shutdown of components"""
        try:
            cleanup_tasks = [
                self.ws_client.cleanup(),
                self.system_manager.cleanup(),
                self.price_monitor.cleanup(),
                self.vector_store.cleanup(),
                self.mempool_analyzer.cleanup(),
                self.impact_calculator.cleanup()
            ]
            await asyncio.gather(*cleanup_tasks)
            logger.info("Bot shutdown complete")
    
        except Exception as e:
            logger.error(f"Shutdown failed: {str(e)}")
        finally:
            sys.exit(0)

    async def main():
        """Main entry point"""
    try:
        # Set event loop policy for Windows
        if sys.platform == 'win32':
            asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
        
        trader = RaydiumTrader()
        await trader.start()
        
    except KeyboardInterrupt:
        logger.info("Received keyboard interrupt, initiating shutdown...")
        if 'trader' in locals():
            await trader.shutdown()
    except Exception as e:
        logger.error(f"Fatal error: {str(e)}")
        sys.exit(1)

if __name__ == "__main__":
    asyncio.run(main())