use pyo3::prelude::*;
use tokio::sync::broadcast;
use serde::{Serialize, Deserialize};
use serde_json::{json, Value};
use std::{sync::Arc, time::Duration};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Mutex as StdMutex;
use std::future::Future;
use std::pin::Pin;
use reqwest::Client;
use pyo3_asyncio::tokio::future_into_py;

// Constants for API endpoints
const HELIUS_API_KEY: &str = "1ea5843b-9daa-4926-a97a-be021922da2f";
const MARGA_RPC: &str = "https://marga-e56m84-fast-mainnet.helius-rpc.com";
const JUPITER_V6: &str = "https://quote-api.jup.ag/v6";
const RAYDIUM_PROGRAM_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8";

// Trading settings
const MAX_CONCURRENT_TRADES: usize = 10;
const PRIORITY_FEE_LAMPORTS: u64 = 15000;
const MIN_LIQUIDITY: f64 = 50_000.0;
const MAX_SLIPPAGE: f64 = 0.5;
const INITIAL_POSITION_SIZE: f64 = 0.05;
const MAX_POSITION_SIZE: f64 = 2.0;
const SCALING_THRESHOLD: u64 = 4;

#[pyclass]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Position {
    #[pyo3(get)]
    token: String,
    #[pyo3(get)]
    entry_price: f64,
    #[pyo3(get)]
    size: f64,
    #[pyo3(get)]
    timestamp: i64,
}

#[pyclass]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarketData {
    #[pyo3(get)]
    token: String,
    #[pyo3(get)]
    price: f64,
    #[pyo3(get)]
    volume_5m: f64,
    #[pyo3(get)]
    liquidity: f64,
    #[pyo3(get)]
    timestamp: i64,
}

struct TradeStats {
    successful_trades: AtomicU64,
    total_trades: AtomicU64,
    current_position_size: StdMutex<f64>,
    total_profit: StdMutex<f64>,
}

impl TradeStats {
    fn new() -> Self {
        Self {
            successful_trades: AtomicU64::new(0),
            total_trades: AtomicU64::new(0),
            current_position_size: StdMutex::new(INITIAL_POSITION_SIZE),
            total_profit: StdMutex::new(0.0),
        }
    }
}

#[pyclass]
struct TurboTrader {
    trade_stats: Arc<TradeStats>,
    positions: Arc<StdMutex<Vec<Position>>>,
    trade_tx: broadcast::Sender<String>,
    active: Arc<StdMutex<bool>>,
    torch: Py<PyAny>,
    device: Py<PyAny>,
    client: Arc<Client>,
}

fn to_py_err<E: std::fmt::Display>(err: E) -> PyErr {
    PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(err.to_string())
}

#[pymethods]
impl TurboTrader {
    #[new]
    fn new() -> PyResult<Self> {
        Python::with_gil(|py| {
            let torch = PyModule::import(py, "torch")?;
            
            if !torch.getattr("cuda")?.getattr("is_available")?.call0()?.extract::<bool>()? {
                return Err(to_py_err("CUDA required!"));
            }
            
            let device = torch.getattr("device")?.call1(("cuda",))?;
            let (tx, _) = broadcast::channel(1000);

            let client = Client::builder()
                .timeout(Duration::from_millis(100))
                .pool_max_idle_per_host(100)
                .build()
                .map_err(to_py_err)?;

            Ok(Self {
                trade_stats: Arc::new(TradeStats::new()),
                positions: Arc::new(StdMutex::new(Vec::new())),
                trade_tx: tx,
                active: Arc::new(StdMutex::new(true)),
                torch: torch.into(),
                device: device.into(),
                client: Arc::new(client),
            })
        })
    }

    fn start<'p>(&'p self, py: Python<'p>) -> PyResult<&'p PyAny> {
        let trade_stats = self.trade_stats.clone();
        let client = self.client.clone();
        let trade_tx = self.trade_tx.clone();
        let torch = self.torch.clone_ref(py);
        let device = self.device.clone_ref(py);
        let active = self.active.clone();
        let this = Arc::new(self.clone());

        future_into_py(py, async move {
            println!("🚀 TurboTrader Active - CUDA Enabled");
            
            while *active.lock().unwrap() {
                match this.get_market_data_inner(&client).await {
                    Ok(market_data) => {
                        if market_data.liquidity >= MIN_LIQUIDITY {
                            let should_trade = Python::with_gil(|py| {
                                this.analyze_market(py, &market_data, &torch, &device)
                            })?;
                            
                            if should_trade {
                                let pos_size = *trade_stats.current_position_size.lock().unwrap();
                                if let Ok(quote) = this.get_jupiter_quote_inner(&client, &market_data.token, pos_size).await {
                                    if let Ok(signature) = this.execute_trade_inner(&client, &quote).await {
                                        trade_tx.send(signature.clone()).unwrap_or_default();
                                        println!("✅ Trade executed: {}", signature);
                                        trade_stats.successful_trades.fetch_add(1, Ordering::SeqCst);
                                        this.update_position_size(&trade_stats);
                                    }
                                }
                                trade_stats.total_trades.fetch_add(1, Ordering::SeqCst);
                            }
                        }
                    }
                    Err(e) => println!("Market data error: {}", e),
                }
                tokio::time::sleep(Duration::from_millis(100)).await;
            }
            Ok(())
        })
    }

