#!/bin/sh
# build.sh — internal ISO build entrypoint executed inside the builder container.

set -e

if [ "${BEE_CONTAINER_BUILD:-0}" != "1" ]; then
    echo "build.sh must run inside iso/builder/build-in-container.sh" >&2
    exit 1
fi

REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
BUILDER_DIR="${REPO_ROOT}/iso/builder"
OVERLAY_DIR="${REPO_ROOT}/iso/overlay"
DIST_DIR="${REPO_ROOT}/dist"
VENDOR_DIR="${REPO_ROOT}/iso/vendor"
CACHE_ROOT="${BEE_CACHE_DIR:-${DIST_DIR}/cache}"
AUTH_KEYS=""
BUILD_VARIANT="nvidia"
BEE_GPU_VENDOR="nvidia"
BEE_NVIDIA_MODULE_FLAVOR="open"

# parse args
while [ $# -gt 0 ]; do
    case "$1" in
        --authorized-keys) AUTH_KEYS="$2"; shift 2 ;;
        --variant) BUILD_VARIANT="$2"; shift 2 ;;
        *) echo "unknown arg: $1"; exit 1 ;;
    esac
done

case "$BUILD_VARIANT" in
    nvidia)
        BEE_GPU_VENDOR="nvidia"
        BEE_NVIDIA_MODULE_FLAVOR="open"
        ;;
    nvidia-legacy)
        BEE_GPU_VENDOR="nvidia"
        BEE_NVIDIA_MODULE_FLAVOR="proprietary"
        ;;
    amd)
        BEE_GPU_VENDOR="amd"
        BEE_NVIDIA_MODULE_FLAVOR=""
        ;;
    nogpu)
        BEE_GPU_VENDOR="nogpu"
        BEE_NVIDIA_MODULE_FLAVOR=""
        ;;
    *)
        echo "unknown variant: $BUILD_VARIANT (expected nvidia, nvidia-legacy, amd, or nogpu)" >&2
        exit 1
        ;;
esac

BUILD_WORK_DIR="${DIST_DIR}/live-build-work-${BUILD_VARIANT}"
OVERLAY_STAGE_DIR="${DIST_DIR}/overlay-stage-${BUILD_VARIANT}"

export BEE_GPU_VENDOR BEE_NVIDIA_MODULE_FLAVOR BUILD_VARIANT

. "${BUILDER_DIR}/VERSIONS"
export MEMTEST_VERSION
export PATH="$PATH:/usr/local/go/bin"
: "${BEE_REQUIRE_MEMTEST:=0}"

# Allow git to read the bind-mounted repo (different UID inside container).
git config --global safe.directory "${REPO_ROOT}"
mkdir -p "${DIST_DIR}"
mkdir -p "${CACHE_ROOT}"
: "${GOCACHE:=${CACHE_ROOT}/go-build}"
: "${GOMODCACHE:=${CACHE_ROOT}/go-mod}"
export GOCACHE GOMODCACHE

resolve_audit_version() {
    if [ -n "${BEE_AUDIT_VERSION:-}" ]; then
        echo "${BEE_AUDIT_VERSION}"
        return 0
    fi

    tag="$(git -C "${REPO_ROOT}" describe --tags --match 'v[0-9]*' --abbrev=7 --dirty 2>/dev/null || true)"
    case "${tag}" in
        v*)
            echo "${tag#v}"
            return 0
            ;;
        "")
            ;;
        *)
            echo "${tag}"
            return 0
            ;;
    esac

    if [ -n "${AUDIT_VERSION:-}" ]; then
        echo "${AUDIT_VERSION}"
        return 0
    fi

    date +%Y%m%d
}

# ISO image versioned separately from the audit binary (iso/v* tags).
resolve_iso_version() {
    if [ -n "${BEE_ISO_VERSION:-}" ]; then
        echo "${BEE_ISO_VERSION}"
        return 0
    fi

    # Plain v* tags (e.g. v2.7) take priority — this is the current tagging scheme
    tag="$(git -C "${REPO_ROOT}" describe --tags --match 'v[0-9]*' --abbrev=7 --dirty 2>/dev/null || true)"
    case "${tag}" in
        v*)
            echo "${tag#v}"
            return 0
            ;;
    esac

    # Legacy iso/v* tags fallback
    tag="$(git -C "${REPO_ROOT}" describe --tags --match 'iso/v*' --abbrev=7 --dirty 2>/dev/null || true)"
    case "${tag}" in
        iso/v*)
            echo "${tag#iso/v}"
            return 0
            ;;
    esac

    # Fall back to audit version so the name is still meaningful
    resolve_audit_version
}

iso_list_files() {
    iso_path="$1"

    if command -v bsdtar >/dev/null 2>&1; then
        bsdtar -tf "$iso_path"
        return $?
    fi

    if command -v xorriso >/dev/null 2>&1; then
        xorriso -indev "$iso_path" -find / -type f -print 2>/dev/null | sed 's#^/##'
        return $?
    fi

    return 127
}

iso_extract_file() {
    iso_path="$1"
    iso_member="$2"

    if command -v bsdtar >/dev/null 2>&1; then
        bsdtar -xOf "$iso_path" "$iso_member"
        return $?
    fi

    if command -v xorriso >/dev/null 2>&1; then
        xorriso -osirrox on -indev "$iso_path" -cat "/$iso_member" 2>/dev/null
        return $?
    fi

    return 127
}

iso_read_file_list() {
    iso_path="$1"
    out_path="$2"

    iso_list_files "$iso_path" > "$out_path" || return 1
    [ -s "$out_path" ] || return 1
    return 0
}

iso_read_member() {
    iso_path="$1"
    iso_member="$2"
    out_path="$3"

    iso_extract_file "$iso_path" "$iso_member" > "$out_path" || return 1
    [ -s "$out_path" ] || return 1
    return 0
}

require_iso_reader() {
    command -v bsdtar >/dev/null 2>&1 && return 0
    command -v xorriso >/dev/null 2>&1 && return 0
    memtest_fail "ISO reader is required for validation/debug (expected bsdtar or xorriso)" "${1:-}"
}

