#!/usr/bin/env bash
# Deploy a NixOS host from this flake. ALL arguments are mandatory (no defaults).
#
#   ./deploy kexec   <host>            headless kexec into a RAM installer, for a
#                                      read-only-root box (ZimaOS) where
#                                      nixos-anywhere can't ssh-copy-id. Ships our
#                                      SSH login key. Then run `install`.
#   ./deploy install <config> <host>  first install (nixos-anywhere). Wipes the
#                                      OS disk. Ships the host's sops key.
#   ./deploy switch  <config> <host>  rebuild + activate on a running host.
#   ./deploy boot    <config> <host>  stage for next boot, don't activate now.
#   ./deploy test    <config> <host>  activate without adding a boot entry.
#   ./deploy image   <config>         build an SD-card image (e.g. rpi mercury).
#   ./deploy flash   <config> <dev>   build SD image, write to <dev>, and (if
#                                     ~/.config/homelab/<config>/age.txt exists)
#                                     drop the sops key on its boot partition.
#
# <config> = a nixosConfigurations name (e.g. jupiter, vps). Its pre-generated
# SSH host key must be at ~/.config/homelab/<config>/ssh_host_ed25519_key.
#
# Runs from a non-NixOS host too (nixos-rebuild / nixos-anywhere via `nix run`).
set -euo pipefail

# Locate the repo root (flake dir) regardless of where this script lives on disk.
SCRIPT_DIR="$(cd "$(dirname "$(realpath "$0")")" && pwd)"
REPO="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || dirname "$SCRIPT_DIR")"
cd "$REPO"
export PATH="/nix/var/nix/profiles/default/bin:$PATH"

die() { echo "error: $*" >&2; exit 1; }

cmd="${1:-}"; [ -n "$cmd" ] || die "usage: ./deploy <kexec|install|switch|boot|test> ..."

