#!/bin/sh

set -eux

INCUS_ARCHITECTURE="$1"
RELEASE="$2"
IFS=+ read -r VARIANT FS <<EOF
$3
EOF
WORKSPACE="$4"
MIRROR="$5"

NOW="$(date +%s)"
SERIAL="$(date -u +%Y%m%d_%H:%M)"
echo "$SERIAL" > "$WORKSPACE/serial"

SHORT_ARCHITECTURE="$INCUS_ARCHITECTURE"
EXPANDED_ARCHITECTURE="$INCUS_ARCHITECTURE"
if [ "$SHORT_ARCHITECTURE" = "arm64" ]; then
    SHORT_ARCHITECTURE="aarch64"
    EXPANDED_ARCHITECTURE="arm64-aarch64"
elif [ "$SHORT_ARCHITECTURE" = "riscv64" ]; then
    EXPANDED_ARCHITECTURE="riscv-riscv64"
fi

BASE_URL="$MIRROR/releases/VM-IMAGES/$RELEASE-RELEASE/$SHORT_ARCHITECTURE/Latest"

if [ "$VARIANT" = "cloud" ]; then
    IMAGE="FreeBSD-$RELEASE-RELEASE-$EXPANDED_ARCHITECTURE-BASIC-CLOUDINIT-$FS.raw.xz"
else
    IMAGE="FreeBSD-$RELEASE-RELEASE-$EXPANDED_ARCHITECTURE-$FS.raw.xz"
fi

# Download a pre-built VM image
curl -Lo "$WORKSPACE/$IMAGE" "$BASE_URL/$IMAGE"
curl -Lo "$WORKSPACE/CHECKSUM.SHA256" "$BASE_URL/CHECKSUM.SHA256"
(cd "$WORKSPACE" && sha256sum -c CHECKSUM.SHA256 --ignore-missing)

# Decompress the image
unxz -c "$WORKSPACE/$IMAGE" > "$WORKSPACE/image.raw"
OFFSET="$(fdisk -lo Start "$WORKSPACE/image.raw" | tail -n1)"

if [ "$FS" = "zfs" ]; then
    # Mount the ZFS partition
    DEV="$(losetup -fPo "$((OFFSET*512))" --show "$WORKSPACE/image.raw")"
    ROOTFS="$WORKSPACE/rootfs"
    zpool import -d "$DEV" -R "$ROOTFS" zroot
    zfs mount zroot/ROOT/default
    RUN=""
else
    # Mount the UFS partition from within FreeBSD
    FREEBSD="$(mktemp -u freebsd-build-XXXXXX)"
    incus launch images:freebsd/15.0 "$FREEBSD" --vm --config security.secureboot=false --device root,size=16GiB
    incus wait "$FREEBSD" agent
    incus file push "$WORKSPACE/image.raw" "$FREEBSD/root"
    RUN="incus exec $FREEBSD -T --"
    DEV=$($RUN mdconfig -at vnode -f image.raw)
    $RUN gnop create -o "$((OFFSET*512))" "$DEV"
    ROOTFS=/mnt
    $RUN mount "/dev/$DEV.nop" "$ROOTFS"
fi

RUNSH="$RUN sh -c"

# Prepare templates
mkdir -p "$WORKSPACE/incus/templates"
echo 'hostname="{{ instance.name }}"' > "$WORKSPACE/incus/templates/hostname.tpl"
cat > "$WORKSPACE/incus/templates/hosts.tpl" << 'EOF'
127.0.1.1	{{ container.name }}
127.0.0.1	localhost
::1		localhost ip6-localhost ip6-loopback
ff02::1		ip6-allnodes
ff02::2		ip6-allrouters
EOF

# Generate metadata
cat > "$WORKSPACE/incus/metadata.yaml" << EOF
architecture: $INCUS_ARCHITECTURE
creation_date: $NOW
expiry_date: $((NOW+2592000))
properties:
  architecture: $INCUS_ARCHITECTURE
  description: FreeBSD $RELEASE $INCUS_ARCHITECTURE ($SERIAL)
  name: freebsd-$RELEASE-$INCUS_ARCHITECTURE-$VARIANT-$SERIAL
  os: freebsd
  release: $RELEASE
  serial: "$SERIAL"
  variant: $VARIANT