dump_memtest_debug() {
    phase="$1"
    lb_dir="${2:-}"
    iso_path="${3:-}"
    phase_slug="$(printf '%s' "${phase}" | tr ' /' '__')"
    memtest_log="${LOG_DIR:-}/memtest-${phase_slug}.log"

    (
        echo "=== memtest debug: ${phase} ==="

        echo "-- auto/config --"
        if [ -f "${BUILDER_DIR}/auto/config" ]; then
            grep -n -- '--memtest' "${BUILDER_DIR}/auto/config" || echo "  (no --memtest line found)"
        else
            echo "  (missing ${BUILDER_DIR}/auto/config)"
        fi

        echo "-- source bootloader templates --"
        for cfg in \
            "${BUILDER_DIR}/config/bootloaders/grub-pc/grub.cfg" \
            "${BUILDER_DIR}/config/bootloaders/isolinux/live.cfg.in"; do
            if [ -f "$cfg" ]; then
                echo "  file: $cfg"
                grep -n 'Memory Test\|memtest' "$cfg" || echo "    (no memtest lines)"
            fi
        done

        echo "-- source binary hooks --"
        for hook in \
            "${BUILDER_DIR}/config/hooks/normal/9100-memtest.hook.binary"; do
            if [ -f "$hook" ]; then
                echo "  hook: $hook"
            else
                echo "  (missing $hook)"
            fi
        done

        if [ -n "$lb_dir" ] && [ -d "$lb_dir" ]; then
            echo "-- live-build workdir package lists --"
            for pkg in \
                "$lb_dir/config/package-lists/bee.list.chroot" \
                "$lb_dir/config/package-lists/bee-gpu.list.chroot" \
                "$lb_dir/config/package-lists/bee-nvidia.list.chroot"; do
                if [ -f "$pkg" ]; then
                    echo "  file: $pkg"
                    grep -n 'memtest' "$pkg" || echo "    (no memtest lines)"
                fi
            done

            echo "-- live-build chroot/boot --"
            if [ -d "$lb_dir/chroot/boot" ]; then
                find "$lb_dir/chroot/boot" -maxdepth 1 -name 'memtest*' -print | sed 's/^/  /' || true
            else
                echo "  (missing $lb_dir/chroot/boot)"
            fi

            echo "-- live-build binary/boot --"
            if [ -d "$lb_dir/binary/boot" ]; then
                find "$lb_dir/binary/boot" -maxdepth 1 -name 'memtest*' -print | sed 's/^/  /' || true
            else
                echo "  (missing $lb_dir/binary/boot)"
            fi

            echo "-- live-build binary grub cfg --"
            if [ -f "$lb_dir/binary/boot/grub/grub.cfg" ]; then
                grep -n 'Memory Test\|memtest' "$lb_dir/binary/boot/grub/grub.cfg" || echo "  (no memtest lines)"
            else
                echo "  (missing $lb_dir/binary/boot/grub/grub.cfg)"
            fi

            echo "-- live-build binary isolinux cfg --"
            if [ -f "$lb_dir/binary/isolinux/live.cfg" ]; then
                grep -n 'Memory Test\|memtest' "$lb_dir/binary/isolinux/live.cfg" || echo "  (no memtest lines)"
            else
                echo "  (missing $lb_dir/binary/isolinux/live.cfg)"
            fi

            echo "-- live-build package cache --"
            if [ -d "$lb_dir/cache/packages.chroot" ]; then
                find "$lb_dir/cache/packages.chroot" -maxdepth 1 -name 'memtest86+*.deb' -print | sed 's/^/  /' || true
            else
                echo "  (missing $lb_dir/cache/packages.chroot)"
            fi
        fi

        if [ -n "$iso_path" ] && [ -f "$iso_path" ]; then
            iso_files="$(mktemp)"
            iso_grub_cfg="$(mktemp)"
            iso_isolinux_cfg="$(mktemp)"

            echo "-- ISO memtest files --"
            if iso_read_file_list "$iso_path" "$iso_files"; then
                grep 'memtest' "$iso_files" | sed 's/^/  /' || echo "  (no memtest files in ISO)"
            else
                echo "  (failed to list ISO contents)"
            fi

            echo "-- ISO GRUB memtest lines --"
            if iso_read_member "$iso_path" boot/grub/grub.cfg "$iso_grub_cfg"; then
                grep -n 'Memory Test\|memtest' "$iso_grub_cfg" || echo "  (no memtest lines in boot/grub/grub.cfg)"
            else
                echo "  (failed to read boot/grub/grub.cfg from ISO)"
            fi

            echo "-- ISO isolinux memtest lines --"
            if iso_read_member "$iso_path" isolinux/live.cfg "$iso_isolinux_cfg"; then
                grep -n 'Memory Test\|memtest' "$iso_isolinux_cfg" || echo "  (no memtest lines in isolinux/live.cfg)"
            else
                echo "  (failed to read isolinux/live.cfg from ISO)"
            fi

            rm -f "$iso_files" "$iso_grub_cfg" "$iso_isolinux_cfg"
        fi

        echo "=== end memtest debug: ${phase} ==="
    ) | {
        if [ -n "${LOG_DIR:-}" ] && [ -d "${LOG_DIR}" ]; then
            tee "${memtest_log}"
        else
            cat
        fi
    }
}

memtest_fail() {
    msg="$1"
    iso_path="${2:-}"
    level="WARNING"
    if [ "${BEE_REQUIRE_MEMTEST:-0}" = "1" ]; then
        level="ERROR"
    fi
    echo "${level}: ${msg}" >&2
    dump_memtest_debug "failure" "${LB_DIR:-}" "$iso_path" >&2
    if [ "${BEE_REQUIRE_MEMTEST:-0}" = "1" ]; then
        exit 1
    fi
    return 0
}

nvidia_runtime_fail() {
    msg="$1"
    echo "ERROR: ${msg}" >&2
    exit 1
}

iso_memtest_present() {
    iso_path="$1"
    iso_files="$(mktemp)"

    [ -f "$iso_path" ] || return 1

    if command -v bsdtar >/dev/null 2>&1; then
        :
    elif command -v xorriso >/dev/null 2>&1; then
        :
    else
        return 2
    fi

    iso_read_file_list "$iso_path" "$iso_files" || {
        rm -f "$iso_files"
        return 2
    }

    grep -q '^boot/memtest86+x64\.bin$' "$iso_files" || {
        rm -f "$iso_files"
        return 1
    }
    grep -q '^boot/memtest86+x64\.efi$' "$iso_files" || {
        rm -f "$iso_files"
        return 1
    }

    grub_cfg="$(mktemp)"
    isolinux_cfg="$(mktemp)"

    iso_read_member "$iso_path" boot/grub/grub.cfg "$grub_cfg" || {
        rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
        return 2
    }
    iso_read_member "$iso_path" isolinux/live.cfg "$isolinux_cfg" || {
        rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
        return 2
    }

    grep -q 'Memory Test (memtest86+)' "$grub_cfg" || {
        rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
        return 1
    }
    grep -q '/boot/memtest86+x64\.efi' "$grub_cfg" || {
        rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
        return 1
    }
    grep -q '/boot/memtest86+x64\.bin' "$grub_cfg" || {
        rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
        return 1
    }
    grep -q 'Memory Test (memtest86+)' "$isolinux_cfg" || {
        rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
        return 1
    }
    grep -q '/boot/memtest86+x64\.bin' "$isolinux_cfg" || {
        rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
        return 1
    }

    rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
    return 0
}

validate_iso_memtest() {
    iso_path="$1"
    echo "=== validating memtest in ISO ==="

    [ -f "$iso_path" ] || {
        memtest_fail "ISO not found for validation: $iso_path" "$iso_path"
        return 0
    }
    require_iso_reader "$iso_path" || return 0

    iso_files="$(mktemp)"
    iso_read_file_list "$iso_path" "$iso_files" || {
        memtest_fail "failed to list ISO contents while validating memtest" "$iso_path"
        rm -f "$iso_files"
        return 0
    }

    grep -q '^boot/memtest86+x64\.bin$' "$iso_files" || {
        memtest_fail "memtest BIOS binary missing in ISO: boot/memtest86+x64.bin" "$iso_path"
        rm -f "$iso_files"
        return 0
    }
    grep -q '^boot/memtest86+x64\.efi$' "$iso_files" || {
        memtest_fail "memtest EFI binary missing in ISO: boot/memtest86+x64.efi" "$iso_path"
        rm -f "$iso_files"
        return 0
    }

    grub_cfg="$(mktemp)"
    isolinux_cfg="$(mktemp)"

    iso_read_member "$iso_path" boot/grub/grub.cfg "$grub_cfg" || {
        memtest_fail "failed to read boot/grub/grub.cfg from ISO" "$iso_path"
        rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
        return 0
    }
    iso_read_member "$iso_path" isolinux/live.cfg "$isolinux_cfg" || {
        memtest_fail "failed to read isolinux/live.cfg from ISO" "$iso_path"
        rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
        return 0
    }

    grep -q 'Memory Test (memtest86+)' "$grub_cfg" || {
        memtest_fail "GRUB menu entry for memtest is missing" "$iso_path"
        rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
        return 0
    }
    grep -q '/boot/memtest86+x64\.efi' "$grub_cfg" || {
        memtest_fail "GRUB memtest EFI path is missing" "$iso_path"
        rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
        return 0
    }
    grep -q '/boot/memtest86+x64\.bin' "$grub_cfg" || {
        memtest_fail "GRUB memtest BIOS path is missing" "$iso_path"
        rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
        return 0
    }
    grep -q 'Memory Test (memtest86+)' "$isolinux_cfg" || {
        memtest_fail "isolinux menu entry for memtest is missing" "$iso_path"
        rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
        return 0
    }
    grep -q '/boot/memtest86+x64\.bin' "$isolinux_cfg" || {
        memtest_fail "isolinux memtest path is missing" "$iso_path"
        rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
        return 0
    }

    rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
    echo "=== memtest validation OK ==="
}

