use pyo3::prelude::*;
use pyo3::exceptions::PyValueError;
use pyo3_log;
use log::{info, warn, debug, error};
use serde::{Deserialize, Serialize};
use base58::FromBase58;

use solana_client::nonblocking::rpc_client::RpcClient;
use solana_client::rpc_config::RpcSendTransactionConfig;
use solana_sdk::{
    commitment_config::{CommitmentConfig, CommitmentLevel},
    compute_budget::ComputeBudgetInstruction,
    instruction::{AccountMeta, Instruction},
    message::Message,
    pubkey::Pubkey,
    signature::{Keypair, Signer},
    transaction::Transaction,
    system_instruction,
    program_error::ProgramError,
};
use spl_associated_token_account::get_associated_token_address;
use spl_token::instruction as token_instruction;
use spl_token::id as token_program_id;

use std::env;
use std::str::FromStr;
use std::sync::Arc;
use std::path::Path;
use std::collections::HashMap;
use std::time::{Duration, Instant};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::RwLock;

use tokio::time::sleep;
use anyhow::{Result, anyhow};

// Constants
const COMPUTE_UNITS: u32 = 50_000;
const PRIORITY_FEE: u64 = 750_000;
const LAMPORTS_PER_SOL: u64 = 1_000_000_000;

// Dynamic configuration structure
#[pyclass]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DynamicConfig {
    #[pyo3(get, set)]
    pub rpc_url: String,
    #[pyo3(get, set)]
    pub ws_url: String,
    #[pyo3(get, set)]
    pub api_key: String,
    
    #[pyo3(get, set)]
    pub sol_mint: String,
    #[pyo3(get, set)]
    pub token_mint: String, 
    #[pyo3(get, set)]
    pub amm_program: String,
    
    #[pyo3(get, set)]
    pub compute_limit: u32,
    #[pyo3(get, set)]
    pub priority_fee: u64,
    #[pyo3(get, set)]
    pub skip_preflight: bool,
    #[pyo3(get, set)]
    pub min_trade_interval: f64,
    #[pyo3(get, set)]
    pub execution_min_interval: f64,
    #[pyo3(get, set)]
    pub max_retries: u8,
    #[pyo3(get, set)]
    pub max_trades_per_minute: u32,
    #[pyo3(get, set)]
    pub max_active_mevs: u32,
    #[pyo3(get, set)]
    pub transactions_per_trade: u8,
    
    #[pyo3(get, set)]
    pub front_run_amount: f64,
    #[pyo3(get, set)]
    pub min_victim_size: f64,
    #[pyo3(get, set)]
    pub max_victim_size: f64,
    #[pyo3(get, set)]
    pub min_profit: f64,
    #[pyo3(get, set)]
    pub slippage_bps: u16,
    
    #[pyo3(get, set)]
    pub min_sol_balance: f64,
    #[pyo3(get, set)]
    pub wsol_target_ratio: f64,
    #[pyo3(get, set)]
    pub min_wsol_balance: f64,
    #[pyo3(get, set)]
    pub wsol_wrap_interval: u64,
    #[pyo3(get, set)]
    pub sandwich_min_victim_size: f64,
    #[pyo3(get, set)]
    pub sandwich_max_victim_size: f64,
    
    #[pyo3(get, set)]
    pub throttle_ms: u64,
    #[pyo3(get, set)]
    pub cooldown_after_error_ms: u64,
    #[pyo3(get, set)]
    pub cooldown_after_trade_ms: u64,
    
    #[pyo3(get, set)]
    pub amm_id: String,
    #[pyo3(get, set)]
    pub market_amm_authority: String,
    #[pyo3(get, set)]
    pub market_amm_open_orders: String,
    #[pyo3(get, set)]
    pub market_amm_target: String,
    #[pyo3(get, set)]
    pub market_pool_token_account: String,
    #[pyo3(get, set)]
    pub market_pool_pc_token_account: String,
    
    #[pyo3(get)]
    pub extra_settings: HashMap<String, String>,
}

