use boa_engine::{Context, JsResult, JsString, JsValue};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
    fn proxy_fetch(options: &str) -> String;
}
// #[wasm_bindgen]
// extern {
//     fn alert(msg: &str);
// }

#[derive(Clone, Debug, Serialize, Deserialize)]
struct FetchResult {
    error: Option<String>,
    result: Option<String>,
}

// n(&JsValue, &[JsValue], &mut Context) -> JsResult<JsValue>
fn fetch_fn(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult<JsValue> {
    let mut options = String::new();
    options.push('[');
    args.iter().enumerate().for_each(|(i, arg)| {
        if i > 0 { options.push(','); }
        options.push_str(arg.to_json(ctx).expect("to json error").as_str().expect("as str error"))
    });
    options.push(']');
    // let r = serde_json::to_string(args).unwrap();
    let fetch_result_string = proxy_fetch(&options);
    let fetch_result: FetchResult = serde_json::from_str(&fetch_result_string).expect("from str error");
    if let Some(result) = fetch_result.result {
        let r: Value = serde_json::from_str(&result).expect("result from str error");
        let v = JsValue::from_json(&r, ctx).unwrap();
        return JsResult::Ok(v);
    }
    JsResult::Err(JsValue::String(JsString::from(format!("Invoke fetch error: {}", fetch_result.error.unwrap_or("".to_string())))))
}

#[wasm_bindgen]
pub fn run_js(script: String) -> String {
    let mut ctx = Context::default();
    ctx.register_global_function("fetch", 0, fetch_fn);
    let o = match ctx.eval(script) {
        JsResult::Ok(o) => o,
        JsResult::Err(o) => o,
    };
    format!("{}", o.to_json(&mut ctx).unwrap())
}