validate_iso_nvidia_runtime() {
    iso_path="$1"
    [ "$BEE_GPU_VENDOR" = "nvidia" ] || return 0

    echo "=== validating NVIDIA runtime in ISO ==="

    [ -f "$iso_path" ] || nvidia_runtime_fail "ISO not found for NVIDIA runtime validation: $iso_path"
    require_iso_reader "$iso_path" >/dev/null 2>&1 || nvidia_runtime_fail "ISO reader unavailable for NVIDIA runtime validation"
    command -v unsquashfs >/dev/null 2>&1 || nvidia_runtime_fail "unsquashfs is required for NVIDIA runtime validation"

    squashfs_tmp="$(mktemp)"
    squashfs_list="$(mktemp)"
    iso_read_member "$iso_path" live/filesystem.squashfs "$squashfs_tmp" || {
        rm -f "$squashfs_tmp" "$squashfs_list"
        nvidia_runtime_fail "failed to extract live/filesystem.squashfs from ISO"
    }
    unsquashfs -ll "$squashfs_tmp" > "$squashfs_list" 2>/dev/null || {
        rm -f "$squashfs_tmp" "$squashfs_list"
        nvidia_runtime_fail "failed to inspect filesystem.squashfs from ISO"
    }

    grep -Eq 'usr/bin/dcgmi$' "$squashfs_list" || {
        rm -f "$squashfs_tmp" "$squashfs_list"
        nvidia_runtime_fail "dcgmi missing from final NVIDIA ISO"
    }
    grep -Eq 'usr/bin/nv-hostengine$' "$squashfs_list" || {
        rm -f "$squashfs_tmp" "$squashfs_list"
        nvidia_runtime_fail "nv-hostengine missing from final NVIDIA ISO"
    }
    grep -Eq 'usr/bin/dcgmproftester([0-9]+)?$' "$squashfs_list" || {
        rm -f "$squashfs_tmp" "$squashfs_list"
        nvidia_runtime_fail "dcgmproftester missing from final NVIDIA ISO"
    }

    rm -f "$squashfs_tmp" "$squashfs_list"
    echo "=== NVIDIA runtime validation OK ==="
}

append_memtest_grub_entry() {
    grub_cfg="$1"
    [ -f "$grub_cfg" ] || return 1
    grep -q 'Memory Test (memtest86+)' "$grub_cfg" && return 0
    grep -q '### BEE MEMTEST ###' "$grub_cfg" && return 0

    cat >> "$grub_cfg" <<'EOF'

### BEE MEMTEST ###
if [ "${grub_platform}" = "efi" ]; then
    menuentry "Memory Test (memtest86+)" {
        chainloader /boot/memtest86+x64.efi
    }
else
    menuentry "Memory Test (memtest86+)" {
        linux16 /boot/memtest86+x64.bin
    }
fi
### /BEE MEMTEST ###
EOF
}

append_memtest_isolinux_entry() {
    isolinux_cfg="$1"
    [ -f "$isolinux_cfg" ] || return 1
    grep -q 'Memory Test (memtest86+)' "$isolinux_cfg" && return 0
    grep -q '### BEE MEMTEST ###' "$isolinux_cfg" && return 0

    cat >> "$isolinux_cfg" <<'EOF'

# ### BEE MEMTEST ###
label memtest
    menu label ^Memory Test (memtest86+)
    linux /boot/memtest86+x64.bin
# ### /BEE MEMTEST ###
EOF
}

copy_memtest_from_deb() {
    deb="$1"
    dst_boot="$2"
    tmpdir="$(mktemp -d)"

    dpkg-deb -x "$deb" "$tmpdir"
    for f in memtest86+x64.bin memtest86+x64.efi; do
        if [ -f "$tmpdir/boot/$f" ]; then
            cp "$tmpdir/boot/$f" "$dst_boot/$f"
        fi
    done
    rm -rf "$tmpdir"
}

reset_live_build_stage() {
    lb_dir="$1"
    stage="$2"

    for root in \
        "$lb_dir/.build" \
        "$lb_dir/.stage" \
        "$lb_dir/auto"; do
        [ -d "$root" ] || continue
        find "$root" -maxdepth 1 \( -name "${stage}" -o -name "${stage}.*" -o -name "*${stage}*" \) -exec rm -rf {} + 2>/dev/null || true
    done
}

recover_iso_memtest() {
    lb_dir="$1"
    iso_path="$2"
    binary_boot="$lb_dir/binary/boot"
    grub_cfg="$lb_dir/binary/boot/grub/grub.cfg"
    isolinux_cfg="$lb_dir/binary/isolinux/live.cfg"

    echo "=== attempting memtest recovery in binary tree ==="

    mkdir -p "$binary_boot"

    for root in \
        "$lb_dir/chroot/boot" \
        "/boot"; do
        for f in memtest86+x64.bin memtest86+x64.efi; do
            if [ ! -f "$binary_boot/$f" ] && [ -f "$root/$f" ]; then
                cp "$root/$f" "$binary_boot/$f"
                echo "memtest recovery: copied $f from $root"
            fi
        done
    done

    if [ ! -f "$binary_boot/memtest86+x64.bin" ] || [ ! -f "$binary_boot/memtest86+x64.efi" ]; then
        for dir in \
            "$lb_dir/cache/packages.binary" \
            "$lb_dir/cache/packages.chroot" \
            "$lb_dir/chroot/var/cache/apt/archives" \
            "${BEE_CACHE_DIR:-${DIST_DIR}/cache}/lb-packages" \
            "/var/cache/apt/archives"; do
            [ -d "$dir" ] || continue
            deb="$(find "$dir" -maxdepth 1 -type f -name 'memtest86+*.deb' 2>/dev/null | head -1)"
            [ -n "$deb" ] || continue
            echo "memtest recovery: extracting payload from $deb"
            copy_memtest_from_deb "$deb" "$binary_boot"
            break
        done
    fi

    if [ ! -f "$binary_boot/memtest86+x64.bin" ] || [ ! -f "$binary_boot/memtest86+x64.efi" ]; then
        tmpdl="$(mktemp -d)"
        if (
            cd "$tmpdl" && apt-get download memtest86+ >/dev/null 2>&1
        ); then
            deb="$(find "$tmpdl" -maxdepth 1 -type f -name 'memtest86+*.deb' 2>/dev/null | head -1)"
            if [ -n "$deb" ]; then
                echo "memtest recovery: downloaded $deb"
                copy_memtest_from_deb "$deb" "$binary_boot"
            fi
        fi
        rm -rf "$tmpdl"
    fi

    if [ -f "$grub_cfg" ]; then
        append_memtest_grub_entry "$grub_cfg" && echo "memtest recovery: ensured GRUB entry"
    else
        echo "memtest recovery: WARNING: missing $grub_cfg"
    fi

    if [ -f "$isolinux_cfg" ]; then
        append_memtest_isolinux_entry "$isolinux_cfg" && echo "memtest recovery: ensured isolinux entry"
    else
        echo "memtest recovery: WARNING: missing $isolinux_cfg"
    fi

    reset_live_build_stage "$lb_dir" "binary_checksums"
    reset_live_build_stage "$lb_dir" "binary_iso"
    reset_live_build_stage "$lb_dir" "binary_zsync"

    run_optional_step_sh "rebuild live-build checksums after memtest recovery" "91-lb-checksums" "lb binary_checksums 2>&1"
    run_optional_step_sh "rebuild ISO after memtest recovery" "92-lb-binary-iso" "rm -f '$iso_path' && lb binary_iso 2>&1"
    run_optional_step_sh "rebuild zsync after memtest recovery" "93-lb-zsync" "lb binary_zsync 2>&1"

    if [ ! -f "$iso_path" ]; then
        memtest_fail "ISO rebuild was skipped or failed after memtest recovery: $iso_path" "$iso_path"
    fi
}

