use std::{
    error::Error,
    io::{stdout, Write},
    time::Instant,
};

use letterbox_notmuch::Notmuch;
use rayon::iter::{ParallelBridge, ParallelIterator};

#[test]
#[ignore] // it is too expensive, run with `cargo test -- --ignored`
fn parse_one() -> Result<(), Box<dyn Error>> {
    // take_hook() returns the default hook in case when a custom one is not set
    let orig_hook = std::panic::take_hook();
    std::panic::set_hook(Box::new(move |panic_info| {
        // invoke the default handler and exit the process
        orig_hook(panic_info);
        std::process::exit(1);
    }));

    let nm = Notmuch::default();
    let total = nm.count("*")? as f32;
    let start = Instant::now();
    nm.message_ids("*")?
        .iter()
        .enumerate()
        .par_bridge()
        .for_each(|(i, msg)| {
            let _ts = nm
                .show(&msg)
                .expect(&format!("failed to show msg: {}", msg));
            //println!("{:?}", ts);
            if i > 0 && i % 1000 == 0 {
                let diff = start.elapsed();
                let progress = i as f32;
                let percent = (progress * 100.) / total;
                let duration_per_message = diff.div_f32(progress);
                let total_runtime = duration_per_message.mul_f32(total as f32);
                let eta = duration_per_message.mul_f32(total - progress);
                print!(
                    "\nElapsed {}s ETA {}s Total {}s Time/msg {}us Percent {}% ",
                    diff.as_secs_f32(),
                    eta.as_secs_f32(),
                    total_runtime.as_secs_f32(),
                    duration_per_message.as_micros(),
                    percent
                );
                stdout().flush().expect("failed to flush stdout");
            }
            if i % 10 == 0 {
                print!(".");
                stdout().flush().expect("failed to flush stdout");
            }
        });
    println!("\n");
    Ok(())
}

#[test]
#[ignore] // it is too expensive
fn parse_bulk() -> Result<(), Box<dyn Error>> {
    // take_hook() returns the default hook in case when a custom one is not set
    let orig_hook = std::panic::take_hook();
    std::panic::set_hook(Box::new(move |panic_info| {
        // invoke the default handler and exit the process
        orig_hook(panic_info);
        std::process::exit(1);
    }));

    let nm = Notmuch::default();
    let total = nm.count("*")? as f32;
    let start = Instant::now();
    let chunk_size = 1000;
    nm.message_ids("*")?
        .chunks(chunk_size)
        .into_iter()
        .enumerate()
        //.par_bridge()
        .for_each(|(i, msgs)| {
            let query = msgs.join(" OR ");
            let _ts = nm
                .show(&query)
                .expect(&format!("failed to show msgs: {}", query));
            //println!("{:?}", ts);
            if i > 0 && i % 10 == 0 {
                let progress = (i * chunk_size) as f32;
                let diff = start.elapsed();
                let percent = (progress * 100.) / total;
                let duration_per_message = diff.div_f32(progress);
                let total_runtime = duration_per_message.mul_f32(total as f32);
                let eta = duration_per_message.mul_f32(total - progress);
                print!(
                    "\nElapsed {}s ETA {}s Total {}s Time/msg {}us Percent {}% ",
                    diff.as_secs_f32(),
                    eta.as_secs_f32(),
                    total_runtime.as_secs_f32(),
                    duration_per_message.as_micros(),
                    percent
                );
                stdout().flush().expect("failed to flush stdout");
            }
            print!(".");
            stdout().flush().expect("failed to flush stdout");
        });
    println!("\n");
    Ok(())
}