#[pymethods]
impl DynamicConfig {
    #[new]
    #[allow(clippy::too_many_arguments)]
    fn new(
        rpc_url: Option<String>,
        ws_url: Option<String>,
        api_key: Option<String>,
        sol_mint: Option<String>,
        token_mint: Option<String>,
        amm_program: Option<String>,
        compute_limit: Option<u32>,
        priority_fee: Option<u64>,
        skip_preflight: Option<bool>,
        front_run_amount: Option<f64>,
        min_victim_size: Option<f64>,
        max_victim_size: Option<f64>,
        min_profit: Option<f64>,
        slippage_bps: Option<u16>,
        amm_id: Option<String>,
        market_amm_authority: Option<String>,
        market_amm_open_orders: Option<String>,
        market_amm_target: Option<String>,
        market_pool_token_account: Option<String>,
        market_pool_pc_token_account: Option<String>,
    ) -> Self {
        let mut config = Self::default();
        
        if let Some(val) = rpc_url { config.rpc_url = val; }
        if let Some(val) = ws_url { config.ws_url = val; }
        if let Some(val) = api_key { config.api_key = val; }
        if let Some(val) = sol_mint { config.sol_mint = val; }
        if let Some(val) = token_mint { config.token_mint = val; }
        if let Some(val) = amm_program { config.amm_program = val; }
        if let Some(val) = compute_limit { config.compute_limit = val; }
        if let Some(val) = priority_fee { config.priority_fee = val; }
        if let Some(val) = skip_preflight { config.skip_preflight = val; }
        if let Some(val) = front_run_amount { config.front_run_amount = val; }
        if let Some(val) = min_victim_size { config.min_victim_size = val; }
        if let Some(val) = max_victim_size { config.max_victim_size = val; }
        if let Some(val) = min_profit { config.min_profit = val; }
        if let Some(val) = slippage_bps { config.slippage_bps = val; }
        if let Some(val) = amm_id { config.amm_id = val; }
        if let Some(val) = market_amm_authority { config.market_amm_authority = val; }
        if let Some(val) = market_amm_open_orders { config.market_amm_open_orders = val; }
        if let Some(val) = market_amm_target { config.market_amm_target = val; }
        if let Some(val) = market_pool_token_account { config.market_pool_token_account = val; }
        if let Some(val) = market_pool_pc_token_account { config.market_pool_pc_token_account = val; }
        
        for (key, value) in env::vars() {
            config.extra_settings.insert(key, value);
        }
        
        config
    }
    
    fn update_extra_setting(&mut self, key: &str, value: &str) -> PyResult<()> {
        self.extra_settings.insert(key.to_string(), value.to_string());
        Ok(())
    }
    
    fn get_pubkey_str(&self, key: &str) -> PyResult<String> {
        let value = match key {
            "sol_mint" => &self.sol_mint,
            "token_mint" => &self.token_mint,
            "amm_program" => &self.amm_program,
            "amm_id" => &self.amm_id,
            "market_amm_authority" => &self.market_amm_authority,
            "market_amm_open_orders" => &self.market_amm_open_orders,
            "market_amm_target" => &self.market_amm_target,
            "market_pool_token_account" => &self.market_pool_token_account,
            "market_pool_pc_token_account" => &self.market_pool_pc_token_account,
            _ => {
                if let Some(value) = self.extra_settings.get(key) {
                    value
                } else {
                    return Err(PyValueError::new_err(format!("Key not found: {}", key)));
                }
            }
        };
        
        Ok(value.clone())
    }
    
    #[staticmethod]
    fn from_env() -> PyResult<Self> {
        let mut config = Self::default();
        
        for (key, value) in env::vars() {
            config.extra_settings.insert(key.clone(), value.clone());
            
            match key.as_str() {
                "RPC_URL" => config.rpc_url = value,
                "WS_URL" => config.ws_url = value,
                "API_KEY" => config.api_key = value,
                "SOL_MINT" => config.sol_mint = value,
                "TOKEN_MINT" => config.token_mint = value,
                "AMM_PROGRAM" => config.amm_program = value,
                "COMPUTE_LIMIT" => if let Ok(val) = value.parse() {
                    config.compute_limit = val;
                },
                "PRIORITY_FEE" => if let Ok(val) = value.parse() {
                    config.priority_fee = val;
                },
                "SKIP_PREFLIGHT" => if let Ok(val) = value.parse() {
                    config.skip_preflight = val;
                },
                "FRONT_RUN_AMOUNT" => if let Ok(val) = value.parse() {
                    config.front_run_amount = val;
                },
                "MIN_VICTIM_SIZE" => if let Ok(val) = value.parse() {
                    config.min_victim_size = val;
                },
                "MAX_VICTIM_SIZE" => if let Ok(val) = value.parse() {
                    config.max_victim_size = val;
                },
                "MIN_PROFIT" => if let Ok(val) = value.parse() {
                    config.min_profit = val;
                },
                "SLIPPAGE_BPS" => if let Ok(val) = value.parse() {
                    config.slippage_bps = val;
                },
                "AMM_ID" => config.amm_id = value,
                "MARKET_AMM_AUTHORITY" => config.market_amm_authority = value,
                "MARKET_AMM_OPEN_ORDERS" => config.market_amm_open_orders = value,
                "MARKET_AMM_TARGET" => config.market_amm_target = value,
                "MARKET_POOL_TOKEN_ACCOUNT" => config.market_pool_token_account = value,
                "MARKET_POOL_PC_TOKEN_ACCOUNT" => config.market_pool_pc_token_account = value,
                _ => {}
            }
        }
        
        Ok(config)
    }
    
    fn save_to_file(&self, path: &str) -> PyResult<()> {
        let json = serde_json::to_string_pretty(&self)
            .map_err(|e| PyValueError::new_err(format!("Failed to serialize config: {}", e)))?;
        
        std::fs::write(path, json)
            .map_err(|e| PyValueError::new_err(format!("Failed to write config file: {}", e)))?;
        
        Ok(())
    }
    
    #[staticmethod]
    fn load_from_file(path: &str) -> PyResult<Self> {
        let json = std::fs::read_to_string(path)
            .map_err(|e| PyValueError::new_err(format!("Failed to read config file: {}", e)))?;
        
        let config: Self = serde_json::from_str(&json)
            .map_err(|e| PyValueError::new_err(format!("Failed to parse config file: {}", e)))?;
        
        Ok(config)
    }
}