AUDIT_VERSION_EFFECTIVE="$(resolve_audit_version)"
ISO_VERSION_EFFECTIVE="$(resolve_iso_version)"
ISO_BASENAME="easy-bee-${BUILD_VARIANT}-v${ISO_VERSION_EFFECTIVE}-amd64"
# Versioned output directory: dist/easy-bee-v4.1/ — all final artefacts live here.
OUT_DIR="${DIST_DIR}/easy-bee-v${ISO_VERSION_EFFECTIVE}"
mkdir -p "${OUT_DIR}"
LOG_DIR="${OUT_DIR}/${ISO_BASENAME}.logs"
LOG_ARCHIVE="${OUT_DIR}/${ISO_BASENAME}.logs.tar.gz"
ISO_OUT="${OUT_DIR}/${ISO_BASENAME}.iso"
LOG_OUT="${LOG_DIR}/build.log"

cleanup_build_log() {
    status="${1:-$?}"
    trap - EXIT INT TERM HUP

    if [ "${STEP_LOG_ACTIVE:-0}" = "1" ]; then
        cleanup_step_log "${status}" || true
    fi

    if [ "${BUILD_LOG_ACTIVE:-0}" = "1" ]; then
        BUILD_LOG_ACTIVE=0
        exec 1>&3 2>&4
        exec 3>&- 4>&-
        if [ -n "${BUILD_TEE_PID:-}" ]; then
            wait "${BUILD_TEE_PID}" 2>/dev/null || true
        fi
        rm -f "${BUILD_LOG_PIPE}"
    fi

    if [ -n "${LOG_DIR:-}" ] && [ -d "${LOG_DIR}" ] && command -v tar >/dev/null 2>&1; then
        rm -f "${LOG_ARCHIVE}"
        tar -czf "${LOG_ARCHIVE}" -C "$(dirname "${LOG_DIR}")" "$(basename "${LOG_DIR}")" 2>/dev/null || true
        rm -rf "${LOG_DIR}"
    fi

    exit "${status}"
}

start_build_log() {
    command -v tee >/dev/null 2>&1 || {
        echo "ERROR: tee is required for build logging" >&2
        exit 1
    }

    rm -rf "${LOG_DIR}"
    rm -f "${LOG_ARCHIVE}"
    mkdir -p "${LOG_DIR}"
    BUILD_LOG_PIPE="$(mktemp -u "${TMPDIR:-/tmp}/bee-build-log.XXXXXX")"
    mkfifo "${BUILD_LOG_PIPE}"

    exec 3>&1 4>&2
    tee "${LOG_OUT}" < "${BUILD_LOG_PIPE}" &
    BUILD_TEE_PID=$!
    exec > "${BUILD_LOG_PIPE}" 2>&1
    BUILD_LOG_ACTIVE=1

    trap 'cleanup_build_log "$?"' EXIT INT TERM HUP

    echo "=== build log dir: ${LOG_DIR} ==="
    echo "=== build log: ${LOG_OUT} ==="
    echo "=== build log archive: ${LOG_ARCHIVE} ==="
}

cleanup_step_log() {
    status="${1:-$?}"

    if [ "${STEP_LOG_ACTIVE:-0}" = "1" ]; then
        STEP_LOG_ACTIVE=0
        exec 1>&5 2>&6
        exec 5>&- 6>&-
        if [ -n "${STEP_TEE_PID:-}" ]; then
            wait "${STEP_TEE_PID}" 2>/dev/null || true
        fi
        rm -f "${STEP_LOG_PIPE}"
    fi

    return "${status}"
}

run_step() {
    step_name="$1"
    step_slug="$2"
    shift 2

    step_log="${LOG_DIR}/${step_slug}.log"
    echo ""
    echo "=== step: ${step_name} ==="
    echo "=== step log: ${step_log} ==="

    STEP_LOG_PIPE="$(mktemp -u "${TMPDIR:-/tmp}/bee-step-log.XXXXXX")"
    mkfifo "${STEP_LOG_PIPE}"

    exec 5>&1 6>&2
    tee "${step_log}" < "${STEP_LOG_PIPE}" >&5 &
    STEP_TEE_PID=$!
    exec > "${STEP_LOG_PIPE}" 2>&1
    STEP_LOG_ACTIVE=1

    set +e
    "$@"
    step_status=$?
    set -e

    cleanup_step_log "${step_status}"
    if [ "${step_status}" -ne 0 ]; then
        echo "ERROR: step failed: ${step_name} (see ${step_log})" >&2
        exit "${step_status}"
    fi

    echo "=== step OK: ${step_name} ==="
}

run_step_sh() {
    step_name="$1"
    step_slug="$2"
    step_script="$3"

    run_step "${step_name}" "${step_slug}" sh -c "${step_script}"
}

run_optional_step_sh() {
    step_name="$1"
    step_slug="$2"
    step_script="$3"

    if [ "${BEE_REQUIRE_MEMTEST:-0}" = "1" ]; then
        run_step_sh "${step_name}" "${step_slug}" "${step_script}"
        return 0
    fi

    mkdir -p "${LOG_DIR}" 2>/dev/null || true
    step_log="${LOG_DIR}/${step_slug}.log"
    echo ""
    echo "=== optional step: ${step_name} ==="
    echo "=== optional step log: ${step_log} ==="
    set +e
    sh -c "${step_script}" > "${step_log}" 2>&1
    step_status=$?
    set -e
    cat "${step_log}"
    if [ "${step_status}" -ne 0 ]; then
        echo "WARNING: optional step failed: ${step_name} (see ${step_log})" >&2
    else
        echo "=== optional step OK: ${step_name} ==="
    fi
}

start_build_log

# Auto-detect kernel ABI: refresh apt index, then query current linux-image-amd64 dependency.
# If headers for the detected ABI are not yet installed (kernel updated since image build),
# install them on the fly so NVIDIA modules and ISO kernel always match.
if [ -z "${DEBIAN_KERNEL_ABI}" ] || [ "${DEBIAN_KERNEL_ABI}" = "auto" ]; then
    echo "=== refreshing apt index to detect current kernel ABI ==="
    apt-get update -qq || echo "WARNING: apt-get update failed, trying cached index"
    DEBIAN_KERNEL_ABI=$(apt-cache depends linux-image-amd64 2>/dev/null \
        | awk '/Depends:.*linux-image-[0-9]/{print $2}' \
        | grep -oE '[0-9]+\.[0-9]+\.[0-9]+-[0-9]+' \
        | head -1)
    if [ -z "${DEBIAN_KERNEL_ABI}" ]; then
        echo "ERROR: could not auto-detect kernel ABI from apt-cache" >&2
        echo "Hint: set DEBIAN_KERNEL_ABI=x.y.z-N in iso/builder/VERSIONS to skip auto-detection" >&2
        exit 1
    fi
    echo "=== kernel ABI: ${DEBIAN_KERNEL_ABI} ==="
fi

# Export detected ABI so that auto/config can pin the exact kernel package
# (prevents NVIDIA module/kernel mismatch if linux-image-amd64 meta-package
# gets updated between build.sh start and lb build chroot step)
export BEE_KERNEL_ABI="${DEBIAN_KERNEL_ABI}"

KVER="${DEBIAN_KERNEL_ABI}-amd64"
if [ ! -d "/usr/src/linux-headers-${KVER}" ]; then
    echo "=== installing linux-headers-${KVER} (kernel updated since image build) ==="
    apt-get install -y "linux-headers-${KVER}"
fi

