use std::env;
use std::path::Path;
use std::process::Command;

fn main() {
    // docs.rs builds in a network-isolated sandbox; skip native linking so rustdoc
    // can generate documentation from the pre-built src/bindings.rs without curl/tar.
    if env::var("DOCS_RS").is_ok() {
        return;
    }

    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let out_dir = env::var("OUT_DIR").unwrap();

    // Prefer a pre-populated local copy (e.g. from a prior `setup.sh` run during development).
    // Fall back to OUT_DIR so that published crate builds never write outside Cargo's sandbox.
    let local_current = Path::new(&manifest_dir).join("third_party/nwep/current");
    let out_current = Path::new(&out_dir).join("nwep/current");

    let current = if local_current.exists() {
        local_current
    } else {
        if !out_current.exists() {
            let install_dir = Path::new(&out_dir).join("nwep");
            let target_os   = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
            let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
            let status = Command::new("bash")
                .arg(Path::new(&manifest_dir).join("setup.sh"))
                .env("NWEP_INSTALL_DIR", &install_dir)
                .env("NWEP_TARGET_OS",   &target_os)
                .env("NWEP_TARGET_ARCH", &target_arch)
                .current_dir(&manifest_dir)
                .status()
                .unwrap_or_else(|e| {
                    eprintln!("cargo:warning=Failed to run setup.sh: {e}");
                    eprintln!("cargo:warning=setup.sh requires curl and tar (or unzip) to be on PATH.");
                    eprintln!("cargo:warning=On Windows use Git Bash or WSL.");
                    eprintln!("cargo:warning=See https://github.com/usenwep/nwep-rust for details.");
                    std::process::exit(1);
                });
            if !status.success() {
                eprintln!("cargo:warning=setup.sh exited with status {status}.");
                eprintln!("cargo:warning=Ensure curl and tar/unzip are installed and you have internet access.");
                std::process::exit(1);
            }
        }
        out_current
    };

    let lib_dir = current.join("lib");

    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();

    println!("cargo:rustc-link-search=native={}", lib_dir.display());
    println!("cargo:rustc-link-lib=static=nwep_packed");
    if target_os == "android" {
        // Android's Bionic libc bundles pthread and dl; link the NDK log library instead.
        println!("cargo:rustc-link-lib=log");
    } else {
        println!("cargo:rustc-link-lib=pthread");
        println!("cargo:rustc-link-lib=dylib=dl");
    }

    println!("cargo:rerun-if-changed=setup.sh");
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-changed=src/bindings.rs");

    // Regenerate FFI bindings only when the `regenerate-bindings` feature is enabled.
    // This requires clang/libclang. The pre-generated src/bindings.rs is used otherwise.
    #[cfg(feature = "regenerate-bindings")]
    {
        use std::path::PathBuf;
        let include_dir = current.join("include");

        println!("cargo:rerun-if-changed={}", include_dir.join("nwep/nwep.h").display());

        let bindings = bindgen::Builder::default()
            .header(include_dir.join("nwep/nwep.h").to_str().unwrap())
            .clang_arg(format!("-I{}", include_dir.display()))
            .clang_arg("-DNWEP_STATICLIB")
            .allowlist_function("nwep_.*")
            .allowlist_type("nwep_.*")
            .allowlist_var("NWEP_.*")
            .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
            .generate()
            .expect("Unable to generate bindings");

        // Write to OUT_DIR for use by src/ffi.rs when this feature is active.
        let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
        bindings
            .write_to_file(out_path.join("bindings.rs"))
            .expect("Couldn't write bindings");

        // Also update the checked-in pre-built bindings.
        bindings
            .write_to_file(Path::new(&manifest_dir).join("src/bindings.rs"))
            .expect("Couldn't write src/bindings.rs");
    }
}