// Default implementation for DynamicConfig
impl Default for DynamicConfig {
    fn default() -> Self {
        Self {
            rpc_url: "https://winny-rychu7-fast-mainnet.helius-rpc.com".to_string(),
            ws_url: "wss://winny-rychu7-fast-mainnet.helius-rpc.com".to_string(),
            api_key: "YOUR_API_KEY_HERE".to_string(), // Replace with actual API key
            
            sol_mint: "So11111111111111111111111111111111111111112".to_string(),
            token_mint: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263".to_string(), // BONK by default
            amm_program: "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8".to_string(), // Raydium
            
            compute_limit: 50000,
            priority_fee: 750000,
            skip_preflight: true,
            min_trade_interval: 0.2,
            execution_min_interval: 0.1,
            max_retries: 3,
            max_trades_per_minute: 60,
            max_active_mevs: 1,
            transactions_per_trade: 2,
            
            front_run_amount: 0.35,
            min_victim_size: 5.0,
            max_victim_size: 320.0,
            min_profit: 0.002,
            slippage_bps: 800,
            
            min_sol_balance: 0.03,
            wsol_target_ratio: 0.95,
            min_wsol_balance: 0.02,
            wsol_wrap_interval: 30,
            sandwich_min_victim_size: 5.0,
            sandwich_max_victim_size: 320.0,
            
            throttle_ms: 100,
            cooldown_after_error_ms: 1000,
            cooldown_after_trade_ms: 200,
            
            amm_id: "58oQChx4yWmvKdwLLZzBi4ChoCc2fqCUWBkwMihLYQo2".to_string(),
            market_amm_authority: "5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1".to_string(),
            market_amm_open_orders: "HRk9CMrpq7Jn9sh7mzxE8CChHG8dneX9p475QKz4Fsfc".to_string(),
            market_amm_target: "DZnkkTmCiFWfYTfT41X3Rd1kDgozqzxWaHqsw6W4x2oe".to_string(),
            market_pool_token_account: "G5rZ4Qfv5SxruHQJpf8mYdVR8zaBEmr5rQQzYE3ga8Sw".to_string(),
            market_pool_pc_token_account: "6DfxGFP8TQnAqkVFEGX3pd5xFhGNkrLhwVR9MYrjpt6h".to_string(),
            
            extra_settings: HashMap::new(),
        }
    }
}

impl DynamicConfig {
    pub fn get_pubkey(&self, key: &str) -> Result<Pubkey> {
        let value = match key {
            "sol_mint" => &self.sol_mint,
            "token_mint" => &self.token_mint,
            "amm_program" => &self.amm_program,
            "amm_id" => &self.amm_id,
            "market_amm_authority" => &self.market_amm_authority,
            "market_amm_open_orders" => &self.market_amm_open_orders,
            "market_amm_target" => &self.market_amm_target,
            "market_pool_token_account" => &self.market_pool_token_account,
            "market_pool_pc_token_account" => &self.market_pool_pc_token_account,
            _ => {
                if let Some(value) = self.extra_settings.get(key) {
                    value
                } else {
                    return Err(anyhow!("Key not found: {}", key));
                }
            }
        };

        Pubkey::from_str(value).map_err(|e| anyhow!("Invalid pubkey for {}: {}", key, e))
    }
}

// Struct for trade parameters
#[pyclass]
#[derive(Clone)]
pub struct TradeParams {
    #[pyo3(get, set)]
    pub accounts: Vec<String>,
    #[pyo3(get, set)]
    pub amount_in: Option<u64>,
    #[pyo3(get, set)]
    pub is_buy: bool,
    #[pyo3(get, set)]
    pub reuse_blockhash: bool,
    #[pyo3(get, set)]
    pub token_a_mint: String,
    #[pyo3(get, set)]
    pub token_b_mint: String,
    #[pyo3(get, set)]
    pub sell_all: bool,
    #[pyo3(get, set)]
    pub victim_data: Option<Vec<u8>>,
    #[pyo3(get, set)]
    pub slippage_bps: u16,
    #[pyo3(get, set)]
    pub priority_fee: u64,
}

#[pymethods]
impl TradeParams {
    #[new]
    #[pyo3(signature = (
        accounts,
        amount_in=None,
        is_buy=false,
        reuse_blockhash=false,
        token_a_mint="".to_string(),
        token_b_mint="".to_string(),
        sell_all=false,
        victim_data=None,
        slippage_bps=800,
        priority_fee=750000,
    ))]
    fn new(
        accounts: Vec<String>,
        amount_in: Option<u64>,
        is_buy: bool,
        reuse_blockhash: bool,
        token_a_mint: String,
        token_b_mint: String,
        sell_all: bool,
        victim_data: Option<Vec<u8>>,
        slippage_bps: u16,
        priority_fee: u64,
    ) -> Self {
        Self {
            accounts,
            amount_in,
            is_buy,
            reuse_blockhash,
            token_a_mint,
            token_b_mint,
            sell_all,
            victim_data,
            slippage_bps,
            priority_fee,
        }
    }
}