echo "=== bee ISO build (variant: ${BUILD_VARIANT}) ==="
echo "Debian: ${DEBIAN_VERSION}, Kernel ABI: ${DEBIAN_KERNEL_ABI}, Go: ${GO_VERSION}"
echo "Audit version: ${AUDIT_VERSION_EFFECTIVE}, ISO version: ${ISO_VERSION_EFFECTIVE}"
echo ""

run_step "sync git submodules" "05-git-submodules" \
    git -C "${REPO_ROOT}" submodule update --init --recursive

# --- compile bee binary (static, Linux amd64) ---
# Shared between variants — built once, reused on second pass.
BEE_BIN="${DIST_DIR}/bee-linux-amd64"
NEED_BUILD=1
if [ -f "$BEE_BIN" ]; then
    NEWEST_SRC=$(find "${REPO_ROOT}/audit" -name '*.go' -newer "$BEE_BIN" | head -1)
    [ -z "$NEWEST_SRC" ] && NEED_BUILD=0
fi

if [ "$NEED_BUILD" = "1" ]; then
    run_step_sh "build bee binary" "10-build-bee" \
        "cd '${REPO_ROOT}/audit' && \
        env GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \
            go build \
            -ldflags '-s -w -X main.Version=${AUDIT_VERSION_EFFECTIVE}' \
            -o '${BEE_BIN}' \
            ./cmd/bee"
    echo "binary: $BEE_BIN"
    if command -v stat >/dev/null 2>&1; then
        BEE_SIZE_BYTES="$(stat -c '%s' "$BEE_BIN" 2>/dev/null || stat -f '%z' "$BEE_BIN")"
    else
        BEE_SIZE_BYTES="$(wc -c < "$BEE_BIN" | tr -d ' ')"
    fi
    if command -v numfmt >/dev/null 2>&1; then
        echo "size:   $(numfmt --to=iec --suffix=B "$BEE_SIZE_BYTES")"
    else
        echo "size:   ${BEE_SIZE_BYTES} bytes"
    fi
else
    echo "=== bee binary up to date, skipping build ==="
fi

# --- NVIDIA-only build steps ---
GPU_BURN_WORKER_BIN="${DIST_DIR}/bee-gpu-burn-worker-linux-amd64"
if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then
    run_step "download cuBLAS/cuBLASLt/cudart ${NCCL_CUDA_VERSION} userspace" "20-cublas" \
        sh "${BUILDER_DIR}/build-cublas.sh" \
        "${CUBLAS_VERSION}" \
        "${CUDA_USERSPACE_VERSION}" \
        "${NCCL_CUDA_VERSION}" \
        "${DIST_DIR}"

    CUBLAS_CACHE="${DIST_DIR}/cublas-${CUBLAS_VERSION}+cuda${NCCL_CUDA_VERSION}"

    echo "=== bee-gpu-burn FP4 header probe ==="
    fp4_type_match="$(grep -Rsnm 1 'CUDA_R_4F_E2M1' "${CUBLAS_CACHE}/include" 2>/dev/null || true)"
    fp4_scale_match="$(grep -Rsnm 1 'CUBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE4M3' "${CUBLAS_CACHE}/include" 2>/dev/null || true)"
    if [ -n "$fp4_type_match" ]; then
        echo "fp4_header_symbol=present"
        echo "$fp4_type_match"
    else
        echo "fp4_header_symbol=missing"
    fi
    if [ -n "$fp4_scale_match" ]; then
        echo "fp4_scale_mode_symbol=present"
        echo "$fp4_scale_match"
    else
        echo "fp4_scale_mode_symbol=missing"
    fi

    GPU_STRESS_NEED_BUILD=1
    if [ -f "$GPU_BURN_WORKER_BIN" ]; then
        GPU_STRESS_NEED_BUILD=0
        for dep in \
            "${BUILDER_DIR}/bee-gpu-stress.c" \
            "${BUILDER_DIR}/VERSIONS"; do
            if [ "$dep" -nt "$GPU_BURN_WORKER_BIN" ]; then
                GPU_STRESS_NEED_BUILD=1
                break
            fi
        done
        if [ "$GPU_STRESS_NEED_BUILD" = "0" ] && \
            find "${CUBLAS_CACHE}/include" "${CUBLAS_CACHE}/lib" -type f -newer "$GPU_BURN_WORKER_BIN" | grep -q .; then
            GPU_STRESS_NEED_BUILD=1
        fi
    fi

    if [ "$GPU_STRESS_NEED_BUILD" = "1" ]; then
        run_step "build bee-gpu-burn worker" "21-gpu-burn-worker" \
            gcc -O2 -s -Wall -Wextra \
            -I"${CUBLAS_CACHE}/include" \
            -o "$GPU_BURN_WORKER_BIN" \
            "${BUILDER_DIR}/bee-gpu-stress.c" \
            -ldl -lm
        echo "binary: $GPU_BURN_WORKER_BIN"
    else
        echo "=== bee-gpu-burn worker up to date, skipping build ==="
    fi
    echo "=== bee-gpu-burn compiled profile probe ==="
    if grep -aq 'fp4_e2m1' "$GPU_BURN_WORKER_BIN"; then
        echo "fp4_profile_string=present"
    else
        echo "fp4_profile_string=missing"
    fi
fi

echo "=== preparing staged overlay (${BUILD_VARIANT}) ==="
mkdir -p "${BUILD_WORK_DIR}" "${OVERLAY_STAGE_DIR}"

# Sync builder config into variant work dir, preserving lb cache.
rsync -a --delete \
    --exclude='cache/' \
    --exclude='chroot/' \
    --exclude='.build/' \
    --exclude='*.iso' \
    --exclude='*.packages' \
    --exclude='*.contents' \
    --exclude='*.files' \
    "${BUILDER_DIR}/" "${BUILD_WORK_DIR}/"

# Share deb package cache across variants.
# Restore: populate work dir cache from shared cache before build.
# Persist: sync back after build (done after lb build below).
LB_PKG_CACHE="${CACHE_ROOT}/lb-packages"
mkdir -p "${LB_PKG_CACHE}"
if [ -d "${BUILD_WORK_DIR}/cache/packages.chroot" ]; then
    rsync -a --delete "${BUILD_WORK_DIR}/cache/packages.chroot/" "${LB_PKG_CACHE}/"
elif [ -d "${LB_PKG_CACHE}" ] && [ "$(ls -A "${LB_PKG_CACHE}" 2>/dev/null)" ]; then
    mkdir -p "${BUILD_WORK_DIR}/cache/packages.chroot"
    rsync -a "${LB_PKG_CACHE}/" "${BUILD_WORK_DIR}/cache/packages.chroot/"
fi

if [ "$BEE_GPU_VENDOR" != "nvidia" ] || [ "$BEE_NVIDIA_MODULE_FLAVOR" != "proprietary" ]; then
    cat > "${BUILD_WORK_DIR}/config/bootloaders/grub-pc/grub.cfg" <<'EOF'
source /boot/grub/config.cfg

echo ""
echo "  ███████╗ █████╗ ███████╗██╗   ██╗      ██████╗ ███████╗███████╗"
echo "  ██╔════╝██╔══██╗██╔════╝╚██╗ ██╔╝      ██╔══██╗██╔════╝██╔════╝"
echo "  █████╗  ███████║███████╗ ╚████╔╝ █████╗██████╔╝█████╗  █████╗"
echo "  ██╔══╝  ██╔══██║╚════██║  ╚██╔╝  ╚════╝██╔══██╗██╔══╝  ██╔══╝"
echo "  ███████╗██║  ██║███████║   ██║         ██████╔╝███████╗███████╗"
echo "  ╚══════╝╚═╝  ╚═╝╚══════╝   ╚═╝         ╚═════╝ ╚══════╝╚══════╝"
echo "  Hardware Audit LiveCD"
echo ""

