#!/usr/bin/env bash
#
# Build the golden macOS-guest base image the MacVMEngine clones per session.
#
#   scripts/build-macos-base.sh [/path/to/UniversalMac.ipsw]
#
# This is the HOST-side orchestration for the one-time "install + provision locally" base flow.
# Nucleic boots each agent session in a macOS VM (Apple's Virtualization.framework) by cloning a
# pristine "golden base" bundle, then SSHing into it as `agent@<ip>`. This script installs a CLEAN
# macOS into that base and then hands you the manual/provisioning steps to make it golden.
#
#   HEADS UP — this is a ~14 GB, multi-step, PARTLY MANUAL, one-time process.
#   Apple provides NO unattended macOS-guest install outside MDM/DEP, so the operator MUST complete
#   Setup Assistant once, by hand, in the VM's window (create the `agent` account, enable Remote
#   Login), before the in-guest provisioning script can run. Don't expect this to be push-button.
#
# The flow, end to end:
#   1. Build + ad-hoc-codesign the `macvm-spike` helper (it needs the virtualization entitlement,
#      exactly like the app — see signing/spike.entitlements).                     [automated, here]
#   2. Run the spike with NUCLEIC_MACVM_BUILD_BASE=1 to install a clean macOS into the base bundle
#      at ~/Library/Application Support/Nucleic/macvms/base/ (HardwareModel, MachineIdentifier,
#      AuxiliaryStorage, Disk.img). Downloads ~14 GB on first use unless you pass a local .ipsw.
#                                                                                  [automated, here]
#   3. Manually finish the guest: open the base VM in a window, complete Setup Assistant creating an
#      account named `agent`, enable Remote Login, then run scripts/provision-macos-guest.sh INSIDE
#      the guest to install the toolchain + authorize Nucleic's SSH key, and shut it down clean.
#                                                                        [MANUAL — instructions only]
#
# After step 3 the base is "golden": MacVMEngine.ensureRunning() will clone it and SSH straight in.
# See docs/MACOS_VM.md for the full picture, screenshots, and the GUI helper for step 3.

set -euo pipefail

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"

PRODUCT="macvm-spike"
ENTITLEMENTS="$ROOT/signing/spike.entitlements"

# The base bundle + host SSH key paths mirror MacVMEngine's on-disk layout exactly
# (defaultStorageRoot() → ~/Library/Application Support/Nucleic/macvms). Keep these in sync with
# Sources/NucleicCore/MacVM/MacVMEngine.swift if that ever moves.
STORAGE_ROOT="$HOME/Library/Application Support/Nucleic/macvms"
BASE_DIR="$STORAGE_ROOT/base"
PUBKEY="$STORAGE_ROOT/ssh/id_ed25519.pub"

# Optional local restore image: first positional arg wins, else $NUCLEIC_MACVM_IPSW, else the spike
# fetches the latest supported image over the network (~14 GB).
IPSW="${1:-${NUCLEIC_MACVM_IPSW:-}}"

# ── Preflight ────────────────────────────────────────────────────────────────────────────────────
# Virtualization of a macOS guest is Apple-silicon only; fail early rather than deep inside the spike.
if [ "$(uname -s)" != "Darwin" ] || [ "$(uname -m)" != "arm64" ]; then
  echo "✗ macOS-guest VMs require an Apple-silicon Mac (arm64). This host is $(uname -s)/$(uname -m)." >&2
  exit 1
fi
command -v swift    >/dev/null || { echo "✗ swift not found (install Xcode / command line tools)." >&2; exit 1; }
command -v codesign >/dev/null || { echo "✗ codesign not found (install Xcode command line tools)." >&2; exit 1; }
[ -f "$ENTITLEMENTS" ] || { echo "✗ missing $ENTITLEMENTS" >&2; exit 1; }
if [ -n "$IPSW" ] && [ ! -f "$IPSW" ]; then
  echo "✗ restore image not found: $IPSW" >&2
  exit 1
fi

# ── Step 1: build + ad-hoc-codesign the spike ────────────────────────────────────────────────────
# Ad-hoc signing (`--sign -`) is enough for com.apple.security.virtualization; a Developer ID is NOT
# required to run VMs locally. --force re-signs an existing binary so this stays idempotent.
# Per-mount scratch dir (empty on host, --scratch-path redirect on the /Volumes VM share)
# so host and VM builds don't share/poison one .build/. Same flag on the build and the
# --show-bin-path query. See scripts/lib/build-scratch.sh and BUILD.md.
SCRATCH_ARGS="$(bash "$ROOT/scripts/lib/build-scratch.sh" "$ROOT")"