// Trading core class
#[pyclass]
pub struct TradingCore {
    client: Arc<RpcClient>,
    keypair: Arc<Keypair>,
    config: Arc<RwLock<DynamicConfig>>,
    ml_signals: Arc<RwLock<HashMap<String, f64>>>,
    balances: Arc<RwLock<(f64, f64)>>, // (SOL, WSOL)
    last_trade_time: Arc<AtomicU64>,
}

impl TradingCore {
    /// Load keypair from file
    fn load_keypair(path: &str) -> Result<Keypair> {
        if !Path::new(path).exists() {
            return Err(anyhow!("Keypair file not found: {}", path));
        }

        if path.ends_with(".json") {
            let file_content = std::fs::read_to_string(path)?;
            let bytes: Vec<u8> = serde_json::from_str(&file_content)?;
            Keypair::from_bytes(&bytes).map_err(|e| anyhow!("Invalid keypair: {}", e))
        } else {
            let bytes = std::fs::read(path)?;
            Keypair::from_bytes(&bytes).map_err(|e| anyhow!("Invalid keypair: {}", e))
        }
    }

    /// Load keypair from environment variables
    fn load_keypair_from_env() -> Result<Keypair> {
        let private_key = env::var("SOLANA_PRIVATE_KEY")
            .map_err(|_| anyhow!("SOLANA_PRIVATE_KEY not found in environment"))?;
        
        let secret_key_bytes = private_key.from_base58()
            .map_err(|e| anyhow!("Invalid private key encoding: {:?}", e))?;

        Keypair::from_bytes(&secret_key_bytes)
            .map_err(|e| anyhow!("Invalid keypair: {}", e))
    }

    /// Refresh SOL and WSOL balances
    async fn refresh_balances(&self) -> Result<()> {
        let wsol_pubkey = {
            let config = self.config.read().map_err(|_| anyhow!("Failed to read config"))?;
            config.get_pubkey("sol_mint")?
        };
        
        let sol_balance = self.client.get_balance(&self.keypair.pubkey()).await?;
        let sol_balance_f64 = sol_balance as f64 / LAMPORTS_PER_SOL as f64;
        
        let wsol_ata = get_associated_token_address(&self.keypair.pubkey(), &wsol_pubkey);
        
        let wsol_balance_f64 = match self.client.get_token_account_balance(&wsol_ata).await {
            Ok(balance) => match balance.amount.parse::<u64>() {
                Ok(amount) => amount as f64 / LAMPORTS_PER_SOL as f64,
                Err(_) => 0.0,
            },
            Err(_) => 0.0, // ATA might not exist yet
        };
        
        if let Ok(mut balances) = self.balances.write() {
            *balances = (sol_balance_f64, wsol_balance_f64);
        }
        
        Ok(())
    }
    
    /// Ensure Associated Token Account (ATA) exists
    async fn ensure_ata_exists(&self, owner: &Pubkey, mint: &Pubkey) -> Result<Pubkey> {
        let ata = get_associated_token_address(owner, mint);

        match self.client.get_account(&ata).await {
            Ok(_) => {
                debug!("ATA already exists: {}", ata);
                Ok(ata)
            }
            Err(_) => {
                info!("ATA does not exist. Creating ATA for mint: {}", mint);

                let ix = spl_associated_token_account::instruction::create_associated_token_account_idempotent(
                    &self.keypair.pubkey(), // Funding account (fee payer)
                    owner,                  // Wallet address for new ATA
                    mint,                   // Token mint address
                    &spl_token::id(),       // Token program ID
                );

                let blockhash = self.client.get_latest_blockhash().await?;

                let mut tx = Transaction::new_with_payer(&[ix], Some(&self.keypair.pubkey()));
                tx.sign(&[self.keypair.as_ref()], blockhash);

                let config = RpcSendTransactionConfig {
                    skip_preflight: true,
                    preflight_commitment: Some(CommitmentLevel::Confirmed),
                    encoding: None,
                    max_retries: Some(3),
                    min_context_slot: None,
                };

                match self.client.send_transaction_with_config(&tx, config).await {
                    Ok(_) => {
                        info!("ATA created successfully: {}", ata);
                        Ok(ata)
                    },
                    Err(e) => {
                        warn!("ATA creation failed with error: {:?}", e);
                        match self.client.get_account(&ata).await {
                            Ok(_) => {
                                info!("ATA exists after all (concurrent creation): {}", ata);
                                Ok(ata)
                            },
                            Err(_) => {
                                Err(anyhow!("Failed to create ATA: {:?}", e))
                            }
                        }
                    }
                }
            }
        }
    }

    /// Create swap instruction for Raydium
    fn create_raydium_swap_instruction(
        &self,
        raydium_program_id: &Pubkey,
        user_source_token: &Pubkey,
        user_destination_token: &Pubkey,
        amm_id: &Pubkey,  
        amm_authority: &Pubkey,
        amm_open_orders: &Pubkey,
        amm_target: &Pubkey,
        pool_token_account: &Pubkey,
        pool_pc_account: &Pubkey,
        amount: u64,
        is_buy: bool,
    ) -> Instruction {
        let mut data = vec![if is_buy { 9u8 } else { 11u8 }]; // 9 = buy, 11 = sell
        data.extend_from_slice(&amount.to_le_bytes());
        
        let account_metas = vec![
            AccountMeta::new_readonly(*raydium_program_id, false),
            AccountMeta::new_readonly(*amm_authority, false),
            AccountMeta::new(*amm_open_orders, false),
            AccountMeta::new(*amm_target, false),
            AccountMeta::new(*pool_token_account, false),
            AccountMeta::new(*pool_pc_account, false),
            AccountMeta::new(*user_source_token, false),
            AccountMeta::new(*user_destination_token, false),
            AccountMeta::new_readonly(self.keypair.pubkey(), true),
            AccountMeta::new_readonly(spl_token::id(), false),
        ];

        Instruction {
            program_id: *raydium_program_id,
            accounts: account_metas,
            data,
        }
    }
}