templates:
  /etc/rc.conf.d/hostname:
    when:
    - create
    - copy
    create_only: false
    template: hostname.tpl
    properties: {}
  /etc/hosts:
    when:
    - create
    - copy
    create_only: false
    template: hosts.tpl
    properties: {}
EOF

# Build the Incus TAR
tar -cJf "$WORKSPACE/incus.tar.xz" -C "$WORKSPACE/incus" .

# Prepare the agent
$RUNSH "echo 'incus_agent_enable=\"YES\"' >> $ROOTFS/etc/rc.conf"
$RUN mkdir -p "$ROOTFS/usr/local/etc/rc.d"
$RUNSH "cat > $ROOTFS/usr/local/etc/rc.d/incus-agent" << 'EOF'
#!/bin/sh
#
# PROVIDE: incus_agent
# REQUIRE: FILESYSTEMS
#

. /etc/rc.subr

name=incus_agent
rcvar=incus_agent_enable
pidfile="/var/run/${name}.pid"
procname=/usr/sbin/daemon

start_precmd="${name}_prestart"
start_cmd="${name}_start"

load_rc_config "$name"
: ${incus_agent_enable:="NO"}

incus_agent_prestart()
{
        /usr/local/libexec/incus-agent-setup
}

incus_agent_start()
{
        cd /var/run/incus_agent
        /usr/sbin/daemon -P "${pidfile}" -r -f /var/run/incus_agent/incus-agent
}

run_rc_command "$1"
EOF
$RUN chmod 0500 "$ROOTFS/usr/local/etc/rc.d/incus-agent"
$RUN mkdir "$ROOTFS/usr/local/libexec"
$RUNSH "cat > $ROOTFS/usr/local/libexec/incus-agent-setup" << 'EOF'
#!/bin/sh
set -eu
PREFIX="/var/run/incus_agent"

# Functions.
mount_9p() {
    kldload virtio_p9fs >/dev/null 2>&1 || true
    mount -t p9fs -o ro config "$PREFIX.mnt" >/dev/null 2>&1
}

fail() {
    # Check if we already have an agent in place.
    if [ -x "$PREFIX/incus-agent" ]; then
        echo "$1, reusing existing agent"
        exit 0
    fi

    # Cleanup and fail.
    umount "$PREFIX" >/dev/null 2>&1 || true
    rmdir "$PREFIX" >/dev/null 2>&1 || true
    echo "$1, failing"

    exit 1
}

# Try getting an agent drive.
mkdir -p "$PREFIX.mnt"
mount_9p || fail "Couldn't mount 9p"

# Setup the mount target.
umount "$PREFIX" >/dev/null 2>&1 || true
mkdir -p "$PREFIX"
mount -t tmpfs -o mode=0700,size=50M tmpfs "$PREFIX"

# Copy the data.
cp -Ra "$PREFIX.mnt/"* "$PREFIX"

# Unmount the temporary mount.
umount "$PREFIX.mnt"
rmdir "$PREFIX.mnt"

# Fix up permissions.
chown -R root:wheel "$PREFIX"

# Load pty
kldload pty >/dev/null 2>&1 || true

exit 0
EOF
$RUN chmod 0500 "$ROOTFS/usr/local/libexec/incus-agent-setup"

# Unmount and cleanup
if [ "$FS" = "zfs" ]; then
    zfs unmount zroot/ROOT/default
    zpool export zroot
    losetup -d "$DEV"
else
    $RUN umount /mnt
    $RUN gnop destroy "$DEV.nop"
    $RUN mdconfig -du "$DEV"
    incus file pull "$FREEBSD/root/image.raw" "$WORKSPACE/image.raw"
    incus rm -f "$FREEBSD"
fi
qemu-img convert -f raw -O qcow2 "$WORKSPACE/image.raw" "$WORKSPACE/disk.qcow2"
