use ::wasmer::sys::Features;
use std::path::Path;
use wasmer_wast::Wast;

// The generated tests (from build.rs) look like:
// #[cfg(test)]
// mod [compiler] {
//     mod [spec] {
//         mod [vfs] {
//             #[test]
//             fn [test_name]() -> anyhow::Result<()> {
//                 crate::run_wasi("tests/spectests/[test_name].wast", "[compiler]", WasiFileSystemKind::[vfs])
//             }
//         }
//     }
// }
include!(concat!(env!("OUT_DIR"), "/generated_spectests.rs"));

pub fn run_wast(mut config: crate::Config, wast_path: &str) -> anyhow::Result<()> {
    println!("Running wast `{wast_path}`");
    let try_nan_canonicalization = wast_path.contains("nan-canonicalization");
    let mut features = Features::default();
    let is_bulkmemory = wast_path.contains("bulk-memory");
    let is_simd = wast_path.contains("simd");
    let is_relaxed_simd = wast_path.contains("relaxed_");
    let is_threads = wast_path.contains("threads");
    let is_exception_handling = wast_path.contains("exceptions")
        || wast_path.contains("exception-handling")
        || wast_path.ends_with("exports.wast")
        || wast_path.ends_with("imports.wast")
        || wast_path.ends_with("try_table.wast")
        || wast_path.ends_with("tag.wast")
        || wast_path.ends_with("throw.wast")
        || wast_path.ends_with("throw_ref.wast")
        || wast_path.ends_with("imports.wast");
    let is_wide_arithmetic = wast_path.contains("wide-arithmetic");

    let is_tail_call = wast_path.contains("return_call") || wast_path.ends_with("try_table.wast");

    if is_bulkmemory {
        features.bulk_memory(true);
    }
    if is_simd {
        features.simd(true);
    }
    if is_relaxed_simd {
        features.relaxed_simd(true);
    }
    if is_threads {
        features.threads(true);
    }
    if is_exception_handling {
        features.exceptions(true);
    }
    if is_wide_arithmetic {
        features.wide_arithmetic(true);
    }
    if is_tail_call {
        features.tail_call(true);
    }
    config.set_features(features);
    config.set_nan_canonicalization(try_nan_canonicalization);

    let store = config.store();
    let mut wast = Wast::new_with_spectest(store);
    // `bulk-memory-operations/bulk.wast` checks for a message that
    // specifies which element is uninitialized, but our traps don't
    // shepherd that information out.
    wast.allow_trap_message("uninitialized element 2", "uninitialized element");
    // `liking.wast` has different wording but the same meaning
    wast.allow_trap_message("out of bounds memory access", "memory out of bounds");
    wast.allow_trap_message(
        "out of bounds memory access",
        "Validation error: multiple memories",
    );
    if cfg!(feature = "coverage") {
        wast.disable_assert_and_exhaustion();
    }

    wast.allow_instantiation_failures(&[
        "Validation error: memory size must be at most",
        "Validation error: multiple memories",
        "Validation error: function references",
        "Validation error: tables with expression initializers require the function-references proposal",
        "Validation error: heap types not supported without the gc feature",
        "Validation error: rec group usage requires `gc` proposal to be enabled",
        "Validation error: gc proposal must be enabled to use subtypes",
        "Validation error: array indexed types not supported without the gc feature",
        "Validation error: struct indexed types not supported without the gc feature",
        // TODO: remove once added for Cranelift
        "Unsupported feature: proposed tail-call operator ReturnCallIndirect",
        "Validation error: multiple tables",
        "Validation error: unknown memory 0",
        "Validation error: invalid var_u32",
        "Validation error: SIMD index out of bounds",
        // Still needed as it requires GC feature!
        "Validation error: constant expression required",
        "Validation error: memory64 must be enabled for 64-bit memories",
        "Validation error: memory64 must be enabled for 64-bit tables",
        "Unsupported feature: unsupported init expr in element section",
        "Insufficient resources: Table minimum",
        "Insufficient resources: Table maximum",
    ]);
    wast.fail_fast = false;
    let path = Path::new(wast_path);
    wast.run_file(path)
}
