use anyhow::bail;
use build_print::info;
use cc;
use conda_build::python_config;
use std::env;
use std::path::PathBuf;

const NANOBIND_SOURCES: [&str; 7] = [
    "common.cpp",
    "trampoline.cpp",
    "nb_func.cpp",
    "nb_type.cpp",
    "nb_internals.cpp",
    "error.cpp",
    "implicit.cpp",
];

fn main() -> Result<(), anyhow::Error> {
    // Need to build it
    let (pyc_e, py_conda) = python_config();

    // This is very clumsy due to PythonConfig::Error not supporting
    // conversion into std::error::Error, just std::io::Error.
    let (pyc, mut includes, _py_ldflags) = pyc_e
        .and_then(|pyc| {
            pyc.include_paths()
                .and_then(|ip| pyc.ldflags().map(|ld| (pyc, ip, ld)))
        })
        .map_err(|e| anyhow::Error::from(std::io::Error::from(e)))?;

    let nanobind_root = pyc
        .prefix_path().ok()
        .zip(pyc.semantic_version().ok())
        .and_then(|(prefix, version)| {
            info!("{}Python {} found at prefix {}", if py_conda { "Conda " } else { ""}, version, prefix.display());
            let nanobind_root = prefix
                .join("lib")
                .join(format!("python{}.{}", version.major, version.minor))
                .join("site-packages")
                .join("nanobind");
            if !nanobind_root.exists() {
                info!("… but no nanobind installed there");
                None
            } else {
                Some(nanobind_root)
            }
        })
        .map_or_else(|| {
            let last_ditch = "/usr/share/nanobind";
            let nanobind_root = PathBuf::from(last_ditch);
            if !nanobind_root.exists() {
                bail!("Could not find nanobind in known locations. The recommended installation method is with pip at system Python location, or with Conda into the active environment. Also /usr/share/nanobind was attempted.");
            } else {
                info!("Found in {last_ditch}.");
                Ok(nanobind_root)
            }
        }, Ok)?;

    let nanobind_src = nanobind_root.join("src");

    println!("cargo:rerun-if-changed=build.rs");

    //println!("cargo:rustc-link-arg={}", py_ldflags);

    includes.extend([
        nanobind_root.join("include"),
        nanobind_root.join("ext/robin_map/include"),
    ]);

    println!(
        "cargo:include={}",
        env::join_paths(&includes)?.into_string().unwrap()
    );

    cc::Build::new()
        .std("c++20")
        .files(NANOBIND_SOURCES.map(|f| nanobind_src.join(f)))
        .includes(includes)
        //.flags(pyc.cflags()?)
        .compile("nanobind-sys");

    Ok(())
}
