can you do me a big big favor and write this properly with no indentation issues please please use pyo3::prelude::*;
use solana_sdk::{
    pubkey::Pubkey,
    instruction::{AccountMeta, Instruction},
    transaction::Transaction,
    signer::Signer,
    commitment_config::CommitmentConfig,
    compute_budget::ComputeBudgetInstruction,
};
use solana_client::{
    rpc_client::RpcClient,
    rpc_config::RpcSendTransactionConfig,
};
use tokio::sync::Mutex;
use std::{
    sync::Arc,
    collections::HashMap,
    str::FromStr,
    time::{Instant, Duration},
    env,
};
use serde::Deserialize;
use thiserror::Error;
use log::{error, warn, debug};
use solana_program::program_error::ProgramError;

// Constants
const RAYDIUM_V4: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";
const LAMPORTS_PER_SOL: u64 = 1_000_000_000;
const TOKEN_PROGRAM: &str = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";

#[pyclass]
#[derive(Clone, Debug)]
pub struct AccountMetaWrapper {
    #[pyo3(get)]
    pub pubkey: String,
    #[pyo3(get)]
    pub is_signer: bool,
    #[pyo3(get)]
    pub is_writable: bool,
}

impl From<AccountMeta> for AccountMetaWrapper {
    fn from(meta: AccountMeta) -> Self {
        Self {
            pubkey: meta.pubkey.to_string(),
            is_signer: meta.is_signer,
            is_writable: meta.is_writable,
        }
    }
}

#[derive(Error, Debug)]
pub enum TradingError {
    #[error("RPC error: {0}")]
    RpcError(#[from] solana_client::client_error::ClientError),
    #[error("Pool error: {0}")]
    PoolError(String),
    #[error("Execution error: {0}")]
    ExecutionError(String),
    #[error("Insufficient balance: {0}")]
    InsufficientBalance(f64),
    #[error("Token error: {0}")]
    TokenError(#[from] spl_token::error::TokenError),
    #[error("Program error: {0}")]
    ProgramError(#[from] ProgramError),
    #[error("Lock error: {0}")]
    LockError(String),
    #[error("Environment error: {0}")]
    EnvError(String),
}

#[pyclass]
#[derive(Deserialize, Clone, Debug)]
pub struct TradeParams {
    #[pyo3(get, set)]
    pool_address: String,
    #[pyo3(get, set)]
    amount_in: f64,
    #[pyo3(get, set)]
    min_amount_out: f64,
    #[pyo3(get, set)]
    skip_preflight: Option<bool>,
}

#[pymethods]
impl TradeParams {
    #[new]
    fn new(pool_address: String, amount_in: f64, min_amount_out: f64) -> Self {
        Self {
            pool_address,
            amount_in,
            min_amount_out,
            skip_preflight: None,
        }
    }
}

#[pyclass]
#[derive(Clone, Debug)]
pub struct PoolInfo {
    #[pyo3(get)]
    pub base_reserve: u64,
    #[pyo3(get)]
    pub quote_reserve: u64,
    #[pyo3(get)]
    pub last_update: u64,
    #[pyo3(get)]
    pub token_mint: String,
}

#[pyclass]
#[derive(Clone, Debug)]
pub struct TokenPools {
    #[pyo3(get)]
    pub direct_pools: Vec<String>,
    #[pyo3(get)]
    pub intermediate_tokens: Vec<String>,
}

#[derive(Debug, Clone)]
struct PoolState {
    amm_id: Pubkey,
    authority: Pubkey,
    open_orders: Pubkey,
    target_orders: Pubkey,
    base_vault: Pubkey,
    quote_vault: Pubkey,
    market: Pubkey,
    bids: Pubkey,
    asks: Pubkey,
    event_queue: Pubkey,
    vault_signer: Pubkey,
    token_mint: Pubkey,
    last_update: Instant,
}

#[pyclass]
#[derive(Deserialize, Clone, Debug)]
pub struct TradingConfig {
    #[pyo3(get, set)]
    pub compute_limit: u32,
    #[pyo3(get, set)]
    pub priority_fee: u64,
    #[pyo3(get, set)]
    pub max_priority_fee: u64,
    #[pyo3(get, set)]
    pub slippage: f64,
    #[pyo3(get, set)]
    pub retry_count: u32,
    #[pyo3(get, set)]
    pub position_size: f64,
    #[pyo3(get, set)]
    pub max_position_size: f64,
    #[pyo3(get, set)]
    pub gas_reserve: f64,
    #[pyo3(get, set)]
    pub skip_preflight: bool,
}

impl TradingConfig {
    fn validate(&self) -> PyResult<()> {
        if self.compute_limit == 0 || self.compute_limit > 1_400_000 {
            return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
                "Invalid compute limit (must be between 1 and 1,400,000)",
            ));
        }
        if self.priority_fee > self.max_priority_fee {
            return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
                "Priority fee cannot exceed max priority fee",
            ));
        }
        if self.slippage <= 0.0 || self.slippage > 0.1 {
            return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
                "Invalid slippage (must be between 0 and 0.1)",
            ));
        }
        Ok(())
    }
}