#[pymethods]
impl TradingCore {
    #[new]
    #[pyo3(signature = (config, keypair_path))]
    fn new(config: DynamicConfig, keypair_path: &str) -> PyResult<Self> {
        let rpc_url = if config.rpc_url.contains('?') {
            format!("{}&api-key={}", config.rpc_url, config.api_key)
        } else {
            format!("{}?api-key={}", config.rpc_url, config.api_key)
        };
        
        debug!("Using RPC URL: {}", rpc_url.split('?').next().unwrap_or(&rpc_url));
        
        let client = RpcClient::new_with_commitment(rpc_url, CommitmentConfig::confirmed());
        
        let keypair = match Self::load_keypair(keypair_path) {
            Ok(kp) => kp,
            Err(e) => {
                warn!("Failed to load keypair from file: {}", e);
                Self::load_keypair_from_env().map_err(|e| {
                    PyValueError::new_err(format!("Failed to load keypair from file or environment: {} / {}", e, e))
                })?
            }
        };
        
        info!("Loaded keypair with public key: {}", keypair.pubkey());
        
        let core = Self {
            client: Arc::new(client),
            keypair: Arc::new(keypair),
            config: Arc::new(RwLock::new(config)),
            ml_signals: Arc::new(RwLock::new(HashMap::new())),
            balances: Arc::new(RwLock::new((0.0, 0.0))),
            last_trade_time: Arc::new(AtomicU64::new(0)),
        };
        
        Ok(core)
    }

    /// Get public key as string
    fn get_public_key(&self) -> String {
        self.keypair.pubkey().to_string()
    }
    
    /// Update ML signal
    fn update_ml_signal(&self, key: &str, value: f64) -> PyResult<()> {
        if let Ok(mut signals) = self.ml_signals.write() {
            signals.insert(key.to_string(), value);
            Ok(())
        } else {
            Err(PyValueError::new_err("Failed to update ML signal"))
        }
    }
    
    /// Get ML signal
    fn get_ml_signal(&self, key: &str, default: f64) -> f64 {
        if let Ok(signals) = self.ml_signals.read() {
            *signals.get(key).unwrap_or(&default)
        } else {
            default
        }
    }

