use crate::tests::*;
use backtrace::Backtrace;
use directories::*;
use log::*;
use simplelog::*;
use std::panic;
use std::path::PathBuf;

fn get_cache_dir() -> PathBuf {
    if let Some(my_proj_dirs) = ProjectDirs::from("org", "Keyring", "KeyRingTests") {
        PathBuf::from(my_proj_dirs.cache_dir())
    } else {
        PathBuf::from("./")
    }
}

#[no_mangle]
pub extern "C" fn run_tests() {
    if let Err(e) = run_tests_internal() {
        panic!("failed to run unit tests: {}", e);
    }
}

fn run_tests_internal() -> std::result::Result<(), String> {
    let mut logs: Vec<Box<dyn SharedLogger>> = Vec::new();

    let mut cb = ConfigBuilder::new();

    logs.push(TermLogger::new(
        LevelFilter::Trace,
        cb.build(),
        TerminalMode::Mixed,
        ColorChoice::Auto,
    ));
    CombinedLogger::init(logs).map_err(|e| format!("logger init error: {}", e))?;

    panic::set_hook(Box::new(|panic_info| {
        let bt = Backtrace::new();
        if let Some(location) = panic_info.location() {
            error!(
                "panic occurred in file '{}' at line {}",
                location.file(),
                location.line(),
            );
        } else {
            error!("panic occurred but can't get location information...");
        }
        if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
            error!("panic payload: {:?}", s);
        } else if let Some(s) = panic_info.payload().downcast_ref::<String>() {
            error!("panic payload: {:?}", s);
        } else if let Some(a) = panic_info.payload().downcast_ref::<std::fmt::Arguments>() {
            error!("panic payload: {:?}", a);
        } else {
            error!("no panic payload");
        }
        error!("Backtrace:\n{:?}", bt);
    }));

    run_unit_tests();

    Ok(())
}

fn run_unit_tests() {
    info!("Starting unit tests");

    let platform_keyring_manager = KeyringManager::new_secure(TEST_APPLICATION).unwrap();
    let insecure_keyring_manager = KeyringManager::new_insecure(
        TEST_APPLICATION,
        &get_cache_dir().join("test_insecure_keyring"),
    )
    .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);

    info!("Finished unit tests");
}