impl Default for TradingConfig {
    fn default() -> Self {
        Self {
            compute_limit: 400_000,
            priority_fee: 500_000,
            max_priority_fee: 1_000_000,
            slippage: 0.01,
            retry_count: 3,
            position_size: 0.005,
            max_position_size: 0.15,
            gas_reserve: 0.05,
            skip_preflight: false,
        }
    }
}

#[pyclass]
pub struct TradingCore {
    client: Arc<RpcClient>,
    keypair: Arc<solana_sdk::signature::Keypair>,
    config: Arc<Mutex<TradingConfig>>,
    pool_cache: Arc<Mutex<HashMap<String, PoolState>>>,
}

#[pymethods]
impl TradingCore {
    #[new]
    fn new(rpc_url: String, config_json: String) -> PyResult<Self> {
        // Initialize from environment variables
        let secret_key = env::var("SOLANA_PRIVATE_KEY")
            .map_err(|e| {
                error!("SOLANA_PRIVATE_KEY not found: {}", e);
                PyErr::new::<pyo3::exceptions::PyValueError, _>("SOLANA_PRIVATE_KEY not set in environment")
            })?;

        let config: TradingConfig = serde_json::from_str(&config_json)
            .map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(e.to_string()))?;

        config.validate()?;

        // Initialize RPC client with confirmed commitment
        let client = Arc::new(RpcClient::new_with_commitment(rpc_url, CommitmentConfig::confirmed()));