menuentry "EASY-BEE" {
    linux   @KERNEL_LIVE@ @APPEND_LIVE@ nomodeset net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable nowatchdog nosoftlockup
    initrd  @INITRD_LIVE@
}

submenu "EASY-BEE (advanced options) -->" {
    menuentry "EASY-BEE — KMS (no nomodeset)" {
        linux   @KERNEL_LIVE@ @APPEND_LIVE@ net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable nowatchdog nosoftlockup
        initrd  @INITRD_LIVE@
    }

    menuentry "EASY-BEE — fail-safe" {
        linux   @KERNEL_LIVE@ @APPEND_LIVE@ nomodeset noapic noapm nodma nomce nolapic nosmp vga=normal net.ifnames=0 biosdevname=0
        initrd  @INITRD_LIVE@
    }
}

if [ "${grub_platform}" = "efi" ]; then
    menuentry "Memory Test (memtest86+)" {
        chainloader /boot/memtest86+x64.efi
    }
else
    menuentry "Memory Test (memtest86+)" {
        linux16 /boot/memtest86+x64.bin
    }
fi

if [ "${grub_platform}" = "efi" ]; then
    menuentry "UEFI Firmware Settings" {
        fwsetup
    }
fi
EOF

    cat > "${BUILD_WORK_DIR}/config/bootloaders/isolinux/live.cfg.in" <<'EOF'
label live-@FLAVOUR@-normal
    menu label ^EASY-BEE
    menu default
    linux @LINUX@
    initrd @INITRD@
    append @APPEND_LIVE@

label live-@FLAVOUR@-kms
    menu label EASY-BEE (^graphics/KMS)
    linux @LINUX@
    initrd @INITRD@
    append @APPEND_LIVE@ bee.display=kms

label live-@FLAVOUR@-toram
    menu label EASY-BEE (^load to RAM)
    linux @LINUX@
    initrd @INITRD@
    append @APPEND_LIVE@ toram

label live-@FLAVOUR@-failsafe
    menu label EASY-BEE (^fail-safe)
    linux @LINUX@
    initrd @INITRD@
    append @APPEND_LIVE@ memtest noapic noapm nodma nomce nolapic nosmp vga=normal

label memtest
    menu label ^Memory Test (memtest86+)
    linux /boot/memtest86+x64.bin
EOF
fi

rsync -a "${OVERLAY_DIR}/" "${OVERLAY_STAGE_DIR}/"
rm -f \
    "${OVERLAY_STAGE_DIR}/etc/bee-ssh-password-fallback" \
    "${OVERLAY_STAGE_DIR}/etc/bee-release" \
    "${OVERLAY_STAGE_DIR}/root/.ssh/authorized_keys" \
    "${OVERLAY_STAGE_DIR}/usr/local/bin/bee" \
    "${OVERLAY_STAGE_DIR}/usr/local/bin/john" \
    "${OVERLAY_STAGE_DIR}/usr/local/lib/bee/bee-gpu-burn-worker" \
    "${OVERLAY_STAGE_DIR}/usr/local/bin/bee-smoketest" \
    "${OVERLAY_STAGE_DIR}/usr/local/bin/all_reduce_perf"
rm -rf \
    "${OVERLAY_STAGE_DIR}/usr/local/lib/bee/john"

# Remove NVIDIA-specific overlay files for non-nvidia variants
if [ "$BEE_GPU_VENDOR" != "nvidia" ]; then
    rm -f "${OVERLAY_STAGE_DIR}/usr/local/bin/bee-nvidia-load"
    rm -f "${OVERLAY_STAGE_DIR}/etc/systemd/system/bee-nvidia.service"
fi

# --- inject authorized_keys for SSH access ---
AUTHORIZED_KEYS_FILE="${OVERLAY_STAGE_DIR}/root/.ssh/authorized_keys"
mkdir -p "${OVERLAY_STAGE_DIR}/root/.ssh"

if [ -n "$AUTH_KEYS" ]; then
    cp "$AUTH_KEYS" "$AUTHORIZED_KEYS_FILE"
    chmod 600 "$AUTHORIZED_KEYS_FILE"
    echo "SSH authorized_keys: installed from $AUTH_KEYS"
