what does this do use anyhow::{anyhow, Result};
use base64;
use bincode;
use config::{Config, File};
use ctrlc;
use env_logger::Env;
use futures_util::{SinkExt, StreamExt};
use log::{debug, error, info, warn};
use reqwest::Client as HttpClient;
use serde::{Deserialize, Serialize};
use serde_json::json;
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
    commitment_config::CommitmentConfig,
    hash::Hash,
    signer::{keypair::Keypair, Signer},
    transaction::Transaction,
};
use std::{
    collections::HashMap,
    fs::OpenOptions,
    io::{Read, Write},
    sync::{Arc, Mutex as StdMutex},
    time::{Duration, Instant},
};
use tokio::{sync::Mutex, time::interval};
use tokio_tungstenite::{connect_async, tungstenite::protocol::Message as WsMessage};

// Constants
const WSOL_MINT: &str = "So11111111111111111111111111111111111111112";
const RAYDIUM_AMM_PROGRAM_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";
const MAX_ACTIVE_TRADES: usize = 5;
const BUY_ATTEMPT_DELAY: Duration = Duration::from_millis(200);
const STATE_FILE: &str = "bot_state.json";
const PRICE_CHECK_INTERVAL: Duration = Duration::from_secs(1);

// Hardcoded Keypair and Config Paths
const KEYPAIR_PATH: &str = "C:\\solana_rust_bot\\id.json";
const CONFIG_PATH: &str = "C:\\solana_rust_bot\\config.toml";

// Hardcoded Trading Parameters
const SNIPE_AMOUNT: f64 = 0.005; // SOL
const PROFIT_TARGET_PERCENT: f64 = 30.0; // 30%
const MAX_RETRY_ATTEMPTS: u32 = 3;
const STOP_LOSS_PERCENT: f64 = 5.0; // 5%

#[derive(Debug, Deserialize, Clone)]
pub struct BotConfig {
    rpc_url: String,
    helius_websocket_url: String,
    helius_api_key: String,
    max_slippage: f64,
    priority_fee: u64,
    min_liquidity: f64,
    max_liquidity: f64,
    min_sol_balance: f64,
}

fn load_config() -> Result<BotConfig> {
    let settings = Config::builder()
        .add_source(File::with_name(CONFIG_PATH))
        .build()?;
    let mut config: BotConfig = settings.try_deserialize().map_err(|e| anyhow::Error::from(e))?;

    // Set the max_slippage field from the configuration file
    config.max_slippage = config.max_slippage;

    Ok(config)
}

fn load_keypair() -> Result<Keypair> {
    let data = std::fs::read(KEYPAIR_PATH)?;
    let json: Vec<u8> = serde_json::from_slice(&data)?;
    let keypair = Keypair::from_bytes(&json)?;
    Ok(keypair)
}

#[derive(Clone, Serialize, Deserialize, Debug)]
struct Trade {
    token_address: String,
    amount: u64,
    buy_price: f64,
    take_profit_price: f64,
    stop_loss_price: f64,
    timestamp: u64,
}

struct PriceCache {
    prices: HashMap<String, (f64, Instant)>,
    ttl: Duration,
}

impl PriceCache {
    fn new(ttl: u64) -> Self {
        Self {
            prices: HashMap::new(),
            ttl: Duration::from_secs(ttl),
        }
    }

    fn get(&self, token_address: &str) -> Option<f64> {
        self.prices
            .get(token_address)
            .and_then(|(price, timestamp)| {
                if timestamp.elapsed() < self.ttl {
                    Some(*price)
                } else {
                    None
                }
            })
    }

    fn set(&mut self, token_address: String, price: f64) {
        self.prices
            .insert(token_address.clone(), (price, Instant::now()));
        debug!("Price cache updated: {} = {}", token_address, price);
    }
}

struct Bot {
    config: BotConfig,
    rpc_client: Arc<RpcClient>,
    keypair: Arc<Keypair>,
    price_cache: Arc<Mutex<PriceCache>>,
    active_trades: Arc<Mutex<HashMap<String, Trade>>>,
    last_blockhash: Arc<Mutex<(Hash, Instant)>>,
    swap_limiter: Arc<Mutex<()>>,
    http_client: HttpClient,
}