        // Create keypair from private key
        let keypair_bytes = bs58::decode(secret_key)
            .into_vec()
            .map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(e.to_string()))?;

        let keypair = Arc::new(solana_sdk::signature::Keypair::from_bytes(&keypair_bytes)
            .map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(e.to_string()))?);

        Ok(Self {
            client,
            keypair,
            config: Arc::new(Mutex::new(config)),
            pool_cache: Arc::new(Mutex::new(HashMap::new())),
        })
    }
    async def monitor_mempool(self):
        """Monitor mempool for opportunities"""
        logger.info("Starting mempool monitoring...")

        while self.active:
            try:
                if not self.trading_active:
                    await asyncio.sleep(1)
                    continue

                async with websockets.connect(
                    self.config['rpc']['helius']['websocket'],
                    ping_interval=30,
                    ping_timeout=10
                ) as ws:
                    await ws.send(json.dumps({
                        "jsonrpc": "2.0",
                        "id": 1,
                        "method": "logsSubscribe",
                        "params": [
                            {"mentions": [self.config['programs']['raydium']]},
                            {"commitment": "processed"}
                        ]
                    }))
                
                    while self.active:
                        msg = await ws.recv()
                        opportunity = await self.parser.parse_log(msg)
                    
                        if opportunity and await self.parser.validate_opportunity(opportunity):
                            # Execute trade using environment variable key
                            await self.execute_trade(opportunity)
                            
            except websockets.exceptions.ConnectionClosed:
                if self.active:
                    logger.warning("WebSocket connection closed. Reconnecting...")
                    await asyncio.sleep(1)
            except Exception as e:
                if self.active:
                    logger.error(f"Mempool monitoring error: {e}")
                    await asyncio.sleep(1)
                
    }                        
    #[pyo3(name = "execute_trade")]
    
    fn execute_trade_py(&self, params: &TradeParams) -> PyResult<Option<String>> {
        debug!("Executing trade with amount_in: {} SOL, min_out: {} SOL, pool: {}", 
            params.amount_in, params.min_amount_out, params.pool_address);

        // Validate pool exists in cache first
        let pool_exists = self.pool_cache
            .try_lock()
            .map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?
            .contains_key(&params.pool_address);

        if !pool_exists {
            return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
                format!("Pool {} not found in cache. Call update_pool first.", params.pool_address)
            ));
        }
    
        // Get accounts with proper error handling
        let accounts = match self.get_swap_accounts_internal(&params.pool_address) {
            Ok(accts) => accts,
            Err(e) => {
                error!("Failed to get swap accounts: {}", e);
                return Err(e);
            }
        };
    
        let config = self.config.try_lock()
            .map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;

        let amount_in = (params.amount_in * LAMPORTS_PER_SOL as f64) as u64;
        let min_amount_out = (params.min_amount_out * LAMPORTS_PER_SOL as f64) as u64;

        let instructions = vec![
            ComputeBudgetInstruction::set_compute_unit_limit(config.compute_limit),
            ComputeBudgetInstruction::set_compute_unit_price(config.priority_fee),
            Instruction {
                program_id: Pubkey::from_str(RAYDIUM_V4).unwrap(),
                accounts: accounts.into_iter().map(|wrapper| {
                    let pubkey = Pubkey::from_str(&wrapper.pubkey).unwrap();
                    AccountMeta::new(pubkey, wrapper.is_signer)
                }).collect(),
                data: self.create_swap_data(amount_in, min_amount_out),
            }
        ];

        self.send_transaction_internal(&instructions)
    }
    #[pyo3(name = "get_balance")]
    fn get_balance_py(&self) -> PyResult<f64> {
        self.client
            .get_balance(&self.keypair.pubkey())
            .map(|balance| balance as f64 / LAMPORTS_PER_SOL as f64)
            .map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))
    }
    #[pyo3(name = "get_wsol_balance")]
    fn get_wsol_balance_py(&self, token_account: &str) -> PyResult<f64> {
        let pubkey = Pubkey::from_str(token_account)
            .map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(e.to_string()))?;
        
        match self.client.get_token_account_balance(&pubkey) {
            Ok(account) => {
                match account.ui_amount {
                    Some(balance) => Ok(balance),
                    None => Err(PyErr::new::<pyo3::exceptions::PyValueError, _>("No balance found"))
                }
            },
            Err(e) => {
                error!("Failed to get token balance: {}", e);
                Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))
            }
        }
    }    
    #[pyo3(name = "update_pool")]
    fn update_pool_py(&self, pool_data: HashMap<String, String>) -> PyResult<()> {
        let pool_state = PoolState {
            amm_id: Pubkey::from_str(&pool_data["amm_id"])
                .map_err(|_| PyErr::new::<pyo3::exceptions::PyValueError, _>("Invalid AMM ID"))?,
            authority: Pubkey::from_str(&pool_data["amm_authority"])
                .map_err(|_| PyErr::new::<pyo3::exceptions::PyValueError, _>("Invalid authority"))?,
            open_orders: Pubkey::from_str(&pool_data["amm_open_orders"])
                .map_err(|_| PyErr::new::<pyo3::exceptions::PyValueError, _>("Invalid open orders"))?,
            target_orders: Pubkey::from_str(&pool_data["amm_target"])
                .map_err(|_| PyErr::new::<pyo3::exceptions::PyValueError, _>("Invalid target orders"))?,
            base_vault: Pubkey::from_str(&pool_data["pool_coin_token_account"])
                .map_err(|_| PyErr::new::<pyo3::exceptions::PyValueError, _>("Invalid base vault"))?,
            quote_vault: Pubkey::from_str(&pool_data["pool_pc_token_account"])
                .map_err(|_| PyErr::new::<pyo3::exceptions::PyValueError, _>("Invalid quote vault"))?,
            market: Pubkey::from_str(&pool_data["serum_market"])
                .map_err(|_| PyErr::new::<pyo3::exceptions::PyValueError, _>("Invalid market"))?,
            bids: Pubkey::from_str(&pool_data["serum_bids"])
                .map_err(|_| PyErr::new::<pyo3::exceptions::PyValueError, _>("Invalid bids"))?,
            asks: Pubkey::from_str(&pool_data["serum_asks"])
                .map_err(|_| PyErr::new::<pyo3::exceptions::PyValueError, _>("Invalid asks"))?,
            event_queue: Pubkey::from_str(&pool_data["serum_events"])
                .map_err(|_| PyErr::new::<pyo3::exceptions::PyValueError, _>("Invalid events"))?,
            vault_signer: Pubkey::from_str(&pool_data["vault_signer"])
                .map_err(|_| PyErr::new::<pyo3::exceptions::PyValueError, _>("Invalid vault signer"))?,
            token_mint: Pubkey::from_str(pool_data.get("token_mint").unwrap_or(&pool_data["base_mint"]))
                .map_err(|_| PyErr::new::<pyo3::exceptions::PyValueError, _>("Invalid token mint"))?,
            last_update: Instant::now(),
        };

        // Update pool cache with proper error handling
        match self.pool_cache.try_lock() {
            Ok(mut cache) => {
                cache.insert(pool_data["amm_id"].clone(), pool_state);
                debug!("Pool cache updated for AMM ID: {}", pool_data["amm_id"]);
                Ok(())
            },
            Err(e) => {
                error!("Failed to acquire pool cache lock: {}", e);
                Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>("Failed to update pool cache"))
            }
        }
    }
}
// Internal implementation methods
impl TradingCore {
    fn get_swap_accounts_internal(&self, pool_address: &str) -> PyResult<Vec<AccountMetaWrapper>> {
        let pool = self.pool_cache
            .try_lock()
            .map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?
            .get(pool_address)
            .cloned()
            .ok_or_else(|| PyErr::new::<pyo3::exceptions::PyValueError, _>("Pool not found"))?;

        let payer = self.keypair.pubkey();

        let accounts = vec![
            AccountMeta::new_readonly(spl_token::id(), false),
            AccountMeta::new(pool.amm_id, false),
            AccountMeta::new_readonly(pool.authority, false),
            AccountMeta::new(pool.open_orders, false),
            AccountMeta::new(pool.target_orders, false),
            AccountMeta::new(pool.base_vault, false),
            AccountMeta::new(pool.quote_vault, false),
            AccountMeta::new_readonly(pool.market, false),
            AccountMeta::new(pool.bids, false),
            AccountMeta::new(pool.asks, false),
            AccountMeta::new(pool.event_queue, false),
            AccountMeta::new(payer, true),
        ];

        Ok(accounts.into_iter().map(AccountMetaWrapper::from).collect())
    }