    fn analyze_market(&self, py: Python, market_data: &MarketData, 
        torch: Py<PyAny>, device: Py<PyAny>) -> PyResult<bool> {
        let features = [
            market_data.price,
            market_data.volume_5m,
            market_data.liquidity
        ];

        let tensor = torch.call_method1(py, "tensor", (features,))?;
        tensor.call_method1(py, "to", (device,))?;

        let momentum = market_data.volume_5m / market_data.liquidity;
        let volatility = market_data.price * 0.01;

        Ok(momentum > 0.02 && volatility < 0.03 && market_data.liquidity >= MIN_LIQUIDITY)
    }
    fn get_stats(&self) -> PyResult<String> {
        Ok(format!(
            "📊 Stats:\nTotal Trades: {}\nSuccessful: {}\nPosition Size: {:.3}\nTotal Profit: {:.3}",
            self.trade_stats.total_trades.load(Ordering::Relaxed),
            self.trade_stats.successful_trades.load(Ordering::Relaxed),
            *self.trade_stats.current_position_size.lock().unwrap(),
            *self.trade_stats.total_profit.lock().unwrap()
        ))
    }

    fn stop(&self) -> PyResult<()> {
        *self.active.lock().unwrap() = false;
        println!("✨ TurboTrader Stopped");
        Ok(())
    }
}

impl Clone for TurboTrader {
    fn clone(&self) -> Self {
        Self {
            trade_stats: self.trade_stats.clone(),
            positions: self.positions.clone(),
            trade_tx: self.trade_tx.clone(),
            active: self.active.clone(),
            torch: self.torch.clone(),
            device: self.device.clone(),
            client: self.client.clone(),
        }
    }
}

impl TurboTrader {
    fn get_market_data_inner<'a>(&'a self, client: &'a Client) 
        -> Pin<Box<dyn Future<Output = PyResult<MarketData>> + Send + 'a>> {
        Box::pin(async move {
            let response = client.get(MARGA_RPC)
                .header("Authorization", HELIUS_API_KEY)
                .send()
                .await
                .map_err(to_py_err)?
                .json::<Value>()
                .await
                .map_err(to_py_err)?;

            Ok(MarketData {
                token: "SOL".to_string(),
                price: response["price"].as_f64().unwrap_or_default(),
                volume_5m: response["volume5m"].as_f64().unwrap_or_default(),
                liquidity: response["liquidity"].as_f64().unwrap_or_default(),
                timestamp: chrono::Utc::now().timestamp(),
            })
        })
    }

    fn get_jupiter_quote_inner<'a>(&'a self, client: &'a Client, token: &str, amount: f64) 
        -> Pin<Box<dyn Future<Output = PyResult<String>> + Send + 'a>> {
        let token = token.to_string();
        Box::pin(async move {
            let quote_request = json!({
                "inputMint": token,
                "amount": amount,
                "slippageBps": (MAX_SLIPPAGE * 100.0) as u64,
                "priorityFee": PRIORITY_FEE_LAMPORTS,
            });

            let response = client.post(JUPITER_V6)
                .json(&quote_request)
                .send()
                .await
                .map_err(to_py_err)?
                .json::<Value>()
                .await
                .map_err(to_py_err)?;

            Ok(response["swapTransaction"].as_str()
                .unwrap_or_default()
                .to_string())
        })
    }

    fn execute_trade_inner<'a>(&'a self, client: &'a Client, swap_data: &str) 
        -> Pin<Box<dyn Future<Output = PyResult<String>> + Send + 'a>> {
        let swap_data = swap_data.to_string();
        Box::pin(async move {
            let tx_data = json!({
                "jsonrpc": "2.0",
                "id": 1,
                "method": "sendTransaction",
                "params": [
                    swap_data,
                    {
                        "encoding": "base64",
                        "skipPreflight": true,
                        "maxRetries": 0,
                        "priorityFee": PRIORITY_FEE_LAMPORTS,
                    }
                ]
            });

            let response = client.post(MARGA_RPC)
                .json(&tx_data)
                .header("Authorization", HELIUS_API_KEY)
                .send()
                .await
                .map_err(to_py_err)?
                .json::<Value>()
                .await
                .map_err(to_py_err)?;

            Ok(response["result"].as_str()
                .unwrap_or_default()
                .to_string())
        })
    }

    fn update_position_size(&self, trade_stats: &TradeStats) {
        let successful = trade_stats.successful_trades.load(Ordering::SeqCst);
        let total = trade_stats.total_trades.load(Ordering::SeqCst);
        
        if total >= SCALING_THRESHOLD {
            let win_rate = successful as f64 / total as f64;
            let mut position_size = trade_stats.current_position_size.lock().unwrap();
            
            *position_size = if win_rate > 0.6 {
                (*position_size * 1.2).min(MAX_POSITION_SIZE)
            } else {
                INITIAL_POSITION_SIZE
            };
        }
    }
}

#[pymodule]
fn solana_rust_bot(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_class::<TurboTrader>()?;
    Ok(())
}