use anyhow::{bail, Context, Result};
use borsh::{BorshDeserialize, BorshSerialize};
use futures_util::StreamExt;
use log::{debug, error, info, warn};
use reqwest::Client as HttpClient;
use serde::{Deserialize, Serialize};
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
    commitment_config::CommitmentConfig,
    instruction::{AccountMeta, Instruction},
    pubkey::Pubkey,
    signature::{Keypair, Signer},
    system_instruction,
    transaction::Transaction,
};
use solana_client::rpc_request::TokenAccountsFilter;
use solana_transaction_status::UiTransactionEncoding;
use spl_associated_token_account::get_associated_token_address;
use spl_token::instruction as token_instruction;
use std::{collections::HashMap, str::FromStr, sync::Arc, time::Duration};
use tokio::time;
use tokio_tungstenite::{connect_async, tungstenite::protocol::Message};
use toml;
use url::Url;
use futures_util::SinkExt;

const RAYDIUM_AMM_PROGRAM_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";

#[derive(Debug, Deserialize, Clone)]
struct Config {
    api_keys: ApiKeys,
    wallet: WalletConfig,
    accounts: AccountsConfig,
    trading: TradingConfig,
}

#[derive(Debug, Deserialize, Clone)]
struct ApiKeys {
    helius_api_key: String,
}

#[derive(Debug, Deserialize, Clone)]
struct WalletConfig {
    public_key: String,
    keypair: String,
}

#[derive(Debug, Deserialize, Clone)]
struct AccountsConfig {
    wsol_account: String,
}

#[derive(Debug, Deserialize, Clone)]
struct TradingConfig {
    min_liquidity: u64,
    max_liquidity: u64,
    snipe_amount: f64,
    profit_target_percent: f64,
    max_retry_attempts: u8,
    stop_loss_percent: f64,
    max_slippage: f64,
    priority_fee: u64,
    min_sol_balance: f64,
}

#[derive(Debug, Deserialize)]
struct HeliusEvent {
    description: String,
    token_address: String,
    pool_address: String,
    wallet: Option<String>,
}

#[derive(Debug, Deserialize)]
struct TokenMetadata {
    #[serde(rename = "onChainAccountInfo")]
    on_chain_account_info: OnChainAccountInfo,
    #[serde(rename = "priceInfo")]
    price_info: PriceInfo,
}

#[derive(Debug, Deserialize)]
struct OnChainAccountInfo {
    #[serde(rename = "mintAuthority")]
    mint_authority: Option<String>,
    supply: String,
    decimals: u8,
}

#[derive(Debug, Deserialize)]
struct PriceInfo {
    #[serde(rename = "pricePerToken")]
    price_per_token: f64,
}

#[derive(Debug, Deserialize)]
struct PriorityFeeResponse {
    priority_fee: u64,
}

#[derive(Debug)]
struct TradeOpportunity {
    token_address: String,
    pool_address: String,
}

#[derive(Debug, Clone)]
struct TokenInfo {
    mint_authority: Option<Pubkey>,
    liquidity: u64,
    total_supply: u64,
    decimals: u8,
    price: f64,
}

#[derive(Debug, Clone)]
struct Position {
    token_address: String,
    pool_address: String,
    amount: u64,
    buy_price: f64,
    stop_loss_price: f64,
    take_profit_price: f64,
    stop_loss_tx: Transaction,
    take_profit_tx: Transaction,
}

#[derive(BorshSerialize, BorshDeserialize)]
enum AmmInstruction {
    Swap {
        amount_in: u64,
        minimum_amount_out: u64,
    },
    // Add other instruction types as needed
}

struct Bot {
    rpc_client: RpcClient,
    http_client: HttpClient,
    keypair: Keypair,
    config: Config,
    token_cache: HashMap<String, TokenInfo>,
    blacklist: HashMap<String, String>,
    active_positions: HashMap<String, Position>,
}

impl Bot {
    fn new(config_path: &str) -> Result<Self> {
        let config_str =
            std::fs::read_to_string(config_path).context("Failed to read config file")?;
        let config: Config = toml::from_str(&config_str).context("Failed to parse TOML")?;

        let rpc_client = RpcClient::new_with_commitment(
            &format!(
                "https://rpc.helius.xyz/?api-key={}",
                config.api_keys.helius_api_key
            ),
            CommitmentConfig::confirmed(),
        );
        let keypair = read_keypair_file(&config.wallet.keypair)?;
        let http_client = HttpClient::new();

        Ok(Self {
            rpc_client,
            http_client,
            keypair,
            config,
            token_cache: HashMap::new(),
            blacklist: HashMap::new(),
            active_positions: HashMap::new(),
        })
    }

    async fn run(&mut self) -> Result<()> {
        self.ensure_wsol_balance().await?;

        let ws_url = format!(
            "wss://rpc.helius.xyz/?api-key={}",
            self.config.api_keys.helius_api_key
        );
        let (ws_stream, _) = connect_async(Url::parse(&ws_url)?).await?;
        let (mut write, mut read) = ws_stream.split();

        let subscribe_msg = serde_json::json!({
            "jsonrpc": "2.0",
            "id": 1,
            "method": "subscribePoolCreations",
            "params": []
        });
        write.send(Message::Text(subscribe_msg.to_string())).await?;

        let mut update_interval = tokio::time::interval(Duration::from_secs(300)); // Update every 5 minutes

        loop {
            tokio::select! {
                Some(message) = read.next() => {
                    match message {
                        Ok(Message::Text(text)) => {
                            let event: serde_json::Value = serde_json::from_str(&text)?;
                            if let Some(params) = event.get("params") {
                                if let Ok(helius_event) = serde_json::from_value::<HeliusEvent>(params.clone()) {
                                    if let Some(opportunity) = self.analyze_event(&helius_event).await? {
                                        if let Err(e) = self.execute_trade(&opportunity).await {
                                            error!("Failed to execute trade: {}", e);
                                        }
                                    }
                                }
                            }
                        }
                        Err(e) => warn!("WebSocket error: {}", e),
                        _ => {}
                    }
                }
                _ = update_interval.tick() => {
                    if let Err(e) = self.update_pre_signed_transactions().await {
                        error!("Failed to update pre-signed transactions: {}", e);
                    }
                }
            }
        }
    }

    async fn analyze_event(&self, event: &HeliusEvent) -> Result<Option<TradeOpportunity>> {
        if event.description.contains("InitializePool") && !event.description.contains("Deposit") {
            let token_info = self.get_token_info(&event.token_address).await?;

            if self
                .is_mint_authority_revoked(&event.token_address, &token_info)
                .await?
                && self
                    .is_liquidity_pool_burnt(&event.pool_address, &token_info)
                    .await?
                && self.is_valid_token(&token_info).await?
                && !self.blacklist.contains_key(&event.token_address)
                && self.is_raydium_pool(&event.pool_address).await?
            {
                return Ok(Some(TradeOpportunity {
                    token_address: event.token_address.clone(),
                    pool_address: event.pool_address.clone(),
                }));
            } else {
                self.blacklist.insert(
                    event.token_address.clone(),
                    "Invalid token or non-Raydium pool".to_string(),
                );
            }
        }
        Ok(None)
    }

    async fn is_mint_authority_revoked(
        &self,
        token_address: &str,
        token_info: &TokenInfo,
    ) -> Result<bool> {
        Ok(token_info.mint_authority.is_none())
    }

    async fn is_liquidity_pool_burnt(
        &self,
        pool_address: &str,
        token_info: &TokenInfo,
    ) -> Result<bool> {
        let pool_pubkey = Pubkey::from_str(pool_address)?;
        let pool_token_account = get_associated_token_address(
            &pool_pubkey,
            &Pubkey::from_str(&self.config.accounts.wsol_account)?,
        );
        let pool_balance = self
            .rpc_client
            .get_token_account_balance(&pool_token_account)?;
        let liquidity_percentage =
            pool_balance.ui_amount.unwrap_or(0.0) / token_info.total_supply as f64;

        Ok(liquidity_percentage >= 0.99)
    }

