use std::error::Error;
use std::path::Path;

fn main() -> Result<(), Box<dyn Error>> {
    let out_dir = std::env::var("OUT_DIR")?;
    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")?;

    // The specs ship inside the nifi-openapi-gen crate. The helper resolves
    // them relative to nifi-openapi-gen's own manifest dir, which works whether
    // that crate is consumed via path dep (workspace dev build) or pulled from
    // the registry (downstream consumers, `cargo package` verify build).
    let specs_dir = nifi_openapi_gen::specs_dir();
    let specs_dir = specs_dir
        .canonicalize()
        .map_err(|e| format!("Cannot find specs dir {}: {e}", specs_dir.display()))?;

    let config = nifi_openapi_gen::build_api::GenerateConfig::from_cargo_env();

    // Guard against the "no version feature enabled" configuration. This can
    // only happen with --no-default-features and no explicit nifi-x-y-z or
    // `dynamic` feature. Without this guard the build silently produces an
    // empty API surface. The same guard is enforced statically in lib.rs via
    // compile_error!; this build-side check gives an earlier, clearer message.
    if config.versions.is_empty() && !config.dynamic {
        return Err("No NiFi feature enabled. Enable at least one of: \
             `nifi-2-6-0`, `nifi-2-7-2`, `nifi-2-8-0`, `nifi-2-9-0`, \
             or `dynamic` in your Cargo.toml."
            .into());
    }

    // Tell rustc which features are active. lib.rs uses these cfgs as
    // defence-in-depth static guards for code paths that somehow bypass
    // this build script. These flags are not Cargo features, so they are
    // invisible to users — there is no way to set them from the outside.
    if !config.versions.is_empty() {
        println!("cargo::rustc-cfg=has_any_version");
    }
    if !config.versions.is_empty() || config.dynamic {
        println!("cargo::rustc-cfg=has_dynamic_or_version");
    }
    println!("cargo::rustc-check-cfg=cfg(has_any_version)");
    println!("cargo::rustc-check-cfg=cfg(has_dynamic_or_version)");

    nifi_openapi_gen::build_api::generate_client(&specs_dir, Path::new(&out_dir), &config);

    // Copy hand-written strategy.rs and client.rs from src/dynamic/ into
    // $OUT_DIR/dynamic/ so the generated mod.rs (emitted by emit_dynamic) can
    // include them via `pub mod strategy;` and `pub mod client;`.
    // The mod.rs itself is generated by the emitter and must NOT be copied.
    if config.dynamic {
        let dynamic_src_dir = Path::new(&manifest_dir).join("src/dynamic");
        let dynamic_dst_dir = Path::new(&out_dir).join("dynamic");
        std::fs::create_dir_all(&dynamic_dst_dir)
            .map_err(|e| format!("failed to create dynamic dir: {e}"))?;
        for entry in std::fs::read_dir(&dynamic_src_dir)
            .map_err(|e| format!("failed to read src/dynamic: {e}"))?
        {
            let entry = entry.map_err(|e| format!("read_dir entry: {e}"))?;
            let src = entry.path();
            if src.extension().and_then(|s| s.to_str()) == Some("rs")
                && entry.file_name() != "mod.rs"
            {
                let dst = dynamic_dst_dir.join(entry.file_name());
                std::fs::copy(&src, &dst).map_err(|e| {
                    format!("failed to copy {} to {}: {e}", src.display(), dst.display())
                })?;
            }
        }
        println!("cargo::rerun-if-changed=src/dynamic");
    }

    println!("cargo::rerun-if-changed={}", specs_dir.display());
    println!("cargo::rerun-if-changed=build.rs");
    println!("cargo::rerun-if-changed=src/dynamic/strategy.rs");

    Ok(())
}
