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

fn main() {
    let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    let default_swift_pkg = manifest_dir
        .join("vendor")
        .join("foundation-models-c");
    let swift_pkg = env::var("APPLE_FM_SDK_SWIFT_PKG")
        .map(PathBuf::from)
        .unwrap_or(default_swift_pkg);
    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let swift_scratch = out_dir.join("swift-build");

    println!("cargo:rerun-if-env-changed=APPLE_FM_SDK_SWIFT_PKG");
    println!("cargo:rerun-if-changed=wrapper.h");
    let pkg_swift = swift_pkg.join("Package.swift");
    println!("cargo:rerun-if-changed={}", pkg_swift.display());

    let header = swift_pkg
        .join("Sources")
        .join("FoundationModelsCBindings")
        .join("include")
        .join("FoundationModels.h");
    assert!(
        header.exists(),
        "FoundationModels.h not found at {}. Set APPLE_FM_SDK_SWIFT_PKG to the foundation-models-c dir.",
        header.display()
    );
    println!("cargo:rerun-if-changed={}", header.display());

    let status = Command::new("swift")
        .arg("build")
        .arg("-c")
        .arg("release")
        .arg("--scratch-path")
        .arg(&swift_scratch)
        .current_dir(&swift_pkg)
        .status()
        .expect("failed to invoke `swift build` — is Xcode installed?");
    assert!(status.success(), "`swift build` failed in {}", swift_pkg.display());

    let bin_path = Command::new("swift")
        .arg("build")
        .arg("-c")
        .arg("release")
        .arg("--scratch-path")
        .arg(&swift_scratch)
        .arg("--show-bin-path")
        .current_dir(&swift_pkg)
        .output()
        .expect("swift build --show-bin-path failed");
    let bin_dir = String::from_utf8(bin_path.stdout)
        .expect("swift bin path not utf8")
        .trim()
        .to_string();

    println!("cargo:rustc-link-search=native={}", bin_dir);
    println!("cargo:rustc-link-lib=dylib=FoundationModels");
    println!("cargo:rustc-link-arg=-Wl,-rpath,{}", bin_dir);

    let bindings = bindgen::Builder::default()
        .header("wrapper.h")
        .clang_arg(format!("-I{}", header.parent().unwrap().display()))
        .allowlist_function("FM.*")
        .allowlist_type("FM.*")
        .allowlist_var("FM.*")
        .raw_line("pub type FMTaskRef = *const ::std::os::raw::c_void;")
        .derive_default(true)
        .generate()
        .expect("bindgen failed");

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("couldn't write bindings");
}