    async fn is_valid_token(&self, token_info: &TokenInfo) -> Result<bool> {
        if token_info.total_supply < 1_000_000_000 {
            return Ok(false);
        }
        let token_pubkey = Pubkey::from_str(&token_info.mint_authority.unwrap().to_string())?;
        let token_accounts = self
            .rpc_client
            .get_token_accounts_by_owner(&token_pubkey, TokenAccountsFilter::Mint(token_pubkey))?;
        let total_supply = token_info.total_supply as f64;

        for account in token_accounts {
            let pubkey = Pubkey::from_str(&account.pubkey)?;
            let balance = self.rpc_client.get_token_account_balance(&pubkey)?;
            if balance.ui_amount.unwrap_or(0.0) / total_supply > 0.1 {
                return Ok(false);
            }
        }

        Ok(true)
    }

    async fn is_raydium_pool(&self, pool_address: &str) -> Result<bool> {
        let pool_pubkey = Pubkey::from_str(pool_address)?;
        let account_info = self.rpc_client.get_account(&pool_pubkey)?;
        Ok(account_info.owner == Pubkey::from_str(RAYDIUM_AMM_PROGRAM_ID)?)
    }

    async fn execute_trade(&mut self, opportunity: &TradeOpportunity) -> Result<()> {
        let amount = (self.config.trading.snipe_amount * 1e9) as u64;

        let instructions = vec![
            self.create_trade_instruction(
                &opportunity.token_address,
                &opportunity.pool_address,
                amount,
            )
            .context("Failed to create trade instruction")?,
            self.create_priority_fee_instruction()
                .await
                .context("Failed to create priority fee instruction")?,
        ];

        let recent_blockhash = self
            .rpc_client
            .get_latest_blockhash()
            .context("Failed to get recent blockhash")?;
        let mut transaction =
            Transaction::new_with_payer(&instructions, Some(&self.keypair.pubkey()));

        transaction.sign(&[&self.keypair], recent_blockhash);

        for attempt in 1..=self.config.trading.max_retry_attempts {
            match self.rpc_client.send_and_confirm_transaction(&transaction) {
                Ok(signature) => {
                    info!("Trade executed: {}", signature);
                    let buy_price = self
                        .get_token_price(&opportunity.token_address)
                        .await
                        .context("Failed to get token price after trade")?;
                    self.setup_position(
                        &opportunity.token_address,
                        &opportunity.pool_address,
                        amount,
                        buy_price,
                    )
                    .await
                    .context("Failed to setup position")?;
                    return Ok(());
                }
                Err(e) if attempt < self.config.trading.max_retry_attempts => {
                    warn!("Attempt {} failed: {}. Retrying...", attempt, e);
                    time::sleep(Duration::from_secs(1)).await;
                }
                Err(e) => {
                    error!(
                        "Failed to execute trade after {} attempts: {}",
                        self.config.trading.max_retry_attempts, e
                    );
                    return Err(e.into());
                }
            }
        }

        bail!("Failed to execute trade after maximum retry attempts")
    }
    async fn get_token_info(&self, token_address: &str) -> Result<TokenInfo> {
        if let Some(info) = self.token_cache.get(token_address) {
            return Ok(info.clone());
        }

        let token_pubkey = Pubkey::from_str(token_address)?;
        let account_info = self.rpc_client.get_account(&token_pubkey)?;
        let mint_info = Mint::unpack(&account_info.data)?;

        let info = TokenInfo {
            mint_authority: mint_info.mint_authority,
            liquidity: account_info.lamports,
            total_supply: mint_info.supply,
            decimals: mint_info.decimals,
            price: 0.0,
        };

        Ok(info)
    }
}
    async fn setup_position(
        &mut self,
        token_address: &str,
        pool_address: &str,
        amount: u64,
        buy_price: f64,
    ) -> Result<()> {
        let stop_loss_price = buy_price * (1.0 - self.config.trading.stop_loss_percent / 100.0);
        let take_profit_price =
            buy_price * (1.0 + self.config.trading.profit_target_percent / 100.0);

        let stop_loss_tx = self
            .create_pre_signed_sell_transaction(
                token_address,
                pool_address,
                amount,
                stop_loss_price,
            )
            .await?;
        let take_profit_tx = self
            .create_pre_signed_sell_transaction(
                token_address,
                pool_address,
                amount,
                take_profit_price,
            )
            .await?;

        let position = Position {
            token_address: token_address.to_string(),
            pool_address: pool_address.to_string(),
            amount,
            buy_price,
            stop_loss_price,
            take_profit_price,
            stop_loss_tx,
            take_profit_tx,
        };

        self.active_positions
            .insert(token_address.to_string(), position);
        self.monitor_position(token_address.to_string());

        Ok(())
    }

    async fn create_pre_signed_sell_transaction(
        &self,
        token_address: &str,
        pool_address: &str,
        amount: u64,
        price: f64,
    ) -> Result<Transaction> {
        let instructions = vec![
            self.create_sell_instruction(token_address, pool_address, amount)?,
            self.create_priority_fee_instruction().await?,
        ];

        let recent_blockhash = self.rpc_client.get_latest_blockhash()?;
        let mut transaction =
            Transaction::new_with_payer(&instructions, Some(&self.keypair.pubkey()));

        transaction.sign(&[&self.keypair], recent_blockhash);
        Ok(transaction)
    }

    fn create_trade_instruction(
        &self,
        token_address: &str,
        pool_address: &str,
        amount: u64,
    ) -> Result<Instruction> {
        let amm_program_id = Pubkey::from_str(RAYDIUM_AMM_PROGRAM_ID)?;
        let user_wallet = self.keypair.pubkey();
        let pool_id = Pubkey::from_str(pool_address)?;
        let token_account = Pubkey::from_str(token_address)?;
        let wsol_account = Pubkey::from_str(&self.config.accounts.wsol_account)?;

        let (amm_authority, _) = Pubkey::find_program_address(&[pool_id.as_ref()], &amm_program_id);

        let (open_orders, _) = Pubkey::find_program_address(
            &[
                pool_id.as_ref(),
                amm_program_id.as_ref(),
                "open_orders".as_bytes(),
            ],
            &amm_program_id,
        );

        let (target_orders, _) = Pubkey::find_program_address(
            &[
                pool_id.as_ref(),
                amm_program_id.as_ref(),
                "target_orders".as_bytes(),
            ],
            &amm_program_id,
        );

        let (serum_market, _) = Pubkey::find_program_address(
            &[
                pool_id.as_ref(),
                amm_program_id.as_ref(),
                "serum_market".as_bytes(),
            ],
            &amm_program_id,
        );

        Ok(Instruction {
            program_id: amm_program_id,
            accounts: vec![
                AccountMeta::new(pool_id, false),
                AccountMeta::new(amm_authority, false),
                AccountMeta::new(user_wallet, true),
                AccountMeta::new(token_account, false),
                AccountMeta::new(wsol_account, false),
                AccountMeta::new(open_orders, false),
                AccountMeta::new(target_orders, false),
                AccountMeta::new(serum_market, false),
            ],
            data: AmmInstruction::Swap {
                amount_in: amount,
                minimum_amount_out: (amount as f64
                    * (1.0 - self.config.trading.max_slippage / 100.0))
                    as u64,
            }
            .try_to_vec()?,
        })
    }

    async fn create_sell_instruction(
        &self,
        token_address: &str,
        pool_address: &str,
        amount: u64,
    ) -> Result<Instruction> {
        let amm_program_id = Pubkey::from_str(RAYDIUM_AMM_PROGRAM_ID)?;
        let pool_id = Pubkey::from_str(pool_address)?;
        let user_wallet = self.keypair.pubkey();
        let token_account = Pubkey::from_str(token_address)?;
        let wsol_account = Pubkey::from_str(&self.config.accounts.wsol_account)?;

        let (amm_authority, _) = Pubkey::find_program_address(&[pool_id.as_ref()], &amm_program_id);

        let (open_orders, _) = Pubkey::find_program_address(
            &[
                pool_id.as_ref(),
                amm_program_id.as_ref(),
                "open_orders".as_bytes(),
            ],
            &amm_program_id,
        );

        let (target_orders, _) = Pubkey::find_program_address(
            &[
                pool_id.as_ref(),
                amm_program_id.as_ref(),
                "target_orders".as_bytes(),
            ],
            &amm_program_id,
        );

        let (serum_market, _) = Pubkey::find_program_address(
            &[
                pool_id.as_ref(),
                amm_program_id.as_ref(),
                "serum_market".as_bytes(),
            ],
            &amm_program_id,
        );

        let current_price = self.get_token_price(token_address).await?;
        let minimum_amount_out =
            (amount as f64 * current_price * (1.0 - self.config.trading.max_slippage / 100.0))
                as u64;

        Ok(Instruction {
            program_id: amm_program_id,
            accounts: vec![
                AccountMeta::new(pool_id, false),
                AccountMeta::new(amm_authority, false),
                AccountMeta::new(user_wallet, true),
                AccountMeta::new(token_account, false),
                AccountMeta::new(wsol_account, false),
                AccountMeta::new(open_orders, false),
                AccountMeta::new(target_orders, false),
                AccountMeta::new(serum_market, false),
            ],
            data: AmmInstruction::Swap {
                amount_in: amount,
                minimum_amount_out,
            }
            .try_to_vec()?,
        })
    }

    async fn create_priority_fee_instruction(&self) -> Result<Instruction> {
        let priority_fee = self.get_priority_fee().await?;
        Ok(system_instruction::transfer(
            &self.keypair.pubkey(),
            &self.keypair.pubkey(),
            priority_fee,
        ))
    }

    async fn get_priority_fee(&self) -> Result<u64> {
        let url = format!(
            "https://api.helius.xyz/v0/priority-fee?api-key={}",
            self.config.api_keys.helius_api_key
        );
        let response = self
            .http_client
            .get(&url)
            .send()
            .await?
            .json::<PriorityFeeResponse>()
            .await?;
        Ok(response.priority_fee)
    }

    async fn get_token_price(&self, token_address: &str) -> Result<f64> {
        let url = format!(
            "https://api.helius.xyz/v0/token-metadata?api-key={}",
            self.config.api_keys.helius_api_key
        );
        let body = serde_json::json!({
            "mintAccounts": [token_address]
        });
        let response = self
            .http_client
            .post(&url)
            .json(&body)
            .send()
            .await
            .context("Failed to send request to Helius API")?
            .json::<Vec<TokenMetadata>>()
            .await
            .context("Failed to parse Helius API response")?;

        response
            .first()
            .ok_or_else(|| anyhow::anyhow!("Empty response from Helius API"))
            .map(|token_metadata| token_metadata.price_info.price_per_token)
    }

    fn monitor_position(&self, token_address: String) {
        let mut bot = self.clone();
        tokio::spawn(async move {
            loop {
                if let Err(e) = bot.check_position(&token_address).await {
                    error!("Error checking position: {}", e);
                }
                time::sleep(Duration::from_secs(1)).await;
            }
        });
    }

    async fn check_position(&mut self, token_address: &str) -> Result<()> {
        if let Some(position) = self.active_positions.get(token_address) {
            let current_price = self.get_token_price(token_address).await?;

            if current_price <= position.stop_loss_price {
                info!("Stop loss triggered for {}", token_address);
                self.execute_pre_signed_transaction(&position.stop_loss_tx)
                    .await?;
                self.active_positions.remove(token_address);
            } else if current_price >= position.take_profit_price {
                info!("Take profit triggered for {}", token_address);
                self.execute_pre_signed_transaction(&position.take_profit_tx)
                    .await?;
                self.active_positions.remove(token_address);
            }
        }

        Ok(())
    }

    async fn execute_pre_signed_transaction(&self, transaction: &Transaction) -> Result<()> {
        self.rpc_client.send_and_confirm_transaction(transaction)?;
        Ok(())
    }

    async fn ensure_wsol_balance(&self) -> Result<()> {
        let wsol_account = Pubkey::from_str(&self.config.accounts.wsol_account)?;
        let wsol_balance = self.rpc_client.get_token_account_balance(&wsol_account)?;
        let min_wsol_balance = (self.config.trading.min_sol_balance * 1e9) as u64;

        if wsol_balance.amount.parse::<u64>()? < min_wsol_balance {
            self.wrap_sol(min_wsol_balance - wsol_balance.amount.parse::<u64>()?)
                .await?;
        }

        Ok(())
    }

    async fn wrap_sol(&self, amount: u64) -> Result<()> {
        let wsol_account = Pubkey::from_str(&self.config.accounts.wsol_account)?;

        let instructions = vec![
            system_instruction::transfer(&self.keypair.pubkey(), &wsol_account, amount),
            token_instruction::sync_native(&spl_token::id(), &wsol_account)?,
        ];

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

        self.rpc_client.send_and_confirm_transaction(&transaction)?;
        info!("Wrapped {} SOL to WSOL", amount as f64 / 1e9);
        Ok(())
    }

    async fn update_pre_signed_transactions(&mut self) -> Result<()> {
        let positions: Vec<_> = self.active_positions.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
        for (token_address, position) in positions {
            let stop_loss_tx = self.create_pre_signed_sell_transaction(
                &position.token_address,
                &position.pool_address,
                position.amount,
                position.stop_loss_price
            ).await?;
            
            let take_profit_tx = self.create_pre_signed_sell_transaction(
                &position.token_address,
                &position.pool_address,
                position.amount,
                position.take_profit_price
            ).await?;
            
            if let Some(pos) = self.active_positions.get_mut(&token_address) {
                pos.stop_loss_tx = stop_loss_tx;
                pos.take_profit_tx = take_profit_tx;
            }
        }
        Ok(())
    }
            

