#!/usr/bin/env bash
# scripts/opnsense-prep-image.sh <version> <output-qcow2-path> [<growth, e.g. +8G>]
#
# Downloads the OPNsense nano image (pre-installed, serial-console-ready --
# see opentofu/README.md's "OPNsense deployment research" section for why
# nano, not the vga/serial/dvd installer images), decompresses it, converts
# raw -> qcow2, and grows it. Output: a ready qcow2 file at
# <output-qcow2-path> -- feed that LOCAL PATH as modules/base-image's
# source_url (create.content.url accepts local paths, confirmed via
# examples/alpine_cloudinit.tf's libvirt_cloudinit_disk.path usage pattern).
#
# `libvirt_volume`'s create.content.url almost certainly does a plain fetch
# with no decompression/format-conversion -- this script does that work
# OUTSIDE OpenTofu, once, before `tofu apply` ever sees the result.
#
# Requires: curl or wget, bunzip2, qemu-img. Read-only w.r.t. the repo;
# writes only to <output-qcow2-path> and a self-cleaning temp dir.
# Exit: 0 prepared | 1 bad input | 2 required tool missing.
set -euo pipefail
# NO ${BASH_SOURCE[0]} here: this script is also run PIPED (`ssh host 'bash -s' -- ... < script`),
# where BASH_SOURCE is unset and `set -u` kills it before the first guard (measured, dc1 2026-07-22).
# It needs no self-location anyway -- everything is argument- or env-driven.

VERSION="${1:?usage: opnsense-prep-image.sh <version, e.g. 26.1> <output-qcow2-path> [<growth, e.g. +8G>]}"
OUT="${2:?usage: opnsense-prep-image.sh <version> <output-qcow2-path> [<growth>]}"
GROW="${3:-+8G}"

# MIRROR: no single canonical URL is hardcoded here -- OPNsense publishes
# multiple mirrors (see https://opnsense.org/download/) and this repo's own
# "never use an inferred value" discipline (hard rule 2) applies just as
# much to a download source as to a CIDR or a hostname. Defaulting silently
# to one specific mirror risks it going stale/unavailable without anyone
# noticing until a real deploy fails partway through.
MIRROR_BASE="${OPNSENSE_MIRROR_BASE:?set OPNSENSE_MIRROR_BASE to a real OPNsense mirror base URL (see https://opnsense.org/download/) -- not hardcoded here, mirrors change}"

command -v qemu-img >/dev/null 2>&1 || { echo "FAIL: qemu-img required on PATH"; exit 2; }
# bz2 decompressor: prefer bunzip2, else python3's stdlib bz2 module. python3 is
# a base prereq on this platform, so a missing bzip2 package is NOT a blocker
# (DOCFIX-184 -- first real run hit an absent bzip2).
if command -v bunzip2 >/dev/null 2>&1; then
  BZ2_DECOMP=bunzip2
elif command -v python3 >/dev/null 2>&1; then
  BZ2_DECOMP=python3
else
  echo "FAIL: need bunzip2 or python3 to decompress the .bz2 image"; exit 2
fi
if command -v curl >/dev/null 2>&1; then
  FETCH=(curl -fSL -o)
elif command -v wget >/dev/null 2>&1; then
  FETCH=(wget -O)
else
  echo "FAIL: curl or wget required on PATH"
  exit 2
fi

TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
IMG_BZ2="$TMP/OPNsense-${VERSION}-nano-amd64.img.bz2"
URL="${MIRROR_BASE%/}/releases/${VERSION}/OPNsense-${VERSION}-nano-amd64.img.bz2"

echo "== downloading: $URL =="
"${FETCH[@]}" "$IMG_BZ2" "$URL"

echo "== decompressing (via $BZ2_DECOMP) =="
IMG_RAW="${IMG_BZ2%.bz2}"
if [ "$BZ2_DECOMP" = "bunzip2" ]; then
  bunzip2 -k "$IMG_BZ2"
else
  python3 -c "import bz2,shutil,sys; shutil.copyfileobj(bz2.open(sys.argv[1],'rb'), open(sys.argv[2],'wb'))" "$IMG_BZ2" "$IMG_RAW"
fi

echo "== converting raw -> qcow2 =="
qemu-img convert -f raw -O qcow2 "$IMG_RAW" "$OUT"

echo "== growing by $GROW =="
qemu-img resize "$OUT" "$GROW"

echo "PASS: prepared $OUT"