else
    > "$AUTHORIZED_KEYS_FILE"
    FOUND=0
    for ssh_pub in "$HOME"/.keys/*.key.pub; do
        [ -f "$ssh_pub" ] || continue
        cat "$ssh_pub" >> "$AUTHORIZED_KEYS_FILE"
        echo "SSH: added $(basename "$ssh_pub" .key.pub)"
        FOUND=$((FOUND + 1))
    done
    if [ "$FOUND" -gt 0 ]; then
        chmod 600 "$AUTHORIZED_KEYS_FILE"
        echo "SSH authorized_keys: $FOUND key(s) from ~/.keys/*.key.pub"
    else
        echo "WARNING: no SSH public keys found — falling back to password auth"
        echo "  SSH login: bee / eeb"
        USE_PASSWORD_FALLBACK=1
    fi
fi

if [ "${USE_PASSWORD_FALLBACK:-0}" = "1" ]; then
    touch "${OVERLAY_STAGE_DIR}/etc/bee-ssh-password-fallback"
else
    rm -f "${OVERLAY_STAGE_DIR}/etc/bee-ssh-password-fallback"
fi

# --- copy bee binary into overlay ---
mkdir -p "${OVERLAY_STAGE_DIR}/usr/local/bin"
cp "${DIST_DIR}/bee-linux-amd64" "${OVERLAY_STAGE_DIR}/usr/local/bin/bee"
chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/bee"

if [ "$BEE_GPU_VENDOR" = "nvidia" ] && [ -f "$GPU_BURN_WORKER_BIN" ]; then
    mkdir -p "${OVERLAY_STAGE_DIR}/usr/local/lib/bee" "${OVERLAY_STAGE_DIR}/usr/local/bin"
    cp "${GPU_BURN_WORKER_BIN}" "${OVERLAY_STAGE_DIR}/usr/local/lib/bee/bee-gpu-burn-worker"
    chmod +x "${OVERLAY_STAGE_DIR}/usr/local/lib/bee/bee-gpu-burn-worker"
    chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/bee-gpu-burn" 2>/dev/null || true
    chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/bee-john-gpu-stress" 2>/dev/null || true
    chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/bee-nccl-gpu-stress" 2>/dev/null || true
fi

# --- inject smoketest into overlay so it runs directly on the live CD ---
cp "${BUILDER_DIR}/smoketest.sh" "${OVERLAY_STAGE_DIR}/usr/local/bin/bee-smoketest"
chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/bee-smoketest"

# --- vendor utilities (optional pre-fetched binaries) ---
for tool in storcli64 sas2ircu sas3ircu arcconf ssacli; do
    if [ -f "${VENDOR_DIR}/${tool}" ]; then
        cp "${VENDOR_DIR}/${tool}" "${OVERLAY_STAGE_DIR}/usr/local/bin/${tool}"
        chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/${tool}" || true
        echo "vendor tool: ${tool} (included)"
    else
        echo "vendor tool: ${tool} (not found, skipped)"
    fi
done

# --- NVIDIA kernel modules and userspace libs ---
if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then
    run_step "build NVIDIA ${NVIDIA_DRIVER_VERSION} modules" "40-nvidia-module" \
        sh "${BUILDER_DIR}/build-nvidia-module.sh" "${NVIDIA_DRIVER_VERSION}" "${DIST_DIR}" "${DEBIAN_KERNEL_ABI}" "${BEE_NVIDIA_MODULE_FLAVOR}"

    KVER="${DEBIAN_KERNEL_ABI}-amd64"
    NVIDIA_CACHE="${DIST_DIR}/nvidia-${BEE_NVIDIA_MODULE_FLAVOR}-${NVIDIA_DRIVER_VERSION}-${KVER}"

    # Inject .ko files into overlay at /usr/local/lib/nvidia/
    OVERLAY_KMOD_DIR="${OVERLAY_STAGE_DIR}/usr/local/lib/nvidia"
    mkdir -p "${OVERLAY_KMOD_DIR}"
    cp "${NVIDIA_CACHE}/modules/"*.ko "${OVERLAY_KMOD_DIR}/"

    # Inject nvidia-smi and libnvidia-ml
    mkdir -p "${OVERLAY_STAGE_DIR}/usr/local/bin" "${OVERLAY_STAGE_DIR}/usr/lib"
    cp "${NVIDIA_CACHE}/bin/nvidia-smi" "${OVERLAY_STAGE_DIR}/usr/local/bin/"
    chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/nvidia-smi"
    cp "${NVIDIA_CACHE}/bin/nvidia-bug-report.sh" "${OVERLAY_STAGE_DIR}/usr/local/bin/" 2>/dev/null || true
    chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/nvidia-bug-report.sh" 2>/dev/null || true
    cp "${NVIDIA_CACHE}/lib/"* "${OVERLAY_STAGE_DIR}/usr/lib/" 2>/dev/null || true
    mkdir -p "${OVERLAY_STAGE_DIR}/etc/OpenCL/vendors"
    printf 'libnvidia-opencl.so.1\n' > "${OVERLAY_STAGE_DIR}/etc/OpenCL/vendors/nvidia.icd"

    # Inject GSP firmware into /lib/firmware/nvidia/<version>/
    if [ -d "${NVIDIA_CACHE}/firmware" ] && [ "$(ls -A "${NVIDIA_CACHE}/firmware" 2>/dev/null)" ]; then
        mkdir -p "${OVERLAY_STAGE_DIR}/lib/firmware/nvidia/${NVIDIA_DRIVER_VERSION}"
        cp "${NVIDIA_CACHE}/firmware/"* "${OVERLAY_STAGE_DIR}/lib/firmware/nvidia/${NVIDIA_DRIVER_VERSION}/"
        echo "=== firmware: $(ls "${OVERLAY_STAGE_DIR}/lib/firmware/nvidia/${NVIDIA_DRIVER_VERSION}/" | wc -l) files injected ==="
    fi

    # --- build / download NCCL ---
    run_step "download NCCL ${NCCL_VERSION}+cuda${NCCL_CUDA_VERSION}" "50-nccl" \
        sh "${BUILDER_DIR}/build-nccl.sh" "${NCCL_VERSION}" "${NCCL_CUDA_VERSION}" "${DIST_DIR}" "${NCCL_SHA256:-}"

    NCCL_CACHE="${DIST_DIR}/nccl-${NCCL_VERSION}+cuda${NCCL_CUDA_VERSION}"

    # Inject libnccl.so.* into overlay alongside other NVIDIA userspace libs
    cp "${NCCL_CACHE}/lib/"* "${OVERLAY_STAGE_DIR}/usr/lib/"
    echo "=== NCCL: $(ls "${NCCL_CACHE}/lib/" | wc -l) files injected into /usr/lib/ ==="

    # Inject cuBLAS/cuBLASLt/cudart runtime libs used by the bee-gpu-burn worker tensor-core GEMM path
    cp "${CUBLAS_CACHE}/lib/"* "${OVERLAY_STAGE_DIR}/usr/lib/"
    echo "=== cuBLAS: $(ls "${CUBLAS_CACHE}/lib/" | wc -l) files injected into /usr/lib/ ==="

    # --- build nccl-tests ---
    run_step "build nccl-tests ${NCCL_TESTS_VERSION}" "60-nccl-tests" \
        sh "${BUILDER_DIR}/build-nccl-tests.sh" \
        "${NCCL_TESTS_VERSION}" \
        "${NCCL_VERSION}" \
        "${NCCL_CUDA_VERSION}" \
        "${DIST_DIR}" \
        "${NVCC_VERSION}" \
        "${DEBIAN_VERSION}"

    NCCL_TESTS_CACHE="${DIST_DIR}/nccl-tests-${NCCL_TESTS_VERSION}"
    cp "${NCCL_TESTS_CACHE}/bin/all_reduce_perf" "${OVERLAY_STAGE_DIR}/usr/local/bin/all_reduce_perf"
    chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/all_reduce_perf"
    cp "${NCCL_TESTS_CACHE}/lib/"* "${OVERLAY_STAGE_DIR}/usr/lib/" 2>/dev/null || true
    echo "=== all_reduce_perf injected ==="

    run_step "build john jumbo ${JOHN_JUMBO_COMMIT}" "70-john" \
        sh "${BUILDER_DIR}/build-john.sh" "${JOHN_JUMBO_COMMIT}" "${DIST_DIR}"
    JOHN_CACHE="${DIST_DIR}/john-${JOHN_JUMBO_COMMIT}"
    mkdir -p "${OVERLAY_STAGE_DIR}/usr/local/lib/bee/john"
    rsync -a --delete "${JOHN_CACHE}/run/" "${OVERLAY_STAGE_DIR}/usr/local/lib/bee/john/run/"
    ln -sfn ../lib/bee/john/run/john "${OVERLAY_STAGE_DIR}/usr/local/bin/john"
    chmod +x "${OVERLAY_STAGE_DIR}/usr/local/lib/bee/john/run/john"
    echo "=== john injected ==="
fi

# --- embed build metadata ---
mkdir -p "${OVERLAY_STAGE_DIR}/etc"
BUILD_DATE="$(date +%Y-%m-%d)"
GIT_COMMIT="$(git -C "${REPO_ROOT}" rev-parse --short HEAD 2>/dev/null || echo unknown)"

if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then
    GPU_VERSION_LINE="NVIDIA_DRIVER_VERSION=${NVIDIA_DRIVER_VERSION}
NVIDIA_KERNEL_MODULES_FLAVOR=${BEE_NVIDIA_MODULE_FLAVOR}
NCCL_VERSION=${NCCL_VERSION}
NCCL_CUDA_VERSION=${NCCL_CUDA_VERSION}
CUBLAS_VERSION=${CUBLAS_VERSION}
CUDA_USERSPACE_VERSION=${CUDA_USERSPACE_VERSION}
NCCL_TESTS_VERSION=${NCCL_TESTS_VERSION}
JOHN_JUMBO_COMMIT=${JOHN_JUMBO_COMMIT}"
    GPU_BUILD_INFO="nvidia-${BEE_NVIDIA_MODULE_FLAVOR}:${NVIDIA_DRIVER_VERSION}"
elif [ "$BEE_GPU_VENDOR" = "amd" ]; then
    GPU_VERSION_LINE="ROCM_VERSION=${ROCM_VERSION}"
    GPU_BUILD_INFO="rocm:${ROCM_VERSION}"
else
    GPU_VERSION_LINE=""
    GPU_BUILD_INFO="nogpu"
fi

cat > "${OVERLAY_STAGE_DIR}/etc/bee-release" <<EOF
BEE_ISO_VERSION=${ISO_VERSION_EFFECTIVE}
BEE_AUDIT_VERSION=${AUDIT_VERSION_EFFECTIVE}
BEE_BUILD_VARIANT=${BUILD_VARIANT}
BEE_GPU_VENDOR=${BEE_GPU_VENDOR}
BUILD_DATE=${BUILD_DATE}
GIT_COMMIT=${GIT_COMMIT}
DEBIAN_VERSION=${DEBIAN_VERSION}
DEBIAN_KERNEL_ABI=${DEBIAN_KERNEL_ABI}
${GPU_VERSION_LINE}
EOF

# Write GPU vendor marker for hooks
echo "${BEE_GPU_VENDOR}" > "${OVERLAY_STAGE_DIR}/etc/bee-gpu-vendor"
if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then
    echo "${BEE_NVIDIA_MODULE_FLAVOR}" > "${OVERLAY_STAGE_DIR}/etc/bee-nvidia-modules-flavor"
else
    rm -f "${OVERLAY_STAGE_DIR}/etc/bee-nvidia-modules-flavor"
fi

# Patch motd with build info
BEE_BUILD_INFO="${BUILD_DATE} git:${GIT_COMMIT} debian:${DEBIAN_VERSION} ${GPU_BUILD_INFO}"
if [ -f "${OVERLAY_STAGE_DIR}/etc/motd" ]; then
    sed "s/%%BUILD_INFO%%/${BEE_BUILD_INFO}/" "${OVERLAY_STAGE_DIR}/etc/motd" \
        > "${OVERLAY_STAGE_DIR}/etc/motd.patched"
    mv "${OVERLAY_STAGE_DIR}/etc/motd.patched" "${OVERLAY_STAGE_DIR}/etc/motd"
fi

# --- copy variant-specific package list, remove all other variant lists ---
# live-build picks up ALL .list.chroot files — delete other variants to avoid conflicts.
cp "${BUILD_WORK_DIR}/config/package-lists/bee-${BEE_GPU_VENDOR}.list.chroot" \
   "${BUILD_WORK_DIR}/config/package-lists/bee-gpu.list.chroot"
rm -f "${BUILD_WORK_DIR}/config/package-lists/bee-nvidia.list.chroot" \
      "${BUILD_WORK_DIR}/config/package-lists/bee-amd.list.chroot" \
      "${BUILD_WORK_DIR}/config/package-lists/bee-nogpu.list.chroot"

# --- remove archives for the other vendor(s) ---
if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then
    rm -f "${BUILD_WORK_DIR}/config/archives/rocm.list.chroot" \
          "${BUILD_WORK_DIR}/config/archives/rocm.key.chroot"
elif [ "$BEE_GPU_VENDOR" = "amd" ]; then
    rm -f "${BUILD_WORK_DIR}/config/archives/nvidia-cuda.list.chroot" \
          "${BUILD_WORK_DIR}/config/archives/nvidia-cuda.key.chroot"
else
    # nogpu: remove both
    rm -f "${BUILD_WORK_DIR}/config/archives/rocm.list.chroot" \
          "${BUILD_WORK_DIR}/config/archives/rocm.key.chroot" \
          "${BUILD_WORK_DIR}/config/archives/nvidia-cuda.list.chroot" \
          "${BUILD_WORK_DIR}/config/archives/nvidia-cuda.key.chroot"
fi

# --- substitute version placeholders in package list and archive ---
if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then
    sed -i \
        -e "s/%%NVIDIA_FABRICMANAGER_VERSION%%/${NVIDIA_FABRICMANAGER_VERSION}/g" \
        -e "s/%%DCGM_VERSION%%/${DCGM_VERSION}/g" \
        "${BUILD_WORK_DIR}/config/package-lists/bee-gpu.list.chroot"
elif [ "$BEE_GPU_VENDOR" = "amd" ]; then
    sed -i \
        -e "s/%%ROCM_VERSION%%/${ROCM_VERSION}/g" \
        -e "s/%%ROCM_SMI_VERSION%%/${ROCM_SMI_VERSION}/g" \
        -e "s/%%ROCM_BANDWIDTH_TEST_VERSION%%/${ROCM_BANDWIDTH_TEST_VERSION}/g" \
        -e "s/%%ROCM_VALIDATION_SUITE_VERSION%%/${ROCM_VALIDATION_SUITE_VERSION}/g" \
        -e "s/%%ROCBLAS_VERSION%%/${ROCBLAS_VERSION}/g" \
        -e "s/%%ROCRAND_VERSION%%/${ROCRAND_VERSION}/g" \
        -e "s/%%HIP_RUNTIME_AMD_VERSION%%/${HIP_RUNTIME_AMD_VERSION}/g" \
        -e "s/%%HIPBLASLT_VERSION%%/${HIPBLASLT_VERSION}/g" \
        -e "s/%%COMGR_VERSION%%/${COMGR_VERSION}/g" \
        "${BUILD_WORK_DIR}/config/package-lists/bee-gpu.list.chroot"
    if [ -f "${BUILD_WORK_DIR}/config/archives/rocm.list.chroot" ]; then
        sed -i \
            -e "s/%%ROCM_VERSION%%/${ROCM_VERSION}/g" \
            "${BUILD_WORK_DIR}/config/archives/rocm.list.chroot"
    fi
fi

# --- sync overlay into live-build includes.chroot ---
LB_DIR="${BUILD_WORK_DIR}"
LB_INCLUDES="${LB_DIR}/config/includes.chroot"
mkdir -p "${LB_INCLUDES}"
rsync -a "${OVERLAY_STAGE_DIR}/" "${LB_INCLUDES}/"

# Ensure SSH authorized_keys perms are correct (rsync may alter)
if [ -f "${LB_INCLUDES}/root/.ssh/authorized_keys" ]; then
    chmod 700 "${LB_INCLUDES}/root/.ssh"
    chmod 600 "${LB_INCLUDES}/root/.ssh/authorized_keys"
fi

# --- build ISO using live-build ---
echo ""
echo "=== building ISO (variant: ${BUILD_VARIANT}) ==="

# Export for auto/config
BEE_GPU_VENDOR_UPPER="$(echo "${BUILD_VARIANT}" | tr 'a-z-' 'A-Z_')"
export BEE_GPU_VENDOR_UPPER

cd "${LB_DIR}"
run_step_sh "live-build clean" "80-lb-clean" "lb clean --all 2>&1 | tail -3"
run_step_sh "live-build config" "81-lb-config" "lb config 2>&1 | tail -5"
dump_memtest_debug "pre-build" "${LB_DIR}"
run_step_sh "live-build build" "90-lb-build" "lb build 2>&1"

# --- persist deb package cache back to shared location ---
# This allows the second variant to reuse all downloaded packages.
if [ -d "${BUILD_WORK_DIR}/cache/packages.chroot" ]; then
    rsync -a "${BUILD_WORK_DIR}/cache/packages.chroot/" "${LB_PKG_CACHE}/"
    echo "=== package cache synced to ${LB_PKG_CACHE} ==="
fi

# live-build outputs live-image-amd64.hybrid.iso in LB_DIR
ISO_RAW="${LB_DIR}/live-image-amd64.hybrid.iso"
if [ -f "$ISO_RAW" ]; then
    dump_memtest_debug "post-build" "${LB_DIR}" "$ISO_RAW"
    if iso_memtest_present "$ISO_RAW"; then
        :
    else
        memtest_status=$?
        if [ "$memtest_status" -eq 1 ]; then
            recover_iso_memtest "${LB_DIR}" "$ISO_RAW"
            dump_memtest_debug "post-recovery" "${LB_DIR}" "$ISO_RAW"
        elif [ "$memtest_status" -eq 2 ]; then
            memtest_fail "failed to inspect ISO for memtest before recovery" "$ISO_RAW"
        fi
    fi
    validate_iso_memtest "$ISO_RAW"
    validate_iso_nvidia_runtime "$ISO_RAW"
    cp "$ISO_RAW" "$ISO_OUT"
    echo ""
    echo "=== done (${BUILD_VARIANT}) ==="
    echo "ISO: $ISO_OUT"
    if command -v stat >/dev/null 2>&1; then
        ISO_SIZE_BYTES="$(stat -c '%s' "$ISO_OUT" 2>/dev/null || stat -f '%z' "$ISO_OUT")"
    else
        ISO_SIZE_BYTES="$(wc -c < "$ISO_OUT" | tr -d ' ')"
    fi
    if command -v numfmt >/dev/null 2>&1; then
        echo "Size: $(numfmt --to=iec --suffix=B "$ISO_SIZE_BYTES")"
    else
        echo "Size: ${ISO_SIZE_BYTES} bytes"
    fi
else
    echo "ERROR: ISO not found at $ISO_RAW"
    exit 1
fi

echo ""
echo "Boot via BMC virtual media and SSH to the server IP on port 22 as root."
