use crate::tests::*;
use android_logger::{Config, FilterBuilder};
use backtrace::Backtrace;
use jni::errors::Result as JniResult;
use jni::objects::{Global, JClass, JObject, JString};
use jni::vm::JavaVM;
use jni::{jni_sig, jni_str, Env, EnvUnowned};
use log::*;
use std::panic;
use std::path::{Path, PathBuf};

#[no_mangle]
#[allow(non_snake_case)]
pub extern "system" fn Java_com_keyring_1manager_keyring_1android_1test_MainActivity_run_1tests(
    mut env: EnvUnowned,
    _class: JClass,
    ctx: JObject,
) {
    android_logger::init_once(
        Config::default()
            .with_max_level(LevelFilter::Trace)
            .with_tag("keyring_manager_android_tests")
            .with_filter(FilterBuilder::new().parse("keyring=trace").build()),
    );

    panic::set_hook(Box::new(|panic_info| {
        if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
            println!("panic occurred: {:?}", s);
        } else {
            println!("panic occurred");
        }
        let bt = Backtrace::new();
        println!("Backtrace:\n{:?}", bt);
    }));

    env.with_env(|env| -> JniResult<()> {
        let ctx = env.new_global_ref(ctx)?;
        let vm = env.get_java_vm()?;
        run_tests(vm, ctx);
        Ok(())
    })
    .resolve::<jni::errors::ThrowRuntimeExAndDefault>();
}

pub fn run_tests(vm: JavaVM, ctx: Global<JObject<'static>>) {
    info!("Starting unit tests");

    // Test in new non-dalvik native thread to see what happens
    std::thread::spawn(move || {
        vm.attach_current_thread(|jnienv| {
            let insecure_keyring_path_string = jnienv
                .with_local_frame(64, |jnienv: &mut Env| -> JniResult<String> {
                    // context.getFilesDir().getAbsolutePath()
                    let file = jnienv
                        .call_method(
                            ctx.as_obj(),
                            jni_str!("getFilesDir"),
                            jni_sig!("()Ljava/io/File;"),
                            &[],
                        )
                        .unwrap()
                        .l()
                        .unwrap();
                    let path = jnienv
                        .call_method(
                            file,
                            jni_str!("getAbsolutePath"),
                            jni_sig!("()Ljava/lang/String;"),
                            &[],
                        )
                        .unwrap()
                        .l()
                        .unwrap();

                    let jstr = JString::cast_local(jnienv, path)?;
                    Ok(jstr.to_string())
                })
                .unwrap();
            let insecure_keyring_path: PathBuf =
                Path::new(&insecure_keyring_path_string).join("insecure_keyring");

            let android_context = (
                jnienv.get_java_vm().unwrap(),
                jnienv.new_global_ref(ctx.as_obj()).unwrap(),
            );

            let platform_keyring_manager =
                KeyringManager::new_secure(TEST_APPLICATION, android_context).unwrap();
            let insecure_keyring_manager =
                KeyringManager::new_insecure(TEST_APPLICATION, &insecure_keyring_path).unwrap();

            info!("TEST: secure_test_escaped_password_input");
            exec_test_escaped_password_input(&platform_keyring_manager);
            info!("TEST: secure_test_add_ascii_password");
            exec_test_add_ascii_password(&platform_keyring_manager);
            info!("TEST: secure_test_round_trip_ascii_password");
            exec_test_round_trip_ascii_password(&platform_keyring_manager);
            info!("TEST: secure_test_add_non_ascii_password");
            exec_test_add_non_ascii_password(&platform_keyring_manager);
            info!("TEST: secure_test_round_trip_non_ascii_password");
            exec_test_round_trip_non_ascii_password(&platform_keyring_manager);
            info!("TEST: secure_test_multiple");
            exec_test_multiple(&platform_keyring_manager);

            info!("TEST: insecure_test_escaped_password_input");
            exec_test_escaped_password_input(&insecure_keyring_manager);
            info!("TEST: insecure_test_add_ascii_password");
            exec_test_add_ascii_password(&insecure_keyring_manager);
            info!("TEST: insecure_test_round_trip_ascii_password");
            exec_test_round_trip_ascii_password(&insecure_keyring_manager);
            info!("TEST: insecure_test_add_non_ascii_password");
            exec_test_add_non_ascii_password(&insecure_keyring_manager);
            info!("TEST: insecure_test_round_trip_non_ascii_password");
            exec_test_round_trip_non_ascii_password(&insecure_keyring_manager);
            info!("TEST: insecure_test_multiple");
            exec_test_multiple(&insecure_keyring_manager);

            JniResult::Ok(())
        })
        .unwrap();
    })
    .join()
    .unwrap();

    info!("Finished unit tests");
}
