//! Bundled iPXE boot binaries and default chain script.
//!
//! At build time, we expect the iPXE binaries to live at `assets/ipxe/` at
//! the workspace root. They are embedded into the OpenPXE binary via
//! `rust-embed` so the container image is self-contained. If a binary is
//! missing, that architecture simply won't have PXE support — we log at
//! startup and serve what we have.
//!
//! Filename convention (matches `ClientArch::ipxe_bootfile`):
//!   - `undionly.kpxe`       — Legacy x86 BIOS
//!   - `snponly-i386.efi`    — IA32 UEFI
//!   - `snponly.efi`         — x86_64 UEFI
//!   - `snponly-arm32.efi`   — ARM32 UEFI
//!   - `snponly-arm64.efi`   — ARM64 UEFI
//!   - `ipxe.efi` (fallback) — UEFI with bundled drivers, if snponly fails on a NIC
//!   - `wimboot`             — Windows boot shim (fetched separately for WIM chains)
#![forbid(unsafe_code)]

use openpxe_core::ClientArch;
use rust_embed::Embed;

#[derive(Embed)]
#[folder = "../../assets/ipxe/"]
#[include = "*.kpxe"]
#[include = "*.efi"]
#[include = "wimboot"]
pub struct IpxeAssets;

/// Return the embedded iPXE binary for `arch`, or `None` if we didn't bundle
/// one for that architecture.
#[must_use]
pub fn bootfile_bytes(arch: ClientArch) -> Option<Vec<u8>> {
    let name = arch.ipxe_bootfile()?;
    IpxeAssets::get(name).map(|f| f.data.into_owned())
}

/// Return a named asset directly (e.g. `wimboot`, or a fallback `ipxe.efi`).
#[must_use]
pub fn asset_bytes(name: &str) -> Option<Vec<u8>> {
    IpxeAssets::get(name).map(|f| f.data.into_owned())
}

/// Same as [`asset_bytes`] but returns the embedded slice directly,
/// avoiding the heap copy when the caller only needs to read the
/// payload. Falls back to None for unknown names.
#[must_use]
pub fn asset_slice(name: &str) -> Option<std::borrow::Cow<'static, [u8]>> {
    IpxeAssets::get(name).map(|f| f.data)
}

/// Enumerate embedded asset filenames. Useful for startup logging so the
/// operator can immediately tell which architectures will work.
pub fn list_assets() -> Vec<String> {
    IpxeAssets::iter()
        .map(std::borrow::Cow::into_owned)
        .collect()
}

/// Log at startup which iPXE binaries are present and which are missing.
pub fn log_availability() {
    let have: std::collections::HashSet<String> = list_assets().into_iter().collect();
    let needed = [
        (ClientArch::LegacyX86, "undionly.kpxe"),
        (ClientArch::Ia32Uefi, "snponly-i386.efi"),
        (ClientArch::X64Uefi, "snponly.efi"),
        // ARM32 UEFI deferred — no upstream snponly binary published.
        (ClientArch::Arm64Uefi, "snponly-arm64.efi"),
    ];
    for (arch, name) in needed {
        if have.contains(name) {
            tracing::info!(target: "openpxe::ipxe", "bundled iPXE for {}: {}", arch.as_str(), name);
        } else {
            tracing::warn!(
                target: "openpxe::ipxe",
                "MISSING iPXE binary for {}: {} — clients of this arch will not PXE boot",
                arch.as_str(), name
            );
        }
    }
}