impl Clone for Bot {
    fn clone(&self) -> Self {
        Self {
            rpc_client: RpcClient::new(self.rpc_client.url()),
            http_client: self.http_client.clone(),
            keypair: Keypair::from_bytes(&self.keypair.to_bytes()).unwrap(),
            config: self.config.clone(),
            token_cache: self.token_cache.clone(),
            blacklist: self.blacklist.clone(),
            active_positions: self.active_positions.clone(),
        }
    }
}

fn read_keypair_file(keypair_str: &str) -> Result<Keypair> {
    let keypair_bytes: Vec<u8> = keypair_str
        .trim_matches(|c| c == '[' || c == ']')
        .split(',')
        .map(|s| s.trim().parse::<u8>())
        .collect::<std::result::Result<Vec<u8>, _>>()?;

    Ok(Keypair::from_bytes(&keypair_bytes)?)
}

#[tokio::main]
async fn main() -> Result<()> {
    env_logger::init();
    let mut bot = Bot::new("config.toml")?;
    bot.run().await
}
 use anyhow::{anyhow, Result};
use chrono::Utc;
use futures_util::{SinkExt, StreamExt};
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use solana_client::rpc_client::RpcClient;
use solana_sdk::signature::{Keypair, Signer}; // Removed unused CommitmentConfig
use std::{
    collections::HashMap,
    sync::Arc,
    time::{Duration, Instant},
};
use tokio::{
    io::AsyncWriteExt,
    sync::{broadcast, Mutex},
    time,
};
use tokio_tungstenite::{connect_async, tungstenite::protocol::Message};
use tracing::{debug, error, info, warn};

// Constants
pub const JUPITER_API: &str = "https://quote-api.jup.ag/v6";
pub const RAYDIUM_PROGRAM: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";
pub const SOL_MINT: &str = "So11111111111111111111111111111111111111112";

// Configuration Structures
#[derive(Debug, Deserialize, Clone)]
pub struct Config {
    pub rpc: RpcConfig,
    pub urls: UrlConfig,
    pub wallet: WalletConfig,
    pub api_keys: ApiKeys,
    pub trading: TradingConfig,
    pub token_requirements: TokenRequirements,
    pub websocket: WebsocketConfig,
    pub logging: LoggingConfig,
}

#[derive(Debug, Deserialize, Clone)]
pub struct RpcConfig {
    pub solana_rpc_url: String,
    pub fallback_url: String,
}

#[derive(Debug, Deserialize, Clone)]
pub struct UrlConfig {
    pub ws_url: String,
    pub rpc_url: String,
}

#[derive(Debug, Deserialize, Clone)]
pub struct WalletConfig {
    pub keypair_path: String,
    pub public_key: String,
}

#[derive(Debug, Deserialize, Clone)]
pub struct ApiKeys {
    pub helius_api_key: String,
}