    /// Get SOL and WSOL balances
    fn get_balances<'py>(&self, py: Python<'py>) -> PyResult<&'py PyAny> {
        let client = self.client.clone();
        let keypair_pubkey = self.keypair.pubkey();
        let balances_arc = self.balances.clone();
        
        // Extract wsol_pubkey from config before any await points
        let wsol_pubkey_str = {
            match self.config.read() {
                Ok(config) => config.sol_mint.clone(),
                Err(_) => return Err(PyValueError::new_err("Failed to read config"))
            }
        };
        
        let wsol_pubkey = match Pubkey::from_str(&wsol_pubkey_str) {
            Ok(pk) => pk,
            Err(e) => return Err(PyValueError::new_err(format!("Invalid WSOL pubkey: {}", e)))
        };
        
        let fut = async move {
            let sol_balance = match client.get_balance(&keypair_pubkey).await {
                Ok(balance) => balance,
                Err(e) => return Err(PyValueError::new_err(format!("Failed to get SOL balance: {}", e)))
            };
            
            let sol_balance_f64 = sol_balance as f64 / LAMPORTS_PER_SOL as f64;
            
            let wsol_ata = get_associated_token_address(&keypair_pubkey, &wsol_pubkey);
            
            let wsol_balance_f64 = match client.get_token_account_balance(&wsol_ata).await {
                Ok(balance) => {
                    match balance.amount.parse::<u64>() {
                        Ok(amount) => amount as f64 / LAMPORTS_PER_SOL as f64,
                        Err(_) => 0.0,
                    }
                },
                Err(_) => 0.0, // ATA might not exist yet
            };
            
            // Update stored balances
            if let Ok(mut balances) = balances_arc.write() {
                *balances = (sol_balance_f64, wsol_balance_f64);
            }
            
            Ok((sol_balance_f64, wsol_balance_f64))
        };
        
        pyo3_asyncio::tokio::future_into_py(py, fut)
    }

    /// Execute trade
    fn execute_trade<'py>(
        &self,
        py: Python<'py>,
        token_mint: &str,
        sol_mint: &str,
        amount_in: f64,
        is_buy: bool,
        slippage_bps: Option<u16>,
        priority_fee: Option<u64>,
    ) -> PyResult<&'py PyAny> {
        // Relevant variables and extraction
        let token_mint_str = token_mint.to_string();
        let sol_mint_str = sol_mint.to_string();
        let amount_lamports = (amount_in * LAMPORTS_PER_SOL as f64) as u64;
        let client = self.client.clone();
        let keypair = self.keypair.clone();
        let last_trade_time = self.last_trade_time.clone();
        
        // Fetch config values
        let (slippage, priority, min_interval_ms, skip_preflight, max_retries, compute_limit) = {
            match self.config.read() {
                Ok(config) => {
                    let slippage_val = slippage_bps.unwrap_or(config.slippage_bps);
                    let priority_val = priority_fee.unwrap_or(config.priority_fee);
                    let min_interval = (config.min_trade_interval * 1000.0) as u64;
                    let skip_preflight = config.skip_preflight;
                    let max_retries = config.max_retries;
                    let compute_limit = config.compute_limit;
                    (slippage_val, priority_val, min_interval, skip_preflight, max_retries, compute_limit)
                },
                Err(_) => return Err(PyValueError::new_err("Failed to read config"))
            }
        };
        
        let amm_params = {
            match self.config.read() {
                Ok(config) => {
                    let raydium_program_id_str = config.amm_program.clone();
                    let amm_id_str = config.amm_id.clone();
                    let amm_authority_str = config.market_amm_authority.clone();
                    let amm_open_orders_str = config.market_amm_open_orders.clone();
                    let amm_target_str = config.market_amm_target.clone();
                    let pool_token_account_str = config.market_pool_token_account.clone();
                    let pool_pc_account_str = config.market_pool_pc_token_account.clone();
                    
                    (
                        raydium_program_id_str,
                        amm_id_str,
                        amm_authority_str,
                        amm_open_orders_str,
                        amm_target_str,
                        pool_token_account_str,
                        pool_pc_account_str,
                    )
                },
                Err(_) => return Err(PyValueError::new_err("Failed to read config"))
            }
        };

        let fut = async move {
            // Convert token mints to pubkeys
            let token_pubkey = match Pubkey::from_str(&token_mint_str) {
                Ok(pubkey) => pubkey,
                Err(e) => return Err(PyValueError::new_err(format!("Invalid token mint: {}", e)))
            };
            
            let sol_pubkey = match Pubkey::from_str(&sol_mint_str) {
                Ok(pubkey) => pubkey,
                Err(e) => return Err(PyValueError::new_err(format!("Invalid SOL mint: {}", e)))
            };
            
            // Enforce minimum interval between trades
            let now = Instant::now().elapsed().as_millis() as u64;
            let last = last_trade_time.load(Ordering::Relaxed);
            let elapsed = now.saturating_sub(last);
            
            if elapsed < min_interval_ms {
                let wait_ms = min_interval_ms - elapsed;
                sleep(Duration::from_millis(wait_ms)).await;
            }

            // Get Raydium program ID
            let raydium_program_id = match Pubkey::from_str(&amm_params.0) {
                Ok(pubkey) => pubkey,
                Err(e) => return Err(PyValueError::new_err(format!("Failed to get AMM program ID: {}", e)))
            };
            
            // User's ATAs
            let user_wsol_ata = get_associated_token_address(&keypair.pubkey(), &sol_pubkey);
            let user_token_ata = get_associated_token_address(&keypair.pubkey(), &token_pubkey);
            
            // Ensure ATAs exist
            let mut instructions = Vec::new();
            
            // Create WSOL ATA if needed
            let wsol_account_exists = client.get_account(&user_wsol_ata).await.is_ok();
            if !wsol_account_exists {
                let create_wsol_ata_ix = spl_associated_token_account::instruction::create_associated_token_account_idempotent(
                    &keypair.pubkey(),
                    &keypair.pubkey(),
                    &sol_pubkey,
                    &token_program_id(),
                );
                instructions.push(create_wsol_ata_ix);
            }
            
            // Create token ATA if needed
            let token_account_exists = client.get_account(&user_token_ata).await.is_ok();
            if !token_account_exists {
                let create_token_ata_ix = spl_associated_token_account::instruction::create_associated_token_account_idempotent(
                    &keypair.pubkey(),
                    &keypair.pubkey(),
                    &token_pubkey,
                    &token_program_id(),
                );
                instructions.push(create_token_ata_ix);
            }

            // Wrap SOL if buying
            if is_buy && amount_lamports > 0 {
                let transfer_ix = system_instruction::transfer(
                    &keypair.pubkey(),
                    &user_wsol_ata,
                    amount_lamports,
                );
                instructions.push(transfer_ix);

                let sync_ix = match token_instruction::sync_native(&token_program_id(), &user_wsol_ata) {
                    Ok(ix) => ix,
                    Err(e) => return Err(PyValueError::new_err(format!("Failed to create sync instruction: {}", e)))
                };
                instructions.push(sync_ix);
            }

            // Determine the actual amount to use for selling
            let actual_amount = if !is_buy && amount_lamports == 0 {
                match client.get_token_account_balance(&user_token_ata).await {
                    Ok(balance) => {
                        match balance.amount.parse::<u64>() {
                            Ok(amount) => amount,
                            Err(e) => return Err(PyValueError::new_err(format!("Failed to parse token balance: {}", e)))
                        }
                    },
                    Err(e) => return Err(PyValueError::new_err(format!("Failed to get token balance: {}", e)))
                }
            } else {
                amount_lamports
            };

            // Get AMM parameters
            let amm_id = match Pubkey::from_str(&amm_params.1) {
                Ok(pubkey) => pubkey,
                Err(e) => return Err(PyValueError::new_err(format!("Invalid AMM ID: {}", e)))
            };
            
            let amm_authority = match Pubkey::from_str(&amm_params.2) {
                Ok(pubkey) => pubkey,
                Err(e) => return Err(PyValueError::new_err(format!("Invalid AMM authority: {}", e)))
            };
            
            let amm_open_orders = match Pubkey::from_str(&amm_params.3) {
                Ok(pubkey) => pubkey,
                Err(e) => return Err(PyValueError::new_err(format!("Invalid AMM open orders: {}", e)))
            };
            
            let amm_target = match Pubkey::from_str(&amm_params.4) {
                Ok(pubkey) => pubkey,
                Err(e) => return Err(PyValueError::new_err(format!("Invalid AMM target: {}", e)))
            };
            
            let pool_token_account = match Pubkey::from_str(&amm_params.5) {
                Ok(pubkey) => pubkey,
                Err(e) => return Err(PyValueError::new_err(format!("Invalid pool token account: {}", e)))
            };
            
            let pool_pc_account = match Pubkey::from_str(&amm_params.6) {
                Ok(pubkey) => pubkey,
                Err(e) => return Err(PyValueError::new_err(format!("Invalid pool PC account: {}", e)))
            };
            
            // Create the swap instruction
            let swap_ix = match is_buy {
                true => self.create_raydium_swap_instruction(
                    &raydium_program_id,
                    &user_wsol_ata,     // Source: WSOL
                    &user_token_ata,    // Destination: Token
                    &amm_id,
                    &amm_authority,
                    &amm_open_orders,
                    &amm_target,
                    &pool_token_account,
                    &pool_pc_account,
                    actual_amount,
                    true,
                ),
                false => self.create_raydium_swap_instruction(
                    &raydium_program_id,
                    &user_token_ata,    // Source: Token
                    &user_wsol_ata,     // Destination: WSOL
                    &amm_id,
                    &amm_authority,
                    &amm_open_orders,
                    &amm_target,
                    &pool_token_account,
                    &pool_pc_account,
                    actual_amount,
                    false,
                ),
            };
            
            instructions.push(swap_ix);
            
            // Add compute budget instructions
            let compute_limit_ix = ComputeBudgetInstruction::set_compute_unit_limit(compute_limit);
            let compute_budget_ix = ComputeBudgetInstruction::set_compute_unit_price(priority);
            
            instructions.insert(0, compute_budget_ix);
            instructions.insert(0, compute_limit_ix);
            
            // Create transaction
            let blockhash = match client.get_latest_blockhash().await {
                Ok(blockhash) => blockhash,
                Err(e) => return Err(PyValueError::new_err(format!("Failed to get blockhash: {}", e)))
            };
            
            let mut tx = Transaction::new_with_payer(&instructions, Some(&keypair.pubkey()));
            tx.sign(&[keypair.as_ref()], blockhash);
            
            // Send the transaction
            let signature = match client.send_transaction_with_config(
                &tx,
                RpcSendTransactionConfig {
                    skip_preflight,
                    preflight_commitment: Some(CommitmentLevel::Processed),
                    encoding: None,
                    max_retries: Some(max_retries as usize),
                    min_context_slot: None,
                },
            ).await {
                Ok(sig) => sig,
                Err(e) => return Err(PyValueError::new_err(format!("Transaction failed: {}", e)))
            };
            
            // Update last trade time
            last_trade_time.store(Instant::now().elapsed().as_millis() as u64, Ordering::Relaxed);
            
            Ok(signature.to_string())
        };

        pyo3_asyncio::tokio::future_into_py(py, fut)
    }

    /// Execute trade with TradeParams (for advanced usage)
    fn execute_trade_py<'py>(
        &self,
        py: Python<'py>,
        params: &TradeParams,
    ) -> PyResult<&'py PyAny> {
        let params_clone = params.clone();
        let client = self.client.clone();
        let keypair = self.keypair.clone();
        let last_trade_time = self.last_trade_time.clone();

        let fut = async move {
            let mut accounts: Vec<Pubkey> = Vec::with_capacity(params_clone.accounts.len());
            for (idx, acc) in params_clone.accounts.iter().enumerate() {
                let pubkey = match Pubkey::from_str(acc) {
                    Ok(pk) => pk,
                    Err(e) => return Err(PyValueError::new_err(format!("Invalid account address at index {}: {}", idx, e)))
                };
                accounts.push(pubkey);
            }

            let token_a_mint = match Pubkey::from_str(&params_clone.token_a_mint) {
                Ok(pk) => pk,
                Err(e) => return Err(PyValueError::new_err(format!("Invalid token A mint: {}", e)))
            };
            
            let token_b_mint = match Pubkey::from_str(&params_clone.token_b_mint) {
                Ok(pk) => pk,
                Err(e) => return Err(PyValueError::new_err(format!("Invalid token B mint: {}", e)))
            };

            let self_temp = TradingCore {
                client: client.clone(),
                keypair: keypair.clone(),
                config: Arc::new(RwLock::new(DynamicConfig::default())), // Dummy for this scope
                ml_signals: Arc::new(RwLock::new(HashMap::new())),       // Dummy for this scope
                balances: Arc::new(RwLock::new((0.0, 0.0))),             // Dummy for this scope
                last_trade_time: last_trade_time.clone(),
            };

            let source_ata = if params_clone.is_buy {
                match self_temp.ensure_ata_exists(&keypair.pubkey(), &token_b_mint).await {
                    Ok(ata) => ata,
                    Err(e) => return Err(PyValueError::new_err(format!("Failed to ensure source ATA: {}", e)))
                }
            } else {
                match self_temp.ensure_ata_exists(&keypair.pubkey(), &token_a_mint).await {
                    Ok(ata) => ata,
                    Err(e) => return Err(PyValueError::new_err(format!("Failed to ensure source ATA: {}", e)))
                }
            };

            let destination_ata = if params_clone.is_buy {
                match self_temp.ensure_ata_exists(&keypair.pubkey(), &token_a_mint).await {
                    Ok(ata) => ata,
                    Err(e) => return Err(PyValueError::new_err(format!("Failed to ensure destination ATA: {}", e)))
                }
            } else {
                match self_temp.ensure_ata_exists(&keypair.pubkey(), &token_b_mint).await {
                    Ok(ata) => ata,
                    Err(e) => return Err(PyValueError::new_err(format!("Failed to ensure destination ATA: {}", e)))
                }
            };

            let mut instructions = vec![];

            // Add compute budget instructions
            instructions.push(ComputeBudgetInstruction::set_compute_unit_limit(COMPUTE_UNITS));
            instructions.push(ComputeBudgetInstruction::set_compute_unit_price(params_clone.priority_fee));

            // Determine trade amount
            let amount = if params_clone.sell_all {
                0  // Use 0 for "sell all"
            } else if let Some(amount) = params_clone.amount_in {
                amount
            } else {
                return Err(PyValueError::new_err("No amount specified for swap"));
            };

            // Create swap instruction
            if accounts.len() >= 1 {
                let program_id = &accounts[0];
                
                let raydium_swap_ix = self_temp.create_raydium_swap_instruction(
                    program_id,
                    &source_ata,
                    &destination_ata,
                    &accounts.get(1).unwrap_or(&keypair.pubkey()), // AMM ID or fallback
                    &accounts.get(2).unwrap_or(&keypair.pubkey()), // AMM Authority or fallback
                    &accounts.get(3).unwrap_or(&keypair.pubkey()), // AMM Open Orders or fallback
                    &accounts.get(4).unwrap_or(&keypair.pubkey()), // AMM Target or fallback
                    &accounts.get(5).unwrap_or(&keypair.pubkey()), // Pool Token Account or fallback
                    &accounts.get(6).unwrap_or(&keypair.pubkey()), // Pool PC Account or fallback
                    amount,
                    params_clone.is_buy,
                );

                instructions.push(raydium_swap_ix);
            } else {
                return Err(PyValueError::new_err("Not enough accounts provided for Raydium swap"))
            }

            // Get recent blockhash
            let blockhash = match client.get_latest_blockhash().await {
                Ok(bh) => bh,
                Err(e) => return Err(PyValueError::new_err(format!("Failed to get blockhash: {}", e)))
            };

            // Create transaction message
            let message = Message::new_with_blockhash(
                &instructions, 
                Some(&keypair.pubkey()), 
                &blockhash,
            );
            
            // Create and sign the transaction
            let mut tx = Transaction::new_unsigned(message);
            match tx.try_sign(&[keypair.as_ref()], blockhash) {
                Ok(_) => {},
                Err(e) => return Err(PyValueError::new_err(format!("Transaction signing failed: {:?}", e)))
            }

            // Send transaction
            let signature = match client.send_transaction_with_config(
                &tx,
                RpcSendTransactionConfig {
                    skip_preflight: true,
                    preflight_commitment: Some(CommitmentLevel::Processed),
                    encoding: None,
                    max_retries: None,
                    min_context_slot: None,
                },
            ).await {
                Ok(sig) => sig,
                Err(e) => return Err(PyValueError::new_err(format!("Transaction failed: {}", e)))
            };
            
            // Update last trade time
            last_trade_time.store(
                Instant::now().elapsed().as_millis() as u64, 
                Ordering::Relaxed
            );

            Ok(signature.to_string())
        };

        pyo3_asyncio::tokio::future_into_py(py, fut)
    }

    /// Get available keypair paths for searching
    #[staticmethod]
    fn get_keypair_paths() -> Vec<String> {
        vec![
            "keypair.bin".to_string(),
            "id.json".to_string(),
            "../id.json".to_string(),
            "C:/solana_rust_bot/keypair.bin".to_string(),
            "C:/solana_rust_bot/id.json".to_string(),
        ]
    }
}

// Module configuration for Python
#[pymodule]
fn solana_rust_bot(_py: Python, m: &PyModule) -> PyResult<()> {
    pyo3_log::init();
    
    m.add_class::<DynamicConfig>()?;
    m.add_class::<TradingCore>()?;
    m.add_class::<TradeParams>()?;
    
    Ok(())
} do yo u see this script here???