#!/bin/bash
# Unattended install of an Omarchy ISO into a qcow2, through a cidata drive.
#
#   vm-install <omarchy.iso> <out_dir>
#
# out_dir gets base.qcow2 (the installed disk, powered off), OVMF_VARS.4m.fd
# and the key that can log in to it. vm-seal turns that into a reusable image.
# Same answer files and VM shape as omarchy-iso's test/integration.d/base-test.sh.
set -uo pipefail
[[ $# -eq 2 ]] || { echo "Usage: vm-install <omarchy.iso> <out_dir>" >&2; exit 2; }
ISO=$(realpath "$1"); OUT=$(realpath -m "$2"); W=$OUT/work
# Arch and Ubuntu keep the firmware in different places.
for fw in /usr/share/edk2/x64/OVMF_CODE.4m.fd:/usr/share/edk2/x64/OVMF_VARS.4m.fd /usr/share/OVMF/OVMF_CODE_4M.fd:/usr/share/OVMF/OVMF_VARS_4M.fd; do
  [[ -f ${fw%%:*} && -f ${fw##*:} ]] && { FW_CODE=${fw%%:*}; FW_VARS=${fw##*:}; break; }
done
: "${FW_CODE:?no OVMF firmware found}"
[[ -r /dev/kvm && -w /dev/kvm ]] || { echo "vm-install: /dev/kvm is not usable" >&2; exit 1; }
GUEST_USER=omarchy; GUEST_PASSWORD=omarchy; GUEST_HOSTNAME=omarchy-vm; SSH_PORT=2422
rm -rf "$W"; mkdir -p "$W/cidata" "$W/shots"; t0=$(date +%s); el() { echo $(( $(date +%s) - t0 )); }
ssh-keygen -q -t ed25519 -N '' -C omarchy-vm-build -f "$W/id_ed25519"
hash=$(openssl passwd -6 "$GUEST_PASSWORD")
disk_bytes=$((40*1024**3)); mib=$((1024**2)); boot_start=$mib; boot_size=$((2*1024**3)); main_start=$((boot_size+boot_start)); main_size=$((disk_bytes-main_start-mib))
jq -n --arg h "$hash" --arg u "$GUEST_USER" '{root_enc_password:$h, users:[{enc_password:$h, groups:[], sudo:true, username:$u}]}' > "$W/cidata/user_credentials.json"
cat > "$W/cidata/user_configuration.json" <<EOF
{
    "app_config": null, "archinstall-language": "English", "auth_config": {},
    "audio_config": { "audio": "pipewire" },
    "bootloader_config": { "bootloader": "Limine", "uki": false, "removable": false },
    "custom_commands": [],
    "omarchy_install": { "mode": "full_disk", "defer_provisioning": false, "target_mount": "/mnt",
        "boot": { "esp_mount": "/boot", "esp_path": "/EFI/limine", "efi_binary": "limine_x64.efi", "enable_fallback": true },
        "storage": { "kernel": "linux-omarchy" } },
    "disk_config": { "config_type": "default_layout", "device_modifications": [ { "device": "/dev/vda", "partitions": [
        { "btrfs": [], "dev_path": null, "flags": [ "boot", "esp" ], "fs_type": "fat32", "mount_options": [], "mountpoint": "/boot",
          "obj_id": "ea21d3f2-82bb-49cc-ab5d-6f81ae94e18d",
          "size": { "sector_size": { "unit": "B", "value": 512 }, "unit": "B", "value": $boot_size },
          "start": { "sector_size": { "unit": "B", "value": 512 }, "unit": "B", "value": $boot_start }, "status": "create", "type": "primary" },
        { "btrfs": [ { "mountpoint": "/", "name": "@" }, { "mountpoint": "/home", "name": "@home" }, { "mountpoint": "/var/log", "name": "@log" }, { "mountpoint": "/var/cache/pacman/pkg", "name": "@pkg" } ],
          "dev_path": null, "flags": [], "fs_type": "btrfs", "mount_options": [ "compress=zstd" ], "mountpoint": null,
          "obj_id": "8c2c2b92-1070-455d-b76a-56263bab24aa",
          "size": { "sector_size": { "unit": "B", "value": 512 }, "unit": "B", "value": $main_size },
          "start": { "sector_size": { "unit": "B", "value": 512 }, "unit": "B", "value": $main_start }, "status": "create", "type": "primary" } ],
        "wipe": true } ] },
    "hostname": "$GUEST_HOSTNAME", "kernels": [ "linux-omarchy" ], "network_config": { "type": "iso" }, "ntp": true,
    "parallel_downloads": 8, "script": null, "services": [], "swap": true, "timezone": "UTC",
    "locale_config": { "kb_layout": "us", "sys_enc": "UTF-8", "sys_lang": "en_US.UTF-8" },
    "mirror_config": { "custom_repositories": [], "custom_servers": [
        {"url": "https://mirror.omarchy.org/\$repo/os/\$arch"}, {"url": "https://mirror.rackspace.com/archlinux/\$repo/os/\$arch"}, {"url": "https://geo.mirror.pkgbuild.com/\$repo/os/\$arch"} ],
        "mirror_regions": {}, "optional_repositories": [] },
    "packages": [ "base-devel", "git", "omarchy-keyring", "omarchy-settings", "omarchy" ],
    "profile_config": { "gfx_driver": null, "greeter": null, "profile": {} }, "version": "3.0.9"
}
EOF
jq -e . "$W/cidata/user_configuration.json" >/dev/null || { echo "bad json"; exit 1; }
echo "Omarchy Test" > "$W/cidata/user_full_name.txt"; echo "test@omarchy.org" > "$W/cidata/user_email_address.txt"; echo false > "$W/cidata/user_encrypt_installation.txt"
cp "$W/id_ed25519.pub" "$W/cidata/authorized_keys"
truncate -s 4M "$W/cidata.img"; mkfs.vfat -n CIDATA "$W/cidata.img" >/dev/null; mcopy -i "$W/cidata.img" "$W"/cidata/* ::/
qemu-img create -f qcow2 "$W/disk.qcow2" 40G >/dev/null; cp "$FW_VARS" "$W/OVMF_VARS.4m.fd"
qemu-system-x86_64 -cpu host -enable-kvm -machine q35,accel=kvm -smp 8 -m 8G \
  -drive if=pflash,format=raw,readonly=on,file="$FW_CODE" -drive if=pflash,format=raw,file="$W/OVMF_VARS.4m.fd" \
  -drive file="$W/disk.qcow2",format=qcow2,if=none,id=drive0 -device virtio-blk-pci,drive=drive0,bootindex=1 \
  -device virtio-vga -display none -usb -device usb-tablet \
  -netdev user,id=net0,hostfwd=tcp:127.0.0.1:$SSH_PORT-:22 -device virtio-net-pci,netdev=net0 \
  -qmp "unix:$W/qmp.sock,server,nowait" -serial "file:$W/serial.log" -pidfile "$W/qemu.pid" -daemonize \
  -drive "file=$ISO,media=cdrom,if=none,format=raw,id=cdrom0,readonly=on" -device ide-cd,drive=cdrom0,bootindex=2 \
  -drive "file=$W/cidata.img,format=raw,if=none,id=cidata" -device usb-storage,drive=cidata || exit 1
qmp() { printf '{"execute":"qmp_capabilities"}\n%s\n' "$1" | socat - "unix-connect:$W/qmp.sock" >/dev/null 2>&1; }
shot() { qmp "{\"execute\":\"screendump\",\"arguments\":{\"filename\":\"$W/shots/$1.png\",\"format\":\"png\"}}"; }
sshg() { ssh -i "$W/id_ed25519" -p $SSH_PORT -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=3 -o LogLevel=ERROR $GUEST_USER@127.0.0.1 "$@"; }
echo "install started"; n=0
while :; do
  sshg true 2>/dev/null && { echo "ssh_up $(el)s"; break; }
  kill -0 "$(cat "$W/qemu.pid")" 2>/dev/null || { echo "qemu exited at $(el)s"; exit 1; }
  (( $(el) > 2400 )) && { shot timeout; echo "timeout"; exit 1; }
  n=$((n+1)); (( n % 6 == 0 )) && { shot "$(printf 'p-%04d' "$(el)")"; sleep 1
    txt=$(tesseract "$W/shots/$(printf 'p-%04d' "$(el)")".png - 2>/dev/null || true)
    grep -qi "Reboot Now" <<<"$txt" && { echo "reboot prompt $(el)s"; qmp '{"execute":"send-key","arguments":{"keys":[{"type":"qcode","data":"ret"}]}}'; }
    grep -qi "installation stopped" <<<"$txt" && { echo "install stopped $(el)s"; exit 1; }
    echo "... $(el)s"; }
  sleep 10
done
sshg 'timeout 180 systemctl is-system-running --wait; systemd-analyze; cat /etc/os-release | head -3; pacman -Q omarchy linux-omarchy 2>&1; cat ~/.local/share/omarchy/version 2>/dev/null' > "$W/first-boot.txt" 2>&1
sshg 'sudo -n cat /var/log/omarchy-install-timing.json' > "$W/install-timing.json" 2>/dev/null
shot first-boot; echo "first_boot_checked $(el)s"; cat "$W/first-boot.txt"
# Power off cleanly, so the disk is consistent, and leave the three files vm-seal reads.
printf '%s\n' "$GUEST_PASSWORD" | sshg "sudo -S -p '' systemctl poweroff" 2>/dev/null || true
pid=$(cat "$W/qemu.pid"); for i in $(seq 1 120); do kill -0 "$pid" 2>/dev/null || break; sleep 1; done
kill -0 "$pid" 2>/dev/null && { echo "vm-install: the VM did not power off" >&2; exit 1; }
mv "$W/disk.qcow2" "$OUT/base.qcow2"; cp "$W/OVMF_VARS.4m.fd" "$W/id_ed25519" "$W/id_ed25519.pub" "$OUT/"
echo "installed: $OUT/base.qcow2 ($(du -sh "$OUT/base.qcow2" | cut -f1)) in $(el)s"