impl Bot {
    async fn new() -> Result<Self> {
        info!("Loading configuration from environment file");
        let config = load_config()?;
        info!("Config loaded: {:?}", config);

        let rpc_client = Arc::new(RpcClient::new_with_commitment(
            config.rpc_url.clone(),
            CommitmentConfig::processed(),
        ));
        let http_client = HttpClient::builder()
            .timeout(Duration::from_secs(10))
            .build()?;
        let keypair = Arc::new(load_keypair()?);
        debug!("Keypair successfully loaded.");

        let bot = Self {
            config,
            rpc_client,
            keypair,
            price_cache: Arc::new(Mutex::new(PriceCache::new(60))),
            active_trades: Arc::new(Mutex::new(HashMap::new())),
            last_blockhash: Arc::new(Mutex::new((Hash::default(), Instant::now()))),
            swap_limiter: Arc::new(Mutex::new(())),
            http_client,
        };
        bot.load_state().await?;
        info!("Bot initialized successfully.");
        Ok(bot)
    }

    async fn run(&self) -> Result<()> {
        info!("Starting Raydium Sniper Bot");

        let initial_balance = self.get_sol_balance().await?;
        info!("Initial SOL balance: {} SOL", initial_balance as f64 / 1e9);

        loop {
            info!("Attempting to connect to Helius WebSocket...");
            match connect_async(&self.config.helius_websocket_url).await {
                Ok((ws_stream, _)) => {
                    let (mut write, mut read) = ws_stream.split();
                    info!("Connected to Helius WebSocket");

                    write
                        .send(WsMessage::Text(
                            json!({
                                "jsonrpc": "2.0",
                                "id": 1,
                                "method": "logsSubscribe",
                                "params": {
                                    "filter": {
                                        "programId": RAYDIUM_AMM_PROGRAM_ID
                                    },
                                    "commitment": "confirmed"
                                }
                            })
                            .to_string(),
                        ))
                        .await?;
                    info!("Subscribed to Raydium AMM logs");

                    let mut interval = interval(PRICE_CHECK_INTERVAL);
                    loop {
                        tokio::select! {
                            Some(msg) = read.next() => {
                                match msg {
                                    Ok(WsMessage::Text(text)) => {
                                        debug!("Received WebSocket message: {}", text);
                                        if let Err(e) = self.handle_event(&text).await {
                                            error!("Error handling event: {}", e);
                                        }
                                    }
                                    Ok(WsMessage::Close(_)) => {
                                        warn!("WebSocket connection closed");
                                        break;
                                    }
                                    Ok(_) => {},
                                    Err(e) => {
                                        error!("WebSocket error: {}", e);
                                        break;
                                    }
                                }
                            }
                            _ = interval.tick(), if !self.active_trades.lock().await.is_empty() => {
                                if let Err(e) = self.check_active_trades().await {
                                    error!("Error checking active trades: {}", e);
                                }
                            }
                        }
                    }
                }
                Err(e) => {
                    error!("WebSocket connect error: {}. Retrying in 5 seconds...", e);
                    tokio::time::sleep(Duration::from_secs(5)).await;
                }
            }
        }
    }