    fn create_swap_data(&self, amount_in: u64, min_amount_out: u64) -> Vec<u8> {
        // Pre-allocate exact capacity needed
        let mut data = Vec::with_capacity(17);
        data.push(9);  // Swap instruction discriminator
        data.extend_from_slice(&amount_in.to_le_bytes());
        data.extend_from_slice(&min_amount_out.to_le_bytes());
        data
    }

    fn send_transaction_internal(&self, instructions: &[Instruction]) -> PyResult<Option<String>> {
        let config = self.config.try_lock()
            .map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;

        // Get recent blockhash with retry logic
        let recent_blockhash = self.get_blockhash_with_retry(3)
            .map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;

        let transaction = Transaction::new_signed_with_payer(
            instructions,
            Some(&self.keypair.pubkey()),
            &[&*self.keypair],
            recent_blockhash,
        );

        let mut retries = config.retry_count;
        let mut backoff = 500; // Start with 500ms backoff

        while retries > 0 {
            match self.client.send_and_confirm_transaction_with_spinner_and_config(
                &transaction,
                CommitmentConfig::confirmed(),
                RpcSendTransactionConfig {
                    skip_preflight: config.skip_preflight,
                    preflight_commitment: Some(CommitmentConfig::confirmed().commitment),
                    encoding: None,
                    max_retries: None,
                    min_context_slot: Some(0),
                },
            ) {
                Ok(signature) => {
                    debug!("Transaction successful: {}", signature);
                    return Ok(Some(signature.to_string()));
                }
                Err(e) => {
                    warn!("Transaction attempt {} failed: {}", config.retry_count - retries + 1, e);
                    retries -= 1;
                    if retries > 0 {
                        std::thread::sleep(Duration::from_millis(backoff));
                        backoff *= 2;  // Exponential backoff
                    }
                }
            }
        }

        Ok(None)
    }

    fn get_blockhash_with_retry(&self, max_retries: u32) -> Result<solana_sdk::hash::Hash, solana_client::client_error::ClientError> {
        let mut retries = max_retries;
        let mut backoff = 100; // Start with 100ms backoff
        
        while retries > 0 {
            match self.client.get_latest_blockhash() {
                Ok(blockhash) => return Ok(blockhash),
                Err(e) => {
                    warn!("Blockhash fetch attempt {} failed: {}", max_retries - retries + 1, e);
                    retries -= 1;
                    if retries > 0 {
                        std::thread::sleep(Duration::from_millis(backoff));
                        backoff *= 2;  // Exponential backoff
                    } else {
                        return Err(e);
                    }
                }
            }
        }
        
        self.client.get_latest_blockhash()
    }
}

#[pymodule]
fn solana_rust_bot(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_class::<TradingCore>()?;
    m.add_class::<TradingConfig>()?;
    m.add_class::<TradeParams>()?;
    m.add_class::<PoolInfo>()?;
    m.add_class::<TokenPools>()?;
    m.add_class::<AccountMetaWrapper>()?;
    Ok(())
}