case "$cmd" in
  kexec)
    host="${2:-}"; [ -n "$host" ] || die "usage: ./deploy kexec <host>"

    echo ">> building kexec installer + static tools"
    nix build .#nixosConfigurations.kexec.config.system.build.kexecInstallerTarball \
      -o result-kexec
    tb="$(ls result-kexec/*.tar.gz | head -1)"
    # kexec/run rebuilds an initrd with `cpio` + `gzip` from PATH — ZimaOS lacks
    # both. Ship static ones: GNU cpio (reliable -o -H newc), busybox as gzip.
    cpio="$(nix build --no-link --print-out-paths nixpkgs#pkgsStatic.cpio)/bin/cpio"
    bbox="$(nix build --no-link --print-out-paths nixpkgs#pkgsStatic.busybox)/bin/busybox"

    # One password prompt: multiplex scp + ssh over a shared control connection.
    cm="/tmp/homelab-cm-%r@%h:%p"
    o=(-o ControlMaster=auto -o "ControlPath=$cm" -o ControlPersist=300 \
       -o StrictHostKeyChecking=accept-new)

    echo ">> connecting to root@$host  (enter the root password once)"
    ssh "${o[@]}" "root@$host" 'mkdir -p /tmp/bin'
    scp "${o[@]}" "$cpio" "root@$host:/tmp/bin/cpio"
    scp "${o[@]}" "$bbox" "root@$host:/tmp/bin/gzip"   # busybox as gzip (argv0)

    echo ">> streaming installer + kexec-ing. SSH drops as the box jumps into the"
    echo "   RAM installer. Disks are untouched."
    ssh "${o[@]}" "root@$host" \
      'chmod +x /tmp/bin/*; mkdir -p /tmp/k && tar -C /tmp/k -xzf - && PATH=/tmp/bin:$PATH /tmp/k/kexec/run' \
      < "$tb" || true

    ssh "${o[@]}" -O exit "root@$host" 2>/dev/null || true  # close control socket
    echo ">> box is kexec-ing. Wait ~1-2 min for the installer + network, then:"
    echo "   ./deploy install <config> $host"
    ;;

  install)
    config="${2:-}"; host="${3:-}"
    { [ -n "$config" ] && [ -n "$host" ]; } || die "usage: ./deploy install <config> <host>"
    hostkey="$HOME/.config/homelab/$config/ssh_host_ed25519_key"
    [ -f "$hostkey" ] || die "missing host key: $hostkey"
    [ -d "./hosts/$config" ] || die "no ./hosts/$config directory in the repo"

    # Stage the pre-generated SSH host key so sops can decrypt on boot #1.
    stage="$(mktemp -d)"
    trap 'rm -rf "$stage"' EXIT
    install -Dm600 "$hostkey"      "$stage/etc/ssh/ssh_host_ed25519_key"
    install -Dm644 "$hostkey.pub"  "$stage/etc/ssh/ssh_host_ed25519_key.pub"

    echo ">> nixos-anywhere .#$config onto root@$host (OS disk WILL be wiped)"
    nix run github:nix-community/nixos-anywhere -- \
      --flake ".#$config" \
      --extra-files "$stage" \
      --generate-hardware-config nixos-generate-config "./hosts/$config/hardware-configuration.nix" \
      --target-host "root@$host"
    ;;

  switch|boot|test)
    config="${2:-}"; host="${3:-}"
    { [ -n "$config" ] && [ -n "$host" ]; } || die "usage: ./deploy $cmd <config> <host>"

    echo ">> nixos-rebuild $cmd .#$config on darman@$host"
    nix run nixpkgs#nixos-rebuild -- "$cmd" \
      --flake ".#$config" \
      --target-host "darman@$host" \
      --use-remote-sudo
    ;;

  image|flash)
    config="${2:-}"; [ -n "$config" ] || die "usage: ./deploy $cmd <config> [<dev>]"
    # Validate the device BEFORE building, so a bad `flash` fails fast.
    if [ "$cmd" = flash ]; then
      dev="${3:-}"; [ -n "$dev" ] || die "usage: ./deploy flash <config> <dev>  (e.g. /dev/sdX)"
      [ -b "$dev" ] || die "$dev is not a block device"
    fi

    echo ">> building SD image for .#$config (aarch64 needs qemu binfmt)"
    nix build ".#nixosConfigurations.$config.config.system.build.sdImage" -o result-sd
    img="$(ls result-sd/sd-image/*.img.zst | head -1)"
    echo ">> image: $img"
    [ "$cmd" = image ] && exit 0

    echo ">> TARGET DEVICE — everything on it will be ERASED:"
    lsblk -o NAME,SIZE,MODEL,TRAN,MOUNTPOINTS "$dev"
    read -rp ">> type 'yes' to write $config to $dev: " ok
    [ "$ok" = yes ] || die "aborted"
    zstdcat "$img" | sudo dd of="$dev" bs=4M status=progress oflag=sync
    sync

    # If this config has a dedicated sops age key, drop it on the ROOT ext4
    # partition at /var/lib/sops-nix/age.txt so sops decrypts on first boot.
    # (The Pi's vfat partition isn't mounted at runtime, so the key can't live
    # there.) Key stays off-repo, out of the nix store, and out of the image.
    keyfile="$HOME/.config/homelab/$config/age.txt"
    if [ -f "$keyfile" ]; then
      echo ">> installing sops age key onto the root partition"
      sudo partprobe "$dev" 2>/dev/null || sudo blockdev --rereadpt "$dev" 2>/dev/null || true
      sudo udevadm settle 2>/dev/null || true
      # largest ext4 partition = the NixOS root.
      rootpart="$(lsblk -blno PATH,FSTYPE,SIZE "$dev" | awk '$2=="ext4"{print $3, $1}' | sort -rn | head -1 | awk '{print $2}')"
      [ -n "$rootpart" ] || die "no ext4 root partition found on $dev — place $keyfile at /var/lib/sops-nix/age.txt manually"
      mnt="$(mktemp -d)"
      sudo mount "$rootpart" "$mnt"
      sudo install -Dm600 "$keyfile" "$mnt/var/lib/sops-nix/age.txt"
      sudo sync
      sudo umount "$mnt"; rmdir "$mnt"
      echo ">> age key installed (/var/lib/sops-nix/age.txt)"
    fi
    echo ">> done — insert the card into the Pi and boot."
    ;;

  *)
    die "unknown command '$cmd' (kexec|install|switch|boot|test)"
    ;;
esac
