use anyhow::Result;
use clap::Args;
use std::path::PathBuf;
use std::time::Duration;
use crate::geyser::{self, GeyserConfig};

#[derive(Args)]
pub struct BenchmarkArgs {
    #[arg(long, required = true, help = "First Geyser gRPC endpoint URL")]
    pub endpoint1: String,
    #[arg(long, required = true, help = "Second Geyser gRPC endpoint URL")]
    pub endpoint2: String,
    #[arg(long, help = "Auth token for endpoint 1")]
    pub token1: Option<String>,
    #[arg(long, help = "Auth token for endpoint 2")]
    pub token2: Option<String>,
    #[arg(long, default_value = "100", help = "Number of slots to collect")]
    pub slots: usize,
    #[arg(long, help = "Subscribe to account updates")]
    pub with_accounts: bool,
    #[arg(long, help = "Filter accounts by owner pubkey")]
    pub account_owner: Option<String>,
    #[arg(long, default_value = "30", help = "Connection timeout in seconds")]
    pub timeout: u64,
    #[arg(long, default_value = "10", help = "Number of ping samples for latency measurement")]
    pub ping_samples: usize,
    #[arg(long, help = "Path to TOML config file for advanced settings")]
    pub config: Option<PathBuf>,
    #[arg(short, long, help = "Save results to JSON file")]
    pub output: Option<PathBuf>,
    #[arg(short, long, help = "Suppress verbose output")]
    pub quiet: bool,
}

pub async fn run(args: BenchmarkArgs) -> Result<()> {
    let _ = rustls::crypto::ring::default_provider().install_default();
    let verbose = !args.quiet;

    let mut cfg = match &args.config {
        Some(path) => GeyserConfig::from_file(path.to_str().unwrap_or(""))?,
        None => GeyserConfig::default(),
    };

    cfg.grpc.connection_timeout = Duration::from_secs(args.timeout);
    cfg.grpc.request_timeout = Duration::from_secs(args.timeout);
    cfg.benchmark.ping_samples = args.ping_samples;

    if verbose {
        println!("\n=== Aqua Geyser Benchmark ===\n");
        println!("endpoint 1: {}", args.endpoint1);
        println!("endpoint 2: {}", args.endpoint2);
        println!("target slots: {}", args.slots);
        if args.with_accounts {
            println!("with accounts: true");
            if let Some(ref owner) = args.account_owner {
                println!("account owner filter: {}", owner);
            }
        }
        println!();
    }

    let collector = geyser::DualEndpointCollector::new(
        cfg.clone(),
        args.endpoint1,
        args.endpoint2,
        args.token1,
        args.token2,
        args.slots,
        args.with_accounts,
        args.account_owner.clone(),
    );

    let (data1, data2, meta1, meta2, duration) = collector.collect(verbose).await?;

    let result = geyser::process_results(
        &cfg,
        data1,
        data2,
        meta1,
        meta2,
        duration,
        args.with_accounts,
        args.account_owner,
    );

    if verbose {
        geyser::print_summary(&result);
    }

    if let Some(path) = args.output {
        std::fs::write(&path, serde_json::to_string_pretty(&result)?)?;
        if verbose {
            println!("\nresults saved to: {}", path.display());
        }
    }

    Ok(())
}
