//! Pre-built runtime assets for supermachine, packaged as a Rust
//! crate so embedders can `cargo add supermachine-kernel` instead
//! of fetching binaries out-of-band.
//!
//! Two assets are bundled:
//!
//! 1. **Linux kernel image** ([`KERNEL_BYTES`]) — ~29 MiB aarch64
//!    `Image` format with the AF_TSI patch series applied.
//! 2. **`init-oci` shim** ([`INIT_OCI_BYTES`]) — ~1.6 MiB statically
//!    linked aarch64-musl binary. PID 1 inside each microVM. Sets
//!    up overlayfs, mounts /proc + /dev, exec's the customer's
//!    image entrypoint. Only needed if you bake images at runtime
//!    (i.e. via the future `Image::from_oci(...)` API); restoring
//!    a snapshot doesn't need it.
//!
//! Versioned in lockstep with the [`supermachine`] library — pin
//! both to the same version.
//!
//! ## Use cases
//!
//! ### From a `build.rs` (recommended for `.app` bundles)
//!
//! Pre-stage the assets into your bundle's Resources at build time
//! so the resulting `.app` is self-contained:
//!
//! ```no_run
//! // build.rs
//! fn main() {
//!     let resources = std::path::PathBuf::from(
//!         std::env::var("OUT_DIR").unwrap()
//!     ).join("../../../bundle-resources");
//!     std::fs::create_dir_all(&resources).unwrap();
//!     supermachine_kernel::extract_kernel_to(&resources.join("kernel")).unwrap();
//!     supermachine_kernel::extract_init_oci_to(&resources.join("init-oci")).unwrap();
//! }
//! ```
//!
//! ### From runtime code
//!
//! Extract once on first start to a writable scratch dir:
//!
//! ```no_run
//! use std::path::PathBuf;
//!
//! fn ensure_assets() -> std::io::Result<PathBuf> {
//!     let dir = std::env::temp_dir().join("supermachine-assets");
//!     std::fs::create_dir_all(&dir)?;
//!     supermachine_kernel::extract_kernel_to(&dir.join("kernel"))?;
//!     supermachine_kernel::extract_init_oci_to(&dir.join("init-oci"))?;
//!     Ok(dir)
//! }
//! ```
//!
//! Then point [`supermachine::AssetPaths::from_dir`] at the result.
//!
//! [`supermachine`]: https://crates.io/crates/supermachine
//! [`supermachine::AssetPaths::from_dir`]: https://docs.rs/supermachine/latest/supermachine/assets/struct.AssetPaths.html#method.from_dir

/// Raw bytes of the kernel image — an aarch64 Linux `Image` format
/// binary (raw kernel, no ELF wrapper). About 29 MiB.
///
/// The supermachine VMM expects the bytes loaded into guest RAM at
/// the boot offset (0x80000 from RAM start) and PC pointed at the
/// load address. The library handles that wiring internally; you
/// just need the path on disk.
///
/// The bytes are staged into `OUT_DIR` by `build.rs`, which
/// resolves them from one of three sources (env override, dev tree,
/// or download from the matching GitHub release). See `build.rs`
/// for the full discovery order.
pub const KERNEL_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/kernel"));

/// Length of the kernel image in bytes — equivalent to
/// `KERNEL_BYTES.len()` but evaluable in `const` contexts.
pub const KERNEL_LEN: usize = KERNEL_BYTES.len();

/// Raw bytes of the in-VM init shim (statically-linked aarch64-musl
/// ELF executable, ~1.6 MiB). The CLI's bake step copies this into
/// the guest's initramfs as PID 1; it sets up overlayfs, mounts
/// /proc + /dev, then exec's the OCI image's entrypoint.
///
/// Embedders that only restore snapshots don't need to extract this
/// — restoring brings back the post-init state directly. It's only
/// needed if you bake fresh images at runtime, which today still
/// goes through the `supermachine` CLI (the future
/// `Image::from_oci(...)` library API will use this constant).
pub const INIT_OCI_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/init-oci"));

/// Length of the init-oci binary in bytes.
pub const INIT_OCI_LEN: usize = INIT_OCI_BYTES.len();

/// Raw bytes of the in-VM `supermachine-agent` binary
/// (statically-linked aarch64-musl ELF, ~430 KiB). The CLI's bake
/// step copies this into the delta layer at `/supermachine-agent`;
/// `init-oci` forks + exec's it post-pivot to provide
/// docker-style `exec` and other in-guest control RPCs over vsock.
///
/// Like [`INIT_OCI_BYTES`], only the bake path uses this — pure
/// snapshot-restore embedders don't need to extract it.
pub const SUPERMACHINE_AGENT_BYTES: &[u8] =
    include_bytes!(concat!(env!("OUT_DIR"), "/supermachine-agent"));

/// Length of the supermachine-agent binary in bytes.
pub const SUPERMACHINE_AGENT_LEN: usize = SUPERMACHINE_AGENT_BYTES.len();

