use log::{info, warn};
use pyo3::{exceptions::PyValueError, prelude::*};
use pyo3_asyncio::tokio::future_into_py;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::{
    commitment_config::{CommitmentConfig, CommitmentLevel},
    compute_budget::ComputeBudgetInstruction,
    instruction::Instruction,
    message::Message,
    pubkey::Pubkey,
    signature::{Keypair, Signer},
    transaction::Transaction,
};
use spl_associated_token_account::get_associated_token_address;
use spl_token;
use std::{env, str::FromStr, sync::Arc};
use base58::FromBase58;
use tokio::time::{sleep, Duration};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};

// Constants for MEV optimization
const COMPUTE_UNITS: u32 = 50_000;
const PRIORITY_FEE: u64 = 750_000;

#[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>>,
}

#[pyclass]
pub struct TradingCore {
    client: Arc<RpcClient>,
    keypair: Arc<Keypair>,
    active_trade: Arc<AtomicBool>,
    last_trade_time: Arc<AtomicU64>,
}

impl TradingCore {
    fn load_keypair() -> PyResult<Keypair> {
        let private_key = env::var("SOLANA_PRIVATE_KEY")
            .map_err(|_| PyValueError::new_err("SOLANA_PRIVATE_KEY not found in environment"))?;

        // Optional public key validation
        if let Ok(public_key) = env::var("SOLANA_PUBLIC_KEY") {
            let _pubkey = Pubkey::from_str(&public_key)
                .map_err(|e| PyValueError::new_err(format!("Invalid public key: {}", e)))?;
        }

        // Decode private key
        let secret_key_bytes = private_key
            .from_base58()
            .map_err(|e| PyValueError::new_err(format!("Invalid base58 encoding: {:?}", e)))?;

        match secret_key_bytes.len() {
            64 => Keypair::from_bytes(&secret_key_bytes)
                .map_err(|e| PyValueError::new_err(format!("Invalid 64-byte keypair: {}", e))),
            32 => {
                let mut full_keypair = [0u8; 64];
                full_keypair[..32].copy_from_slice(&secret_key_bytes);
                Keypair::from_bytes(&full_keypair)
                    .map_err(|e| PyValueError::new_err(format!("Invalid keypair from 32-byte secret: {}", e)))
            },
            len => Err(PyValueError::new_err(format!("Invalid key length: {} bytes", len)))
        }
    }

