fn main() {
    // Tell rustc that `#[cfg(bazel)]` is a valid (but never set) cfg key when
    // building via Cargo, so it doesn't emit unexpected_cfgs warnings.
    println!("cargo:rustc-check-cfg=cfg(bazel)");

    // Default builds use pre-generated sources in src/proto/;
    // codegen only runs when generate-bindings is explicitly enabled.
    #[cfg(feature = "generate-bindings")]
    generate_all();

    // Without this, Cargo re-runs the build script on every invocation when no
    // rerun-if-changed lines are emitted (default builds don't emit any).
    #[cfg(not(feature = "generate-bindings"))]
    println!("cargo:rerun-if-changed=build.rs");
}

#[cfg(feature = "generate-bindings")]
fn get_protos_recursively(
    root: &std::path::Path,
) -> std::io::Result<Vec<std::path::PathBuf>> {
    let mut protos = Vec::new();
    for entry in std::fs::read_dir(root)? {
        let entry = entry?;
        if entry.path().is_dir() {
            protos.append(&mut get_protos_recursively(&entry.path())?)
        } else if let Some(ext) = entry.path().extension() {
            if ext == "proto" {
                protos.push(entry.path())
            }
        }
    }
    Ok(protos)
}

#[cfg(feature = "generate-bindings")]
fn generate_all() {
    use std::env;
    use std::fs;
    use std::path::{Path, PathBuf};

    let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());

    let proto_input = Path::new("definitions/protos/");
    println!("cargo:rerun-if-changed={}", proto_input.display());

    let proto_files = get_protos_recursively(proto_input).unwrap_or_else(|_| {
        panic!(
            "generate-bindings: {} not found; this is a bug with nominal-api-proto",
            proto_input.display()
        )
    });

    let proto_out = out_dir.join("proto");
    fs::create_dir_all(&proto_out).unwrap();
    let proto_mod_file = proto_out.join("mod.rs");

    tonic_build::configure()
        .build_server(false)
        .emit_rerun_if_changed(false)
        .compile_well_known_types(true)
        .include_file(&proto_mod_file)
        .out_dir(&proto_out)
        .compile_protos(
            &proto_files,
            &[
                proto_input.to_path_buf(),
                Path::new("definitions/proto-includes/").to_path_buf(),
            ],
        )
        .expect("Failed to compile tonic gRPC stubs from proto definitions");

    // `gen` is a reserved keyword in Rust 2024
    let content = fs::read_to_string(&proto_mod_file).expect("Failed to read generated mod.rs");
    let modified = content.replace("pub mod gen", "pub mod r#gen");
    fs::write(&proto_mod_file, modified).expect("Failed to write modified mod.rs");
}