    async fn handle_event(&self, text: &str) -> Result<()> {
    debug!("Handling event for message: {}", text);
    let event: serde_json::Value = serde_json::from_str(text)?;

    if let Some(error) = event.get("error") {
        return Err(anyhow!("WebSocket error received: {}", error));
    }

    if let Some(params) = event.get("params") {
        if let Some(token_address) = params.get("tokenAddress").and_then(|v| v.as_str()) {
            info!("New pool detected: Token {}", token_address);
            self.process_token(token_address.to_string()).await?;
        }
    }
    Ok(())
}

async fn process_token(&self, token_address: String) -> Result<()> {
    let metadata = self.get_token_metadata(vec![token_address.clone()]).await?;
    if let Some(meta) = metadata.get(&token_address) {
        if self.is_valid_pool(&token_address, meta).await? {
            info!("Token {} is valid, attempting to buy...", token_address);
            self.attempt_buy(token_address).await?;
        } else {
            warn!(
                "Token {} failed validation checks. Skipping.",
                token_address
            );
        }
    }
    Ok(())
}

async fn get_token_metadata(
    &self,
    token_addresses: Vec<String>,
) -> Result<HashMap<String, serde_json::Value>> {
    let url = format!(
        "{}?api-key={}",
        self.config.rpc_url, self.config.helius_api_key,
    );

    let body = json!({
        "jsonrpc": "2.0",
        "id": "get-token-metadata",
        "method": "getTokenMetadata",
        "params": [
            token_addresses,
            "decimals",
            "name",
        ]
    });

    info!("Fetching metadata from URL: {}", url);

    let response = self
        .http_client
        .post(&url)
        .json(&body)
        .send()
        .await?
        .json::<serde_json::Value>()
        .await?;

    let metadata_map = response["result"]
        .as_array()
        .ok_or_else(|| anyhow!("Invalid metadata response format"))?
        .iter()
        .map(|meta| {
            let address = meta["address"].as_str().unwrap_or("").to_string();
            (address, meta.clone())
        })
        .collect();

    debug!("Metadata response for multiple tokens: {:?}", metadata_map);

    Ok(metadata_map)
}

async fn is_valid_pool(
    &self,
    token_address: &str,
    metadata: &serde_json::Value,
) -> Result<bool> {
    debug!("Validating pool for token: {}", token_address);

    let is_raydium_pool = metadata
        .get("updateAuthority")
        .and_then(serde_json::Value::as_str)
        .map_or(false, |auth| auth == RAYDIUM_AMM_PROGRAM_ID);

    let is_spl_token = metadata
        .get("tokenStandard")
        .and_then(serde_json::Value::as_str)
        == Some("SPL");

    let liquidity = metadata
        .get("liquidity")
        .and_then(serde_json::Value::as_f64)
        .unwrap_or(0.0);

    let is_valid = is_raydium_pool
        && is_spl_token
        && liquidity >= self.config.min_liquidity
        && liquidity <= self.config.max_liquidity;

    info!(
        "Pool validation for {}: Raydium: {}, SPL: {}, Liquidity: {} SOL, Valid: {}",
        token_address, is_raydium_pool, is_spl_token, liquidity, is_valid
    );

    Ok(is_valid)
}

async fn attempt_buy(&self, token_address: String) -> Result<()> {
    info!("Starting buy attempts for token: {}", token_address);
    let active_trades_count = self.active_trades.lock().await.len();
    if active_trades_count >= MAX_ACTIVE_TRADES {
        warn!(
            "Maximum active trades ({}) reached. Skipping buy for {}.",
            MAX_ACTIVE_TRADES, token_address
        );
        return Ok(());
    }

    let snipe_amount = SNIPE_AMOUNT;
    let snipe_amount_lamports = (snipe_amount * 1e9) as u64;

    for attempt in 1..=MAX_RETRY_ATTEMPTS {
        info!("Buy attempt {} for token {}.", attempt, token_address);
        match self
            .execute_buy(&token_address, snipe_amount_lamports)
            .await
        {
            Ok(_) => {
                info!("Successfully bought token {}", token_address);
                return Ok(());
            }
            Err(e) => {
                warn!(
                    "Buy attempt {} failed for token {}: {}",
                    attempt, token_address, e
                );
                if attempt < MAX_RETRY_ATTEMPTS {
                    tokio::time::sleep(BUY_ATTEMPT_DELAY).await;
                }
            }
        }
    }
    error!("All buy attempts failed for token: {}", token_address);
    Ok(())
}

async fn execute_buy(&self, token_address: &str, amount: u64) -> Result<()> {
    debug!(
        "Preparing to execute buy for token: {}, amount: {}",
        token_address, amount
    );
    let _guard = self.swap_limiter.lock().await;

    let jupiter_url = "https://quote-api.jup.ag/v6/quote";
    let params = json!({
        "inputMint": WSOL_MINT,
        "outputMint": token_address,
        "amount": amount.to_string(),
        "slippageBps": (self.config.max_slippage * 100.0) as u64,
    });

    info!("Fetching quote for buy: {:?}", params);
    let quote_response = self
        .http_client
        .get(jupiter_url)
        .query(&params)
        .send()
        .await?
        .json::<serde_json::Value>()
        .await?;

    debug!("Received quote response: {:?}", quote_response);

    let swap_url = "https://quote-api.jup.ag/v6/swap";
    let swap_params = json!({
        "quoteResponse": quote_response,
        "userPublicKey": self.keypair.pubkey().to_string(),
        "wrapUnwrapSOL": true,
        "priorityFee": self.config.priority_fee,
    });

    info!("Initiating swap with parameters: {:?}", swap_params);
    let swap_response = self
        .http_client
        .post(swap_url)
        .json(&swap_params)
        .send()
        .await?
        .json::<serde_json::Value>()
        .await?;

    debug!("Received swap response: {:?}", swap_response);

    let transaction_data = base64::decode(swap_response["swapTransaction"].as_str().unwrap())?;
    let mut transaction: Transaction = bincode::deserialize(&transaction_data)?;
    let recent_blockhash = self.get_recent_blockhash().await?;
    transaction.sign(&[&self.keypair], recent_blockhash);

    debug!("Sending transaction to Solana network");
    let signature = self.rpc_client.send_and_confirm_transaction(&transaction)?;
    info!("Buy transaction confirmed. Signature: {}", signature);

    // Record the trade
    {
        let mut active_trades = self.active_trades.lock().await;
        let out_amount = quote_response["outAmount"].as_f64().unwrap_or(0.0);
        let trade = Trade {
            token_address: token_address.to_string(),
            amount,
            buy_price: out_amount,
            take_profit_price: out_amount * (1.0 + PROFIT_TARGET_PERCENT / 100.0),
            stop_loss_price: out_amount * (1.0 - STOP_LOSS_PERCENT / 100.0),
            timestamp: Instant::now().elapsed().as_secs(),
        };
        active_trades.insert(token_address.to_string(), trade.clone());
        debug!("Added trade to active trades: {:?}", trade);
    }

    Ok(())
}

async fn check_active_trades(&self) -> Result<()> {
    info!("Checking active trades for sell opportunities...");
    let mut trades_to_remove = Vec::new();
    let active_trades = self.active_trades.lock().await;

    for (token_address, trade) in active_trades.iter() {
        let current_price = self.get_token_price(token_address).await?;
        debug!("Current price for {}: {}", token_address, current_price);

        if current_price >= trade.take_profit_price || current_price <= trade.stop_loss_price {
            info!(
                "Executing sell for token {}. Current price: {}.",
                token_address, current_price
            );
            if let Err(e) = self.execute_sell(token_address).await {
                error!("Failed to sell token {}: {}", token_address, e);
            } else {
                trades_to_remove.push(token_address.clone());
            }
        }
    }
    drop(active_trades);

    if !trades_to_remove.is_empty() {
        let mut active_trades = self.active_trades.lock().await;
        for token_address in trades_to_remove {
            active_trades.remove(&token_address);
            info!(
                "Removed trade for token {} after successful sell.",
                token_address
            );
        }
    }
    Ok(())
}

async fn execute_sell(&self, token_address: &str) -> Result<()> {
    debug!("Preparing to execute sell for token: {}", token_address);
    let _guard = self.swap_limiter.lock().await;

    let amount = {
        let active_trades = self.active_trades.lock().await;
        active_trades
            .get(token_address)
            .map(|trade| trade.amount)
            .ok_or_else(|| anyhow!("Trade not found for token {}", token_address))?
    };

    let jupiter_url = "https://quote-api.jup.ag/v6/quote";
    let params = json!({
        "inputMint": token_address,
        "outputMint": WSOL_MINT,
        "amount": amount.to_string(),
        "slippageBps": (self.config.max_slippage * 100.0) as u64,
    });

    info!("Fetching quote for sell: {:?}", params);
    let quote_response = self
        .http_client
        .get(jupiter_url)
        .query(&params)
        .send()
        .await?
        .json::<serde_json::Value>()
        .await?;

    debug!("Received quote response: {:?}", quote_response);

    let swap_url = "https://quote-api.jup.ag/v6/swap";
    let swap_params = json!({
        "quoteResponse": quote_response,
        "userPublicKey": self.keypair.pubkey().to_string(),
        "wrapUnwrapSOL": true,
        "priorityFee": self.config.priority_fee,
    });

    info!("Initiating swap with parameters: {:?}", swap_params);
    let swap_response = self
        .http_client
        .post(swap_url)
        .json(&swap_params)
        .send()
        .await?
        .json::<serde_json::Value>()
        .await?;

    debug!("Received swap response: {:?}", swap_response);

    let transaction_data = base64::decode(swap_response["swapTransaction"].as_str().unwrap())?;
    let mut transaction: Transaction = bincode::deserialize(&transaction_data)?;
    let recent_blockhash = self.get_recent_blockhash().await?;
    transaction.sign(&[&self.keypair], recent_blockhash);

    debug!("Sending transaction to Solana network");
    let signature = self.rpc_client.send_and_confirm_transaction(&transaction)?;
    info!("Sell transaction confirmed. Signature: {}", signature);

    Ok(())
}

async fn get_token_price(&self, token_address: &str) -> Result<f64> {
    debug!("Fetching token price for: {}", token_address);
    if let Some(price) = self.price_cache.lock().await.get(token_address) {
        debug!("Price cache hit for {}: {}", token_address, price);
        return Ok(price);
    }

    let url = format!(
        "https://rpc.helius.xyz?api-key={}",
        self.config.helius_api_key
    );

    let body = json!({
        "jsonrpc": "2.0",
        "id": "get-token-price",
        "method": "getTokenMetadata",
        "params": [
            [token_address]
        ]
    });

    info!("Fetching token price from URL: {}", url);
    let response = self
        .http_client
        .post(&url)
        .json(&body)
        .send()
        .await?
        .json::<serde_json::Value>()
        .await?;
    debug!("Received token metadata response: {:?}", response);

    if let Some(price) = response["result"][0]["price"].as_f64() {
        self.price_cache
            .lock()
            .await
            .set(token_address.to_string(), price);
        Ok(price)
    } else {
        Err(anyhow!("Price not found for token {}", token_address))
    }
}

async fn get_sol_balance(&self) -> Result<u64> {
    debug!(
        "Fetching SOL balance for account: {}",
        self.keypair.pubkey()
    );
    let balance = self.rpc_client.get_balance(&self.keypair.pubkey())?;
    if balance < self.config.min_sol_balance as u64 * 1_000_000_000 {
        warn!(
            "SOL balance is below minimum threshold: {} SOL",
            balance as f64 / 1e9
        );
    }
    Ok(balance)
}

async fn get_recent_blockhash(&self) -> Result<Hash> {
    debug!("Fetching recent blockhash...");
    let mut last_blockhash = self.last_blockhash.lock().await;
    if last_blockhash.1.elapsed() > Duration::from_secs(60) {
        let new_blockhash = self.rpc_client.get_latest_blockhash()?;
        *last_blockhash = (new_blockhash, Instant::now());
        debug!("Updated blockhash: {}", new_blockhash);
        Ok(new_blockhash)
    } else {
        debug!("Using cached blockhash");
        Ok(last_blockhash.0)
    }
}

async fn save_state(&self) -> Result<()> {
    info!("Saving bot state to file...");
    let active_trades = self.active_trades.lock().await;
    let serialized = serde_json::to_string(&*active_trades)?;
    let mut file = OpenOptions::new()
        .write(true)
        .create(true)
        .truncate(true)
        .open(STATE_FILE)?;
    file.write_all(serialized.as_bytes())?;
    info!("State successfully saved to {}.", STATE_FILE);
    Ok(())
}

async fn load_state(&self) -> Result<()> {
    let state_file_path = STATE_FILE;
    info!("Loading bot state from file: {}", state_file_path);
    match std::fs::File::open(state_file_path) {
        Ok(mut file) => {
            let mut contents = String::new();
            file.read_to_string(&mut contents)?;
            let loaded_trades: HashMap<String, Trade> = serde_json::from_str(&contents)?;
            let mut active_trades = self.active_trades.lock().await;
            *active_trades = loaded_trades;
            info!("Successfully loaded state from {}.", state_file_path);
        }
        Err(e) => {
            warn!(
                "State file not found: {}. Starting with an empty state. Error: {}",
                state_file_path, e
            );
        }
    }
    Ok(())
}

#[tokio::main]
async fn main() -> Result<()> {
    env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
    info!("🚀 Solana Sniper Bot v1.0 - Launching! 🚀");

    let bot = Arc::new(Bot::new().await?);

    // Save state every 60 seconds
    let bot_clone = bot.clone();
    tokio::spawn(async move {
        loop {
            tokio::time::sleep(Duration::from_secs(60)).await;
            if let Err(e) = bot_clone.save_state().await {
                error!("Failed to save state: {}", e);
            }
        }
    });

    // Setup signal handling for graceful shutdown
    let (tx, rx) = tokio::sync::oneshot::channel();
    let tx = Arc::new(StdMutex::new(Some(tx)));
    let tx_clone = tx.clone();

    ctrlc::set_handler(move || {
        if let Some(tx) = tx_clone.lock().unwrap().take() {
            let _ = tx.send(());
        }
    })
    .expect("Error setting Ctrl+C handler");

    tokio::select! {
        result = bot.run() => {
            if let Err(e) = result {
                error!("Bot encountered an error: {}", e);
            }
        }
        _ = rx => {
            info!("Received shutdown signal. Saving state and exiting...");
            if let Err(e) = bot.save_state().await {
                error!("Error saving state on shutdown: {}", e);
            }
        }
    }

    info!("🛑 Bot shutting down. Goodbye!");
    Ok(())
}