#[derive(Debug, Deserialize, Clone)]
pub struct TradingConfig {
    pub snipe_amount: u64,
    pub max_slippage: f64,
    pub priority_fee: u64,
    pub profit_target_percent: f64,
    pub stop_loss_percent: f64,
    pub max_concurrent_trades: usize,
    pub retry_interval: u64,
    pub max_buy_attempts: u32,
    pub min_liquidity_usd: u64,
    pub max_liquidity_usd: u64,
    pub enabled: bool,
    pub execution_enabled: bool,
    pub max_retry_attempts: u32,
}

#[derive(Debug, Deserialize, Clone)]
pub struct TokenRequirements {
    pub min_supply: u64,
    pub liquidity_locked: bool,
    pub authority_revoked: bool,
}

#[derive(Debug, Deserialize, Clone)]
pub struct WebsocketConfig {
    pub reconnect_interval: u64,
    pub heartbeat_interval: u64,
}

#[derive(Debug, Deserialize, Clone)]
pub struct LoggingConfig {
    pub show_balances: bool,
    pub debug_mode: bool,
}

// Trading Structures
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Trade {
    pub token_mint: String,
    pub entry_price: f64,
    pub amount: u64,
    pub take_profit_price: f64,
    pub stop_loss_price: f64,
    pub timestamp: i64,
    pub status: TradeStatus,
    pub buy_signature: Option<String>,
    pub sell_signature: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TradeStatus {
    Active {
        current_price: f64,
        profit_percent: f64,
    },
    Completed {
        exit_price: f64,
        profit: f64,
    },
    Failed {
        reason: String,
    },
}

#[derive(Debug, Default)]
pub struct TradeStatistics {
    pub total_trades: u32,
    pub profitable_trades: u32,
    pub total_profit: f64,
    pub total_loss: f64,
    pub win_rate: f64,
}

#[derive(Debug, Clone)]
pub struct TradeMetrics {
    pub active_trades: usize,
    pub total_value_sol: f64,
    pub total_profit_percent: f64,
    pub total_loss_percent: f64,
    pub timestamp: chrono::DateTime<chrono::Utc>,
}

// Main Bot Structure
pub struct TradeBot {
    config: Config,
    rpc_client: Arc<RpcClient>,
    wallet: Arc<Keypair>,
    active_trades: Arc<Mutex<HashMap<String, Trade>>>,
    shutdown: broadcast::Sender<()>,
}
impl TradeBot {
    pub async fn new(config_path: &str) -> Result<Arc<Self>> {
        // Load config from file
        let config_content = tokio::fs::read_to_string(config_path).await?;
        let config: Config = serde_json::from_str(&config_content)?;
        
        // Initialize RPC client
        let rpc_client = Arc::new(RpcClient::new(config.rpc.solana_rpc_url.clone()));
        
        // Load wallet keypair
        let keypair_bytes = tokio::fs::read(&config.wallet.keypair_path).await?;
        let wallet = Arc::new(Keypair::from_bytes(&keypair_bytes)?);
        
        // Create shutdown channel
        let (shutdown_tx, _) = broadcast::channel(1);
        
        let bot = Arc::new(Self {
            config,
            rpc_client,
            wallet,
            active_trades: Arc::new(Mutex::new(HashMap::new())),
            shutdown: shutdown_tx,
        });
        
        Ok(bot)
    }
    pub async fn run(self: Arc<Self>) -> Result<()> {
        let mut shutdown_rx = self.shutdown.subscribe();
        
        info!("Starting bot with configuration:");
        info!("  ├── RPC URL: {}", self.config.urls.rpc_url);
        info!("  ├── Wallet: {}", self.config.wallet.public_key);
        info!("  └── Trading enabled: {}", self.config.trading.enabled);

        // Start monitoring tasks
        let monitor_handle = {
            let bot = self.clone();
            tokio::spawn(async move {
                while let Err(e) = bot.monitor_new_tokens().await {
                    error!("Monitor error: {}, restarting...", e);
                    time::sleep(Duration::from_secs(5)).await;
                }
            })
        };

        // Start trade management
        let trade_handle = {
            let bot = self.clone();
            tokio::spawn(async move {
                let mut interval = time::interval(Duration::from_millis(500));
                loop {
                    interval.tick().await;
                    if let Err(e) = bot.update_active_trades().await {
                        warn!("Trade update error: {}", e);
                    }
                }
            })
        };

        // Start metrics logging
        let metrics_handle = {
            let bot = self.clone();
            tokio::spawn(async move {
                let mut interval = time::interval(Duration::from_secs(60));
                loop {
                    interval.tick().await;
                    if let Err(e) = bot.log_periodic_metrics().await {
                        warn!("Metrics logging error: {}", e);
                    }
                }
            })
        };

        // Wait for shutdown signal
        tokio::select! {
            _ = shutdown_rx.recv() => {
                info!("Received shutdown signal");
            }
        }

        // Cleanup
        monitor_handle.abort();
        trade_handle.abort();
        metrics_handle.abort();

        // Final state save
        self.save_final_state().await?;

        info!("Bot shutdown complete");
        Ok(())
    }

    async fn log_periodic_metrics(&self) -> Result<()> {
        let balance = self.get_wallet_balance().await?;
        let active_trades = self.active_trades.lock().await.len();
        
        let mut file = tokio::fs::OpenOptions::new()
            .create(true)
            .append(true)
            .open("periodic_metrics.log")
            .await?;

        let metrics = format!(
            "[{}] Balance: {:.4} SOL, Active Trades: {}, Status: Active\n",
            Utc::now().format("%Y-%m-%d %H:%M:%S"),
            balance,
            active_trades
        );

        file.write_all(metrics.as_bytes()).await?;
        file.flush().await?;

        Ok(())
    }

    async fn save_final_state(&self) -> Result<()> {
        let trades = self.active_trades.lock().await;
        
        // Save active trades
        let state = json!({
            "timestamp": Utc::now().to_rfc3339(),
            "wallet_balance": self.get_wallet_balance().await?,
            "active_trades": &*trades,
        });

        tokio::fs::write(
            "bot_final_state.json",
            serde_json::to_string_pretty(&state)?
        ).await?;

        info!("Final state saved successfully");
        Ok(())
    }

    pub async fn monitor_new_tokens(&self) -> Result<()> {
        let ws_url = self.config.urls.ws_url.clone();
        
        loop {
            match connect_async(&ws_url).await {
                Ok((ws_stream, _)) => {
                    let (mut write, mut read) = ws_stream.split();
                    info!("📡 WebSocket connected");

                    // Subscribe to Raydium program
                    let subscribe_msg = json!({
                        "jsonrpc": "2.0",
                        "id": 1,
                        "method": "programSubscribe",
                        "params": [
                            RAYDIUM_PROGRAM,
                            {
                                "encoding": "jsonParsed",
                                "commitment": "confirmed"
                            }
                        ]
                    });

                    if let Err(e) = write.send(Message::Text(subscribe_msg.to_string())).await {
                        error!("WebSocket subscription failed: {}", e);
                        time::sleep(Duration::from_secs(5)).await;
                        continue;
                    }

                    info!("✅ Subscribed to Raydium program");

                    // Process incoming messages
                    while let Some(msg) = read.next().await {
                        match msg {
                            Ok(Message::Text(text)) => {
                                if let Err(e) = self.process_token_update(&text).await {
                                    warn!("Failed to process update: {}", e);
                                }
                            }
                            Ok(Message::Close(_)) => {
                                info!("WebSocket closed by server");
                                break;
                            }
                            Err(e) => {
                                error!("WebSocket error: {}", e);
                                break;
                            }
                            _ => {}
                        }
                    }
                }
                Err(e) => {
                    error!("WebSocket connection failed: {}", e);
                    time::sleep(Duration::from_secs(
                        self.config.websocket.reconnect_interval
                    )).await;
                }
            }

            // Check for shutdown signal
            if let Ok(()) = self.check_shutdown_signal().await {
                info!("Shutting down token monitor");
                break;
            }

            // Add reconnection delay
            time::sleep(Duration::from_secs(self.config.websocket.reconnect_interval)).await;
        }

        Ok(())
    }

    async fn check_shutdown_signal(&self) -> Result<()> {
        let mut shutdown_rx = self.shutdown.subscribe();
        tokio::select! {
            _ = shutdown_rx.recv() => Ok(()),
            _ = time::sleep(Duration::from_millis(100)) => Err(anyhow!("Continue monitoring"))
        }
    }

    async fn process_token_update(&self, message: &str) -> Result<()> {
        let data: Value = serde_json::from_str(message)?;
        
        if let Some(params) = data.get("params") {
            if let Some(token_mint) = self.extract_token_mint(params).await? {
                if self.is_valid_token(&token_mint).await? {
                    info!("🔍 Found new token: {}", token_mint);
                    self.handle_new_token(&token_mint).await?;
                }
            }
        }

        Ok(())
    }

    async fn extract_token_mint(&self, params: &Value) -> Result<Option<String>> {
        if let Some(account_data) = params
            .get("result")
            .and_then(|r| r.get("value"))
            .and_then(|v| v.get("data"))
            .and_then(|d| d.get("parsed"))
        {
            if let Some(mint) = account_data
                .get("info")
                .and_then(|i| i.get("tokenMint"))
                .and_then(|m| m.as_str())
            {
                return Ok(Some(mint.to_string()));
            }
        }
        Ok(None)
    }

    pub async fn update_active_trades(&self) -> Result<()> {
        let mut trades = self.active_trades.lock().await;
        let trades_to_update = trades.clone();

        for (token_mint, trade) in trades_to_update.iter() {
            let current_price = self.get_token_price(token_mint).await?;
            let profit_percent = (current_price - trade.entry_price) / trade.entry_price * 100.0;

            if profit_percent >= self.config.trading.profit_target_percent {
                match self.execute_trade(token_mint, false).await {
                    Ok(_) => {
                        trades.remove(token_mint);
                        info!("✅ Take profit executed for {} at {:.2}%", token_mint, profit_percent);
                        self.log_trade_completion(token_mint, profit_percent, "Take Profit").await?;
                        self.update_metrics(profit_percent).await?;
                    }
                    Err(e) => warn!("Failed to execute take profit for {}: {}", token_mint, e),
                }
            } else if profit_percent <= -self.config.trading.stop_loss_percent {
                match self.execute_trade(token_mint, false).await {
                    Ok(_) => {
                        trades.remove(token_mint);
                        warn!("⚠️ Stop loss executed for {} at {:.2}%", token_mint, profit_percent);
                        self.log_trade_completion(token_mint, profit_percent, "Stop Loss").await?;
                        self.update_metrics(profit_percent).await?;
                    }
                    Err(e) => error!("Failed to execute stop loss for {}: {}", token_mint, e),
                }
            }

            if let Some(active_trade) = trades.get_mut(token_mint) {
                active_trade.status = TradeStatus::Active {
                    current_price,
                    profit_percent,
                };
            }
        }

        Ok(())
    }

    async fn update_metrics(&self, profit_percent: f64) -> Result<()> {
        let mut file = tokio::fs::OpenOptions::new()
            .create(true)
            .append(true)
            .open("trading_metrics.log")
            .await?;

        let balance = self.get_wallet_balance().await?;
        let timestamp = Utc::now();
        let metrics_entry = format!(
            "[{}] Trade completed - P/L: {:.2}% - Balance: {:.4} SOL",
            timestamp.format("%Y-%m-%d %H:%M:%S"),
            profit_percent,
            balance
        );

        file.write_all(format!("{}\n", metrics_entry).as_bytes()).await?;
        file.flush().await?;

        // Update daily statistics
        self.update_daily_stats(profit_percent, balance).await?;

        Ok(())
    }

    async fn update_daily_stats(&self, profit_percent: f64, balance: f64) -> Result<()> {
        let mut file = tokio::fs::OpenOptions::new()
            .create(true)
            .append(true)
            .open("daily_stats.log")
            .await?;

        let today = Utc::now().format("%Y-%m-%d").to_string();
        let stats = format!(
            "{},{:.2},{:.4},{}\n",
            today,
            profit_percent,
            balance,
            Utc::now().timestamp()
        );

        file.write_all(stats.as_bytes()).await?;
        file.flush().await?;

        Ok(())
    }

    async fn log_trade_completion(&self, token_mint: &str, profit_percent: f64, reason: &str) -> Result<()> {
        let mut file = tokio::fs::OpenOptions::new()
            .create(true)
            .append(true)
            .open("trade_history.log")
            .await?;

        let log_entry = format!(
            "[{}] {} - {} - Profit: {:.2}% - Balance: {:.4} SOL",
            Utc::now().format("%Y-%m-%d %H:%M:%S"),
            reason,
            token_mint,
            profit_percent,
            self.get_wallet_balance().await?
        );

        file.write_all(format!("{}\n", log_entry).as_bytes()).await?;
        file.flush().await?;

        Ok(())
    }

    // Required methods from previous implementation...
    pub async fn get_wallet_balance(&self) -> Result<f64> {
        let balance = self.rpc_client.get_balance(&self.wallet.pubkey())?;
        Ok(balance as f64 / 1e9)
    }
    pub async fn get_trade_statistics(&self) -> Result<TradeStatistics> {
        let trades = self.active_trades.lock().await;
        let mut stats = TradeStatistics::default();

        for trade in trades.values() {
            if let TradeStatus::Active { profit_percent, .. } = trade.status {
                stats.total_trades += 1;
                if profit_percent > 0.0 {
                    stats.profitable_trades += 1;
                    stats.total_profit += profit_percent;
                } else {
                    stats.total_loss += profit_percent.abs();
                }
            }
        }

        if stats.total_trades > 0 {
            stats.win_rate = (stats.profitable_trades as f64 / stats.total_trades as f64) * 100.0;
        }

        Ok(stats)
    }
    pub async fn save_state(&self) -> Result<()> {
        let trades = self.active_trades.lock().await;
        let state = serde_json::to_string_pretty(&*trades)?;
        tokio::fs::write("bot_state.json", state).await?;
        
        info!("💾 Bot state saved successfully");
        Ok(())
    }

    pub async fn get_network_status(&self) -> Result<String> {
        let version = self.rpc_client.get_version()?;
        let cluster_nodes = self.rpc_client.get_cluster_nodes()?;
        
        Ok(format!("Solana {} - {} (Connected to {} nodes)", 
            version.solana_core,
            self.config.urls.rpc_url,
            cluster_nodes.len()
        ))
    }

    pub async fn get_trade_metrics(&self) -> Result<TradeMetrics> {
        let trades = self.active_trades.lock().await;
        
        let total_active = trades.len();
        let total_value: f64 = trades.values()
            .map(|t| t.amount as f64 / 1e9)
            .sum();

        let (total_profit, total_loss) = trades.values()
            .filter_map(|t| {
                if let TradeStatus::Active { profit_percent, .. } = t.status {
                    Some(profit_percent)
                } else {
                    None
                }
            })
            .fold((0.0, 0.0), |(profit, loss), pct| {
                if pct > 0.0 {
                    (profit + pct, loss)
                } else {
                    (profit, loss + pct.abs())
                }
            });

        Ok(TradeMetrics {
            active_trades: total_active,
            total_value_sol: total_value,
            total_profit_percent: total_profit,
            total_loss_percent: total_loss,
            timestamp: chrono::Utc::now(),
        })
    }

    pub async fn get_active_trades(&self) -> Result<Vec<Trade>> {
        let trades = self.active_trades.lock().await;
        Ok(trades.values().cloned().collect())
    }
    async fn get_token_price(&self, token_mint: &str) -> Result<f64> {
        let url = format!("{}/price?ids={}&vsToken=SOL", JUPITER_API, token_mint);

        let client = reqwest::Client::new();
        let response = client.get(&url)
            .send()
            .await?
            .json::<Value>()
            .await?;

        response["data"][token_mint]["price"]
            .as_f64()
            .ok_or_else(|| anyhow!("Price not found"))
    }
    pub async fn shutdown(&self) -> Result<()> {
        let _ = self.shutdown.send(());
        Ok(())
    }
    async fn is_valid_token(&self, token_mint: &str) -> Result<bool> {
        // Check if already trading
        let active_trades = self.active_trades.lock().await;
        if active_trades.contains_key(token_mint) {
            return Ok(false);
        }
        drop(active_trades);

        // Get token metadata using RPC
        let metadata = self.get_token_metadata(token_mint).await?;
        
        // Check supply requirement
        if metadata.supply < self.config.token_requirements.min_supply {
            return Ok(false);
        }

        // Check authority revocation (simplified boolean expression)
        if metadata.mint_authority.is_some() && self.config.token_requirements.authority_revoked {
            return Ok(false);
        }

        // Check liquidity
        let pool_info = self.get_pool_info(token_mint).await?;
        let sol_price = self.get_sol_price().await?;
        let liquidity_usd = pool_info.liquidity * sol_price;

        if liquidity_usd < self.config.trading.min_liquidity_usd as f64 
            || liquidity_usd > self.config.trading.max_liquidity_usd as f64 {
            return Ok(false);
        }

        // Check if liquidity is locked
        if !pool_info.liquidity_locked && self.config.token_requirements.liquidity_locked {
            return Ok(false);
        }

        Ok(true)
    }

    pub async fn execute_trade(&self, token_mint: &str, is_buy: bool) -> Result<String> {
        let start = Instant::now();
        let trade_type = if is_buy { "BUY" } else { "SELL" };
        
        let (input_mint, output_mint, amount) = if is_buy {
            (SOL_MINT, token_mint, self.config.trading.snipe_amount)
        } else {
            (token_mint, SOL_MINT, self.config.trading.snipe_amount)
        };

        // Get quote from Jupiter v6
        let quote = self.get_jupiter_quote(input_mint, output_mint, amount).await?;
        debug!("⚡ Quote received in {:?}", start.elapsed());

        // Build swap with Jupiter v6
        let swap_data = self.build_v6_swap(&quote).await?;
        debug!("⚡ Swap built in {:?}", start.elapsed());

        // Send priority transaction
        let signature = self.send_priority_transaction(&swap_data).await?;
        info!("✨ {} complete - TX: {} ({:?})", trade_type, signature, start.elapsed());

        Ok(signature)
    }


    async fn get_jupiter_quote(&self, input_mint: &str, output_mint: &str, amount: u64) -> Result<Value> {
        let url = format!(
            "{}/quote?inputMint={}&outputMint={}&amount={}&slippageBps={}&onlyDirectRoutes=true",
            JUPITER_API,
            input_mint,
            output_mint,
            amount,
            (self.config.trading.max_slippage * 100.0) as u32
        );

        let response = reqwest::Client::new()
            .get(&url)
            .send()
            .await?
            .json::<Value>()
            .await?;

        Ok(response)
    }

    async fn build_v6_swap(&self, quote: &Value) -> Result<String> {
        let swap_request = json!({
            "quoteResponse": quote,
            "userPublicKey": self.config.wallet.public_key,
            "wrapUnwrapSOL": true,
            "computeUnitPriceMicroLamports": self.config.trading.priority_fee,
            "priorityFeeLamports": self.config.trading.priority_fee,
            "asLegacyTransaction": false,
            "maxAccounts": 64
        });

        let response = reqwest::Client::new()
            .post(format!("{}/swap", JUPITER_API))
            .json(&swap_request)
            .send()
            .await?
            .json::<Value>()
            .await?;

        match response.get("swapTransaction") {
            Some(tx) => Ok(tx.as_str()
                .ok_or_else(|| anyhow!("Invalid transaction format"))?
                .to_string()),
            None => Err(anyhow!("No swap transaction in response"))
        }
    }

    async fn send_priority_transaction(&self, swap_data: &str) -> Result<String> {
        let helius_request = json!({
            "jsonrpc": "2.0",
            "id": "helius-priority",
            "method": "sendTransaction",
            "params": [
                swap_data,
                {
                    "encoding": "base64",
                    "maxRetries": self.config.trading.max_retry_attempts,
                    "skipPreflight": true,
                    "preflightCommitment": "processed",
                    "priorityFee": self.config.trading.priority_fee,
                    "maxAccountLocks": 64
                }
            ]
        });

        let client = reqwest::Client::new();
        let response = client.post(&self.config.urls.rpc_url)
            .header("Authorization", format!("Bearer {}", self.config.api_keys.helius_api_key))
            .json(&helius_request)
            .send()
            .await?
            .json::<Value>()
            .await?;

        match response.get("result") {
            Some(result) => Ok(result.as_str()
                .ok_or_else(|| anyhow!("Invalid signature format"))?
                .to_string()),
            None => {
                if let Some(error) = response.get("error") {
                    Err(anyhow!("Helius API error: {:?}", error))
                } else {
                    Err(anyhow!("Transaction failed without error details"))
                }
            }
        }
    }

    async fn handle_new_token(&self, token_mint: &str) -> Result<()> {
        info!("🎯 Found new valid token: {}", token_mint);

        // Try to execute buy with retry logic
        for attempt in 1..=self.config.trading.max_buy_attempts {
            match self.execute_trade(token_mint, true).await {
                Ok(signature) => {
                    info!("✅ Successfully bought {} on attempt {}", token_mint, attempt);
                    
                    // Get entry price
                    let price = self.get_token_price(token_mint).await?;
                    
                    // Create trade object
                    let trade = Trade {
                        token_mint: token_mint.to_string(),
                        entry_price: price,
                        amount: self.config.trading.snipe_amount,
                        take_profit_price: price * (1.0 + self.config.trading.profit_target_percent / 100.0),
                        stop_loss_price: price * (1.0 - self.config.trading.stop_loss_percent / 100.0),
                        timestamp: Utc::now().timestamp(),
                        status: TradeStatus::Active {
                            current_price: price,
                            profit_percent: 0.0,
                        },
                        buy_signature: Some(signature),
                        sell_signature: None,
                    };

                    // Add to active trades
                    let mut active_trades = self.active_trades.lock().await;
                    active_trades.insert(token_mint.to_string(), trade);
                    
                    return Ok(());
                }
                Err(e) => {
                    warn!("Buy attempt {} failed: {}", attempt, e);
                    if attempt < self.config.trading.max_buy_attempts {
                        time::sleep(Duration::from_secs(self.config.trading.retry_interval)).await;
                    }
                }
            }
        }

        warn!("Failed to buy {} after {} attempts", token_mint, self.config.trading.max_buy_attempts);
        Ok(())
    }

    async fn get_token_metadata(&self, token_mint: &str) -> Result<TokenMetadata> {
        let url = format!("{}/v0/token-metadata?apiKey={}", 
            self.config.urls.rpc_url, self.config.api_keys.helius_api_key);

        let client = reqwest::Client::new();
        let response = client.get(&url)
            .query(&[("mintAccounts", token_mint)])
            .send()
            .await?
            .json::<Value>()
            .await?;

        Ok(TokenMetadata {
            supply: response["supply"].as_u64().unwrap_or_default(),
            mint_authority: response["mintAuthority"].as_str().map(String::from),
        })
    }

    async fn get_pool_info(&self, token_mint: &str) -> Result<PoolInfo> {
        let url = format!("{}/v0/protocols/raydium/pools?apiKey={}", 
            self.config.urls.rpc_url, self.config.api_keys.helius_api_key);

        let client = reqwest::Client::new();
        let response = client.get(&url)
            .query(&[("tokenMint", token_mint)])
            .send()
            .await?
            .json::<Value>()
            .await?;

        Ok(PoolInfo {
            liquidity: response["liquiditySol"].as_f64().unwrap_or_default(),
            liquidity_locked: response["liquidityLocked"].as_bool().unwrap_or_default(),
        })
    }

    async fn get_sol_price(&self) -> Result<f64> {
        let response = reqwest::Client::new()
            .get("https://price.jup.ag/v6/price?ids=SOL")
            .send()
            .await?
            .json::<Value>()
            .await?;

        Ok(response["data"]["SOL"]["price"].as_f64().unwrap_or(0.0))
    }
}

#[derive(Debug)]
struct TokenMetadata {
    supply: u64,
    mint_authority: Option<String>,
}

#[derive(Debug)]
struct PoolInfo {
    liquidity: f64,
    liquidity_locked: bool,
}use {
    solana_client::rpc_client::RpcClient,
    solana_sdk::{
        commitment_config::CommitmentConfig, pubkey::Pubkey, signature::{Keypair, read_keypair_file},
        transaction::Transaction, instruction::{AccountMeta, Instruction}, system_instruction, signer::Signer,
    },
    solana_program::program_pack::Pack,
    spl_token, spl_associated_token_account,
    serde::{Deserialize, Serialize},
    anyhow::{Result, anyhow},
    log::{info, warn, error, debug},
    tokio_tungstenite::{connect_async, tungstenite::protocol::Message},
    futures_util::{StreamExt, SinkExt},
    std::{str::FromStr, sync::Arc, collections::{HashMap, HashSet}, fs},
    tokio::{sync::Mutex, time::{Duration, interval}},
    reqwest::Client as HttpClient,
};

#[allow(dead_code)]
#[derive(Debug, Deserialize, Clone)]
struct Config {
    rpc: RpcConfig, urls: UrlConfig, wallet: WalletConfig, api_keys: ApiKeysConfig,
    accounts: AccountsConfig, trading: TradingConfig, websocket: WebSocketConfig,
}

#[allow(dead_code)]
#[derive(Debug, Deserialize, Clone)]
struct RpcConfig { solana_rpc_url: String, fallback_url: String }

#[allow(dead_code)]
#[derive(Debug, Deserialize, Clone)]
struct UrlConfig { ws_url: String, rpc_url: String }

#[allow(dead_code)]
#[derive(Debug, Deserialize, Clone)]
struct WalletConfig { public_key: String, keypair_path: String }

#[allow(dead_code)]
#[derive(Debug, Deserialize, Clone)]
struct ApiKeysConfig { helius_api_key: String }

#[derive(Debug, Deserialize, Clone)]
struct AccountsConfig { wsol_account: String }

#[allow(dead_code)]
#[derive(Debug, Deserialize, Clone)]
struct TradingConfig {
    min_liquidity: u64, max_liquidity: u64, snipe_amount: u64,
    profit_target_percent: f64, max_retry_attempts: u8, stop_loss_percent: f64,
    max_slippage: f64, priority_fee: u64, min_sol_balance: f64,
}

#[derive(Debug, Deserialize, Clone)]
struct WebSocketConfig { ping_interval: u64, reconnect_interval: u64 }

struct Bot {
    rpc_client: Arc<RpcClient>, http_client: HttpClient, keypair: Keypair, config: Config,
    token_prices: Arc<Mutex<HashMap<String, f64>>>,
    active_trades: Arc<Mutex<HashMap<String, Trade>>>,
    blacklisted_tokens: Arc<Mutex<HashSet<String>>>,
}

#[allow(dead_code)]
#[derive(Debug, Deserialize)]
struct HeliusEvent { description: String, token_address: String, pool_address: String }

#[derive(Debug, Deserialize)]
struct PriceUpdate { token: String, price: f64 }

#[derive(Debug, Clone, Serialize, Deserialize)]
struct Trade {
    token_address: String, pool_address: String, initial_investment: f64,
    amount: u64, buy_price: f64, signature: String,
}

impl Bot {
    fn new(config_path: &str) -> Result<Self> {
        let config: Config = toml::from_str(&fs::read_to_string(config_path)?)?;
        let rpc_client = Arc::new(RpcClient::new_with_commitment(
            &config.rpc.solana_rpc_url, CommitmentConfig::confirmed()));
        let keypair = read_keypair_file(&config.wallet.keypair_path)
            .map_err(|e| anyhow!("Failed to read keypair: {}", e))?;
        Ok(Self {
            rpc_client, http_client: HttpClient::new(), keypair, config,
            token_prices: Arc::new(Mutex::new(HashMap::new())),
            active_trades: Arc::new(Mutex::new(HashMap::new())),
            blacklisted_tokens: Arc::new(Mutex::new(HashSet::new())),
        })
    }
    async fn run(&self) -> Result<()> {
        info!("Starting Solana Trading Bot");
        let ws_handle = tokio::spawn(self.clone().run_websocket());

        let trade_manager_bot = self.clone();
        let trade_manager_handle = tokio::spawn(async move {
            trade_manager_bot.manage_trades().await;
        });

        let state_saver_bot = self.clone();
        let state_saver_handle = tokio::spawn(async move {
            state_saver_bot.save_state_periodically().await;
        });

        let (ws_result, trade_manager_result, state_saver_result) = tokio::join!(
            ws_handle,
            trade_manager_handle,
            state_saver_handle
        );

        ws_result.map_err(|e| anyhow!("WebSocket task failed: {}", e))??;

        if let Err(e) = trade_manager_result {
            error!("Trade manager task failed: {}", e);
        }

        if let Err(e) = state_saver_result {
            error!("State saver task failed: {}", e);
        }

        Ok(())
    }
    async fn run_websocket(self) -> Result<()> {
        loop {
            let (ws_stream, _) = connect_async(&self.config.urls.ws_url).await?;
            let (mut write, mut read) = ws_stream.split();
            for method in &["subscribePoolCreations", "subscribeTokenPrices"] {
                write.send(Message::Text(serde_json::json!({
                    "jsonrpc": "2.0", "id": 1, "method": method, "params": []
                }).to_string())).await?;
            }
            let mut ping_interval = interval(Duration::from_secs(self.config.websocket.ping_interval));
            loop {
                tokio::select! {
                    message = read.next() => match message {
                        Some(Ok(Message::Text(text))) => {
                            let event: serde_json::Value = serde_json::from_str(&text)?;
                            if let Some(method) = event["method"].as_str() {
                                match method {
                                    "poolCreation" => if let Ok(pool_event) = serde_json::from_value::<HeliusEvent>(event["params"].clone()) {
                                        if let Err(e) = self.handle_pool_event(&pool_event).await {
                                            error!("Failed to handle pool event: {}", e);
                                        }
                                    },
                                    "tokenPrice" => if let Ok(price_update) = serde_json::from_value::<PriceUpdate>(event["params"].clone()) {
                                        if let Err(e) = self.update_token_price(&price_update.token, price_update.price).await {
                                            error!("Failed to update token price: {}", e);
                                        }
                                    },
                                    _ => {}
                                }
                            }
                        },
                        Some(Err(e)) => {
                            error!("WebSocket error: {}. Reconnecting...", e);
                            break;
                        },
                        None => {
                            info!("WebSocket connection closed. Reconnecting...");
                            break;
                        },
                        _ => {}
                    },
                    _ = ping_interval.tick() => { write.send(Message::Ping(vec![])).await?; }
                }
            }
            tokio::time::sleep(Duration::from_secs(self.config.websocket.reconnect_interval)).await;
        }
    }
}
impl Bot {
    async fn handle_pool_event(&self, event: &HeliusEvent) -> Result<()> {
        info!("New pool detected: {}", event.token_address);
        if self.verify_pool(event).await? {
            self.execute_trade(event).await?;
        } else {
            debug!("Pool verification failed for token: {}", event.token_address);
        }
        Ok(())
    }

    async fn verify_pool(&self, event: &HeliusEvent) -> Result<bool> {
        let token_pubkey = Pubkey::from_str(&event.token_address)?;
        let pool_pubkey = Pubkey::from_str(&event.pool_address)?;
        if self.blacklisted_tokens.lock().await.contains(&event.token_address) {
            return Ok(false);
        }
        let mint_account = self.rpc_client.get_account(&token_pubkey)?;
        let mint = spl_token::state::Mint::unpack(&mint_account.data)?;
        let is_mint_authority_revoked = mint.mint_authority.is_none();
        let pool_token_account = spl_associated_token_account::get_associated_token_address(
            &pool_pubkey, &Pubkey::from_str("So11111111111111111111111111111111111111112")?);
        let pool_balance = self.rpc_client.get_token_account_balance(&pool_token_account)?;
        let liquidity = pool_balance.amount.parse::<u64>()?;
        let is_liquidity_valid = liquidity >= self.config.trading.min_liquidity && liquidity <= self.config.trading.max_liquidity;
        if !is_liquidity_valid {
            self.blacklist_token(&event.token_address).await?;
            return Ok(false);
        }
        Ok(is_mint_authority_revoked && is_liquidity_valid)
    }

    async fn execute_trade(&self, event: &HeliusEvent) -> Result<()> {
        let pool_pubkey = Pubkey::from_str(&event.pool_address)?;
        let token_pubkey = Pubkey::from_str(&event.token_address)?;
       

        // Use these correctly in your transactions
        let swap_instruction = Instruction::new_with_bytes(
            pool_pubkey,
            &[0],
            vec![
                AccountMeta::new(pool_pubkey, false),
                AccountMeta::new(token_pubkey, false),
                AccountMeta::new(wsol_mint, false),  // Use the mint address here
                AccountMeta::new(your_wsol_account, false),  // Use your account here
                AccountMeta::new(self.keypair.pubkey(), true),
            ],
        );
        let priority_fee_instruction = system_instruction::transfer(
            &self.keypair.pubkey(),
            &Pubkey::from_str("So11111111111111111111111111111111111111112")?,
            self.config.trading.priority_fee,
        );
        let recent_blockhash = self.rpc_client.get_latest_blockhash()?;
        let transaction = Transaction::new_signed_with_payer(
            &[priority_fee_instruction, swap_instruction],
            Some(&self.keypair.pubkey()),
            &[&self.keypair],
            recent_blockhash,
        );
        let signature = self.retry_operation(|| {
            self.rpc_client.send_and_confirm_transaction_with_spinner(&transaction)
                .map_err(|e| anyhow!("Transaction error: {}", e))
        }).await?;
        info!("Trade executed for token {}: {}", event.token_address, signature);
        let current_price = self.get_token_price(&event.token_address).await?;
        let trade = Trade {
            token_address: event.token_address.clone(),
            pool_address: event.pool_address.clone(),
            initial_investment: self.config.trading.snipe_amount as f64 / 1e9,
            amount: self.config.trading.snipe_amount,
            buy_price: current_price,
            signature: signature.to_string(),
        };
        self.active_trades.lock().await.insert(event.token_address.clone(), trade);
        self.log_active_trades().await;
        Ok(())
    }

    async fn update_token_price(&self, token: &str, price: f64) -> Result<()> {
        self.token_prices.lock().await.insert(token.to_string(), price);
        self.check_active_trades(token, price).await
    }

    async fn check_active_trades(&self, token_address: &str, current_price: f64) -> Result<()> {
        let active_trades = self.active_trades.lock().await;
        if let Some(trade) = active_trades.get(token_address) {
            let profit_percent = (current_price - trade.buy_price) / trade.buy_price * 100.0;
            if profit_percent >= self.config.trading.profit_target_percent ||
               profit_percent <= -self.config.trading.stop_loss_percent {
                let pool_address = trade.pool_address.clone();
                drop(active_trades);
                info!("{} target reached for {}. Selling...",
                      if profit_percent > 0.0 { "Profit" } else { "Stop loss" }, token_address);
                if let Err(e) = self.sell_token(token_address, &pool_address).await {
                    error!("Failed to sell token {}: {}", token_address, e);
                } else {
                    self.active_trades.lock().await.remove(token_address);
                }
            }
        }
        Ok(())
    }

    async fn sell_token(&self, token_address: &str, pool_address: &str) -> Result<()> {
        let token_pubkey = Pubkey::from_str(token_address)?;
        let pool_pubkey = Pubkey::from_str(pool_address)?;
        let wsol_pubkey = Pubkey::from_str(&self.config.accounts.wsol_account)?;
        let token_account = spl_associated_token_account::get_associated_token_address(
            &self.keypair.pubkey(), &token_pubkey);
        let sell_instruction = Instruction::new_with_bytes(
            pool_pubkey,
            &[1],
            vec![
                AccountMeta::new(token_account, false),
                AccountMeta::new(wsol_pubkey, false),
                AccountMeta::new(self.keypair.pubkey(), true),
            ],
        );
        let priority_fee_instruction = system_instruction::transfer(
            &self.keypair.pubkey(),
            &Pubkey::from_str("So11111111111111111111111111111111111111112")?,
            self.config.trading.priority_fee,
        );
        let recent_blockhash = self.rpc_client.get_latest_blockhash()?;
        let transaction = Transaction::new_signed_with_payer(
            &[priority_fee_instruction, sell_instruction],
            Some(&self.keypair.pubkey()),
            &[&self.keypair],
            recent_blockhash,
        );
        let signature = self.retry_operation(|| {
            self.rpc_client.send_and_confirm_transaction_with_spinner(&transaction)
                .map_err(|e| anyhow!("Transaction error: {}", e))
        }).await?;
        info!("Token {} sold successfully. Signature: {}", token_address, signature);
        self.log_active_trades().await;
        Ok(())
    }

    async fn get_token_price(&self, token_address: &str) -> Result<f64> {
        self.token_prices.lock().await.get(token_address).cloned()
            .ok_or_else(|| anyhow!("Price not found for token"))
    }

    async fn log_active_trades(&self) {
        let active_trades = self.active_trades.lock().await;
        info!("Current Active Trades:");
        for (token_address, trade) in active_trades.iter() {
            info!("Token: {}, Investment: {} SOL, Buy Price: {}, Transaction: {}",
                  token_address, trade.initial_investment, trade.buy_price, trade.signature);
        }
    }

    async fn manage_trades(&self) {
        let mut interval = interval(Duration::from_secs(60));
        loop {
            interval.tick().await;
            self.log_active_trades().await;
            let trades_to_check: Vec<(String, String, f64)> = {
                let active_trades = self.active_trades.lock().await;
                active_trades.iter()
                    .map(|(token_address, trade)| (token_address.clone(), trade.pool_address.clone(), trade.buy_price))
                    .collect()
            };
            for (token_address, pool_address, buy_price) in trades_to_check {
                if let Ok(current_price) = self.get_token_price(&token_address).await {
                    let profit_percent = (current_price - buy_price) / buy_price * 100.0;
                    info!("Token: {}, Current Price: {}, Profit: {:.2}%", token_address, current_price, profit_percent);
                    if profit_percent >= self.config.trading.profit_target_percent ||
                       profit_percent <= -self.config.trading.stop_loss_percent {
                        if let Err(e) = self.sell_token(&token_address, &pool_address).await {
                            error!("Failed to sell token {}: {}", token_address, e);
                        }
                    }
                }
            }
        }
    }

    async fn blacklist_token(&self, token_address: &str) -> Result<()> {
        self.blacklisted_tokens.lock().await.insert(token_address.to_string());
        info!("Token blacklisted: {}", token_address);
        Ok(())
    }

    async fn retry_operation<F, T>(&self, operation: F) -> Result<T>
    where
        F: Fn() -> Result<T>,
    {
        let mut backoff = 1;
        for attempt in 1..=self.config.trading.max_retry_attempts {
            match operation() {
                Ok(result) => return Ok(result),
                Err(e) if attempt < self.config.trading.max_retry_attempts => {
                    warn!("Attempt {} failed: {}. Retrying in {} seconds...", attempt, e, backoff);
                    tokio::time::sleep(Duration::from_secs(backoff)).await;
                    backoff *= 2;
                }
                Err(e) => return Err(e),
            }
        }
        Err(anyhow!("Operation failed after maximum retry attempts"))
    }

    async fn save_state_periodically(&self) {
        let mut interval = interval(Duration::from_secs(300));
        loop {
            interval.tick().await;
            if let Err(e) = self.save_state().await {
                error!("Failed to save state: {}", e);
            }
        }
    }

    async fn save_state(&self) -> Result<()> {
        let state = serde_json::json!({
            "active_trades": *self.active_trades.lock().await,
            "blacklisted_tokens": self.blacklisted_tokens.lock().await.iter().cloned().collect::<Vec<String>>()
        });
        fs::write("bot_state.json", serde_json::to_string(&state)?)?;
        debug!("Bot state saved successfully");
        Ok(())
    }
}

impl Clone for Bot {
    fn clone(&self) -> Self {
        Self {
            rpc_client: Arc::clone(&self.rpc_client),
            http_client: self.http_client.clone(),
            keypair: Keypair::from_bytes(&self.keypair.to_bytes()).unwrap(),
            config: self.config.clone(),
            token_prices: Arc::clone(&self.token_prices),
            active_trades: Arc::clone(&self.active_trades),
            blacklisted_tokens: Arc::clone(&self.blacklisted_tokens),
        }
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    env_logger::init();
    let bot = Bot::new("config.toml")?;
    bot.run().await
} 
 can u do me a favor and just write all the very best functions that i have for a trade bot all the best working code for my best strongest functions?