echo "▸ [1/3] Building $PRODUCT …"
swift build $SCRATCH_ARGS --product "$PRODUCT"
BIN_DIR="$(swift build $SCRATCH_ARGS --product "$PRODUCT" --show-bin-path)"
BIN="$BIN_DIR/$PRODUCT"
[ -x "$BIN" ] || { echo "✗ built binary not found at $BIN" >&2; exit 1; }

echo "▸ Ad-hoc codesigning $PRODUCT with $(basename "$ENTITLEMENTS") …"
codesign --force --sign - --entitlements "$ENTITLEMENTS" "$BIN"
codesign --verify --verbose=1 "$BIN"

# ── Step 2: install a clean macOS into the base bundle ───────────────────────────────────────────
# Idempotency note: if a COMPLETE base bundle already exists we skip the multi-minute reinstall.
# (An interrupted install leaves base.building/, never a half-built base/ — see buildBaseImage.)
BASE_COMPLETE=1
for f in HardwareModel MachineIdentifier AuxiliaryStorage Disk.img; do
  [ -e "$BASE_DIR/$f" ] || BASE_COMPLETE=0
done
if [ "$BASE_COMPLETE" -eq 1 ]; then
  echo "▸ [2/3] Base bundle already installed at:"
  echo "      $BASE_DIR"
  echo "      (delete that directory to force a clean reinstall.) Skipping install."
else
  echo "▸ [2/3] Installing clean macOS into the base bundle — this is the ~14 GB / multi-minute step."
  if [ -n "$IPSW" ]; then
    echo "      using local restore image: $IPSW"
  else
    echo "      no .ipsw supplied — the spike will fetch the latest supported restore image (~14 GB)."
    echo "      (pass a local UniversalMac.ipsw as \$1 or set NUCLEIC_MACVM_IPSW to skip the download.)"
  fi
  NUCLEIC_MACVM_BUILD_BASE=1 ${IPSW:+NUCLEIC_MACVM_IPSW="$IPSW"} "$BIN"
  echo "✓ clean macOS installed into $BASE_DIR (guest now sits at Setup Assistant)."
fi

# ── Step 3: the manual + provisioning phase (instructions only) ──────────────────────────────────
# We CANNOT automate this: Apple has no unattended macOS-guest install/first-boot path outside
# MDM/DEP, so a human must click through Setup Assistant once, in a display session, to create the
# `agent` account. Everything after that (toolchain + SSH key) is provision-macos-guest.sh.
cat <<EOF

────────────────────────────────────────────────────────────────────────────────────────────────
✓ Base install done. NEXT STEPS ARE MANUAL — follow these to make the base "golden":

  A. Boot the base VM in a WINDOW so you can drive Setup Assistant.
     The installed guest sits at Setup Assistant, which needs a display — the headless spike/engine
     path can't show it. Use the GUI helper (Apple's VZVirtualMachineView over the SAME base
     bundle) described in docs/MACOS_VM.md. In the VM window:
        • Complete Setup Assistant.
        • Create an account whose SHORT NAME is exactly:   agent
          (this is MacVMSettings.defaultSSHUser — Nucleic logs in as agent@<ip>).
        • Skip Apple ID / analytics; you just need a local admin `agent` account.

  B. Get scripts/provision-macos-guest.sh + Nucleic's PUBLIC key into the guest and run it AS agent.
     Easiest: temporarily enable Remote Login in the VM (System Settings ▸ General ▸ Sharing ▸
     Remote Login), then from THIS host, scp the script + key in, or paste them via the window.

     Nucleic's host PUBLIC key to authorize for \`agent\` (its private half never leaves the host):
        $PUBKEY

     Inside the guest, as the agent user:
        chmod +x provision-macos-guest.sh
        ./provision-macos-guest.sh /path/to/id_ed25519.pub    # the key you copied in
     (It enables Remote Login, installs the toolchain, appends PATH to /etc/zshenv, authorizes the
     key, and finally shuts the guest down cleanly.)

  C. Once the guest has powered off, the base bundle at
        $BASE_DIR
     is golden and ready to clone. Verify with:  swift build --product $PRODUCT && \\
        codesign --force --sign - --entitlements signing/spike.entitlements "$BIN" && "$BIN"
     (default spike mode: clone → boot → ssh \`sw_vers\` → teardown; expect "SPIKE PASS").
────────────────────────────────────────────────────────────────────────────────────────────────
EOF

if [ ! -f "$PUBKEY" ]; then
  echo "⚠  Note: $PUBKEY does not exist yet — the spike generates the host keypair during step 2." >&2
  echo "   If step 2 was skipped, run the app once (or the spike) to create it, then re-read step B." >&2
fi