/// Raw bytes of the `smpark.ko` Linux kernel module (~10 KiB,
/// aarch64). Loaded by `init-oci` BEFORE the rootfs pivot via
/// `finit_module`. Provides `/dev/smpark` with PARK/UNPARK
/// ioctls; the host's bake driver brackets the BASE+WARM
/// snapshot captures with PARK/UNPARK RPCs to drive secondary
/// vCPUs into a known byte-identical parked-WFI state that
/// HVF can round-trip cleanly. Required for `vcpus > 1`
/// reliability — see `docs/design/multi-vcpu-shipped-2026-05-18.md`.
///
/// vermagic is pinned to the kernel build that produced
/// [`KERNEL_BYTES`] (rebuild via `kernel-build/scripts/build.sh`
/// keeps them in sync). On a mismatch the module load fails
/// silently and multi-vCPU bakes fall back to the pre-fix
/// intermittent capture path; single-vCPU is unaffected.
pub const SMPARK_KO_BYTES: &[u8] =
    include_bytes!(concat!(env!("OUT_DIR"), "/smpark.ko"));

/// Length of the smpark.ko module in bytes.
pub const SMPARK_KO_LEN: usize = SMPARK_KO_BYTES.len();

/// Write the bundled kernel image to `dest`. Overwrites any
/// existing file. Caller is responsible for the parent dir
/// existing — use [`extract_kernel_to_with_parents`] if you'd
/// rather mkdir -p.
///
/// ```no_run
/// supermachine_kernel::extract_kernel_to(
///     std::path::Path::new("/tmp/supermachine/kernel"),
/// ).unwrap();
/// ```
pub fn extract_kernel_to(dest: &std::path::Path) -> std::io::Result<()> {
    std::fs::write(dest, KERNEL_BYTES)
}

/// Like [`extract_kernel_to`] but `mkdir -p`'s the parent dir first.
pub fn extract_kernel_to_with_parents(dest: &std::path::Path) -> std::io::Result<()> {
    if let Some(parent) = dest.parent() {
        std::fs::create_dir_all(parent)?;
    }
    extract_kernel_to(dest)
}

/// Write the bundled init-oci binary to `dest`. Sets it executable
/// (mode 0o755) on Unix.
pub fn extract_init_oci_to(dest: &std::path::Path) -> std::io::Result<()> {
    std::fs::write(dest, INIT_OCI_BYTES)?;
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        let mut perms = std::fs::metadata(dest)?.permissions();
        perms.set_mode(0o755);
        std::fs::set_permissions(dest, perms)?;
    }
    Ok(())
}

/// Like [`extract_init_oci_to`] but `mkdir -p`'s the parent dir first.
pub fn extract_init_oci_to_with_parents(dest: &std::path::Path) -> std::io::Result<()> {
    if let Some(parent) = dest.parent() {
        std::fs::create_dir_all(parent)?;
    }
    extract_init_oci_to(dest)
}

/// Write the bundled `supermachine-agent` binary to `dest`. Sets it
/// executable (mode 0o755) on Unix.
pub fn extract_supermachine_agent_to(dest: &std::path::Path) -> std::io::Result<()> {
    std::fs::write(dest, SUPERMACHINE_AGENT_BYTES)?;
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        let mut perms = std::fs::metadata(dest)?.permissions();
        perms.set_mode(0o755);
        std::fs::set_permissions(dest, perms)?;
    }
    Ok(())
}

/// Like [`extract_supermachine_agent_to`] but `mkdir -p`'s the parent dir first.
pub fn extract_supermachine_agent_to_with_parents(
    dest: &std::path::Path,
) -> std::io::Result<()> {
    if let Some(parent) = dest.parent() {
        std::fs::create_dir_all(parent)?;
    }
    extract_supermachine_agent_to(dest)
}

/// Write the bundled smpark.ko kernel module to `dest`. Caller
/// is responsible for the parent dir existing — use
/// [`extract_smpark_ko_to_with_parents`] for `mkdir -p`.
pub fn extract_smpark_ko_to(dest: &std::path::Path) -> std::io::Result<()> {
    std::fs::write(dest, SMPARK_KO_BYTES)
}

/// Like [`extract_smpark_ko_to`] but `mkdir -p`'s the parent dir first.
pub fn extract_smpark_ko_to_with_parents(
    dest: &std::path::Path,
) -> std::io::Result<()> {
    if let Some(parent) = dest.parent() {
        std::fs::create_dir_all(parent)?;
    }
    extract_smpark_ko_to(dest)
}

// ---------- backward compat ----------
// The earlier 0.1.0-pre layout shipped only the kernel. Keep these
// names available for one minor so anyone who tracked HEAD doesn't
// break.

/// Deprecated alias for [`extract_kernel_to`]. Will be removed in 0.2.
#[deprecated(since = "0.1.0", note = "use extract_kernel_to")]
pub fn extract_to(dest: &std::path::Path) -> std::io::Result<()> {
    extract_kernel_to(dest)
}

/// Deprecated alias for [`extract_kernel_to_with_parents`].
#[deprecated(since = "0.1.0", note = "use extract_kernel_to_with_parents")]
pub fn extract_to_with_parents(dest: &std::path::Path) -> std::io::Result<()> {
    extract_kernel_to_with_parents(dest)
}
