use anyhow::{Result, anyhow};
use json::JsonValue;
use std::fmt::Display;
use std::sync::mpsc;
use std::sync::mpsc::Sender;
use std::thread;
use std::thread::JoinHandle;
use std::time::Instant;

const USER_NAME: &str = "Jonah";

pub fn compute_async<T: Display + Send + 'static, R: Send + 'static>(mut function: impl FnMut(Sender<(T, T)>) -> R + Send + 'static ) -> R {
    let (tx, jh1) = spawn_submission_thread();
    let jh2 = thread::spawn(move || {
        function(tx)
    });

    jh1.join().unwrap();
    jh2.join().unwrap()
}

pub fn spawn_submission_thread<T: Display + Send + 'static>() -> (Sender<(T, T)>, JoinHandle<()>) {
    let (tx, rx) = mpsc::channel::<(T, T)>();
    let handle = thread::spawn(move || {
        let mut t = Instant::now();
        for (n, x) in rx {
            let t2 = Instant::now();
            println!("\nn = {n}\nx = {x}\nduration = {}", t2.duration_since(t).as_millis());
            t = t2;

            match submit(n, x) {
                Ok(_) => {}
                Err(err) => eprintln!("{:?}", err),
            };
        }
    });
    (tx, handle)
}

pub fn submit<T: Display + Send>(n: T, x: T) -> Result<Option<String>> {
    let url = format!("https://button.qedaka.de/server.php?name={USER_NAME}&n={n}&x={x}");
    let body = reqwest::blocking::get(url)?.text()?;
    match json::parse(&body)? {
        JsonValue::Object(obj) => {
            if let Some(error) = obj.get("error") {
                Err(anyhow!("{:?}", error))
            } else if let Some(flag) = obj.get("flag") {
                Ok(Some(flag.to_string()))
            } else {
                Ok(None)
            }
        }
        json => Err(anyhow!("Unexpected JSON response {:?}", json)),
    }
}