    async fn ensure_ata_exists(
        &self, 
        owner: &Pubkey, 
        mint: &Pubkey
    ) -> PyResult<Pubkey> {
        // For WSOL, use predefined account
        if let Ok(wsol_address) = env::var("WSOL_ADDRESS") {
            if mint.to_string() == wsol_address {
                if let Ok(wsol_account) = env::var("WSOL_TOKEN_ACCOUNT") {
                    let wsol_pubkey = Pubkey::from_str(&wsol_account)
                        .map_err(|e| PyValueError::new_err(format!("Invalid WSOL account: {}", e)))?;
                    return Ok(wsol_pubkey);
                }
            }
        }

        let ata = get_associated_token_address(owner, mint);
        
        match self.client.get_account(&ata).await {
            Ok(_) => {
                info!("ATA exists: {}", ata);
                Ok(ata)
            }
            Err(_) => {
                warn!("Creating ATA for mint: {}", mint);
                
                let ix = spl_associated_token_account::instruction::create_associated_token_account_idempotent(
                    &self.keypair.pubkey(),
                    owner,
                    mint,
                    &spl_token::ID
                );

                let blockhash = self.client.get_latest_blockhash().await
                    .map_err(|e| PyValueError::new_err(format!("Failed to get blockhash: {}", e)))?;

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

                match self.client.send_transaction_with_config(
                    &tx,
                    solana_client::rpc_config::RpcSendTransactionConfig {
                        skip_preflight: true,
                        preflight_commitment: Some(CommitmentLevel::Confirmed),
                        encoding: None,
                        max_retries: Some(2),
                        min_context_slot: None,
                    },
                ).await {
                    Ok(_) => {
                        info!("ATA created successfully: {}", ata);
                        Ok(ata)
                    },
                    Err(e) => Err(PyValueError::new_err(format!("Failed to create ATA: {}", e)))
                }
            }
        }
    }fn create_swap_instruction(
        &self,
        program_id: &Pubkey,
        accounts: &[Pubkey],
        amount: u64,
        is_buy: bool,
        source_ata: Pubkey,
        destination_ata: Pubkey,
        victim_data: Option<&[u8]>,
    ) -> Instruction {
        let mut modified_accounts = accounts.to_vec();
        modified_accounts[1] = source_ata;
        modified_accounts[2] = destination_ata;

        let mut data = vec![if is_buy { 9u8 } else { 11u8 }];
        if is_buy && victim_data.is_some() {
            data.extend_from_slice(victim_data.unwrap());
        } else {
            data.extend_from_slice(&amount.to_le_bytes());
        }

        let account_metas = modified_accounts.iter().enumerate()
            .map(|(i, &pubkey)| {
                match i {
                    0 => solana_sdk::instruction::AccountMeta::new_readonly(pubkey, false),
                    1 | 2 => solana_sdk::instruction::AccountMeta::new(pubkey, false),
                    15..=17 => solana_sdk::instruction::AccountMeta::new(pubkey, true),
                    _ => solana_sdk::instruction::AccountMeta::new(pubkey, false),
                }
            })
            .collect();

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

    async fn execute_trade(
        &self,
        params: &TradeParams,
    ) -> PyResult<String> {
        // Check if we're already in a trade
        if self.active_trade.load(Ordering::Relaxed) {
            return Err(PyValueError::new_err("Trade already in progress"));
        }

        // Check time since last trade
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();
        let last_trade = self.last_trade_time.load(Ordering::Relaxed);
        
        if now - last_trade < 1 {  // Enforce 1 second between trades
            return Err(PyValueError::new_err("Too soon for new trade, respecting RPC limits"));
        }

        // Set active trade flag
        self.active_trade.store(true, Ordering::Relaxed);

        let result = async {
            let token_a_mint = Pubkey::from_str(&params.token_a_mint)?;
            let token_b_mint = Pubkey::from_str(&params.token_b_mint)?;

            let mut accounts = Vec::with_capacity(params.accounts.len());
            for acc in &params.accounts {
                accounts.push(Pubkey::from_str(acc)
                    .map_err(|e| PyValueError::new_err(format!("Invalid account: {} - {}", acc, e)))?);
            }

            // Handle different trade types
            if params.is_buy {
                // For new token buys
                let destination_ata = self.ensure_ata_exists(&self.keypair.pubkey(), &token_a_mint).await?;
                let source_ata = self.ensure_ata_exists(&self.keypair.pubkey(), &token_b_mint).await?;
                
                // Pause after ATA setup
                sleep(Duration::from_millis(100)).await;

                let mut instructions = vec![
                    ComputeBudgetInstruction::set_compute_unit_limit(COMPUTE_UNITS),
                    ComputeBudgetInstruction::set_compute_unit_price(PRIORITY_FEE),
                ];

                instructions.push(self.create_swap_instruction(
                    &accounts[0],
                    &accounts,
                    params.amount_in.unwrap(),
                    true,
                    source_ata,
                    destination_ata,
                    params.victim_data.as_deref(),
                ));

                let blockhash = self.client.get_latest_blockhash().await?;
                
                let message = Message::new_with_blockhash(
                    &instructions,
                    Some(&self.keypair.pubkey()),
                    &blockhash,
                );
                
                let mut tx = Transaction::new_unsigned(message);
                tx.try_sign(&[self.keypair.as_ref()], blockhash)?;

                let sig = self.client.send_transaction_with_config(
                    &tx,
                    solana_client::rpc_config::RpcSendTransactionConfig {
                        skip_preflight: true,
                        preflight_commitment: Some(CommitmentLevel::Confirmed),
                        encoding: None,
                        max_retries: Some(2),
                        min_context_slot: None,
                    },
                ).await?;

                Ok(sig.to_string())
            } else {
                // For exit trades
                sleep(Duration::from_millis(100)).await;
                
                let source_ata = get_associated_token_address(&self.keypair.pubkey(), &token_a_mint);
                let destination_ata = get_associated_token_address(&self.keypair.pubkey(), &token_b_mint);

                // Get full balance for exit
                let balance = self.client.get_token_account_balance(&source_ata)
                    .await?
                    .amount
                    .parse::<u64>()?;

                let mut instructions = vec![
                    ComputeBudgetInstruction::set_compute_unit_limit(COMPUTE_UNITS),
                    ComputeBudgetInstruction::set_compute_unit_price(PRIORITY_FEE),
                ];

                instructions.push(self.create_swap_instruction(
                    &accounts[0],
                    &accounts,
                    balance,
                    false,
                    source_ata,
                    destination_ata,
                    None,
                ));

                let blockhash = self.client.get_latest_blockhash().await?;
                
                let message = Message::new_with_blockhash(
                    &instructions,
                    Some(&self.keypair.pubkey()),
                    &blockhash,
                );
                
                let mut tx = Transaction::new_unsigned(message);
                tx.try_sign(&[self.keypair.as_ref()], blockhash)?;

                let sig = self.client.send_transaction_with_config(
                    &tx,
                    solana_client::rpc_config::RpcSendTransactionConfig {
                        skip_preflight: true,
                        preflight_commitment: Some(CommitmentLevel::Confirmed),
                        encoding: None,
                        max_retries: Some(2),
                        min_context_slot: None,
                    },
                ).await?;

                Ok(sig.to_string())
            }
        }.await;

        // Clear active trade flag and update last trade time
        self.active_trade.store(false, Ordering::Relaxed);
        self.last_trade_time.store(now, Ordering::Relaxed);

        result
    }
}

#[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,
    ))]
    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>>,
    ) -> Self {
        Self {
            accounts,
            amount_in,
            is_buy,
            reuse_blockhash,
            token_a_mint,
            token_b_mint,
            sell_all,
            victim_data,
        }
    }
}  can you finish off the end of this script please