use crate::request::{self, Request, RequestPlanner};

use std::{
    io::{self, Write},
    time::{Duration, Instant},
};

pub async fn benchmark(
    requests: &[Request],
    planner: &RequestPlanner,
    target_index: usize,
    count: usize,
) -> Result<(), Box<dyn std::error::Error>> {
    println!(
        "Benchmarking request: {}",
        requests
            .get(target_index)
            .map(|req| req.description.as_str())
            .unwrap_or("<unknown>")
    );

    let plan = planner.order_for(target_index)?;

    let mut bench_requests = requests.to_vec();
    for idx in &plan {
        if let Some(req) = bench_requests.get_mut(*idx) {
            req.callback_src.clear();
        }
    }

    let mut request_time: Vec<Duration> = vec![];

    for i in 0..count {
        let start = Instant::now();
        request::execute_request_plan(&bench_requests, &plan, request::ExecutionOptions::default())
            .await
            .map_err(|err| -> Box<dyn std::error::Error> { Box::new(err) })?;
        let duration = start.elapsed();
        request_time.push(duration);

        // prints a progress bar
        let progress = if count > 1 {
            (i as f64 / (count - 1) as f64) * 100.0
        } else {
            100.0
        };
        print!("\r[");
        for j in 0..50 {
            if j as f64 / 50.0 <= progress / 100.0 {
                print!("#");
            } else {
                print!(" ");
            }
        }
        print!("] {:.2}%", progress);

        io::stdout().flush().unwrap();
    }

    let mean_duration = request_time.iter().sum::<Duration>() / count as u32;
    let variance = request_time
        .iter()
        .map(|x| (x.as_secs_f64() - mean_duration.as_secs_f64()).powi(2))
        .sum::<f64>()
        / count as f64;

    println!("\n");
    println!("Mean Duration: {:?}", mean_duration);
    println!("Variance (%): {:?}", variance * 100.0);

    Ok(())
}
