#!/bin/bash

# omarchy:summary=Factory-reset this machine back to its freshly-installed state
# omarchy:group=setup
# omarchy:requires-sudo=true
# omarchy:examples=sudo omarchy-system-factory-reset

# Returns the machine to provisioning state — fully installed, no user, first-boot
# setup pending — the state you want before selling or handing the machine on.
#
# The running root (@) is swapped for a fresh writable clone of the @factory
# snapshot, so user-installed packages and /etc drift are gone too. That
# snapshot is taken by the Omarchy Quattro installer, so only machines
# installed from that ISO can be reset; a machine upgraded to Quattro from an
# earlier version has no baseline to return to and is turned away.
#
# The heavy lifting happens on the next boot via omarchy-system-factory-reset-finish, staged
# here. On encrypted installs the LUKS volume is re-keyed to a throwaway
# passphrase with an auto-unlock keyfile for the provisioning window; first-boot setup
# re-keys it to the new owner's password and removes the keyfile. A platform
# whose boot chain the Limine UKI path can't drive rebuilds, verifies and
# unlocks it through omarchy-lifecycle-dispatch.
#
# Honest limitations:
#  - On unencrypted disks this is deletion, not forensic erasure. fstrim helps
#    on SSDs; use blkdiscard + a reinstall if you need certainty.
#  - LUKS re-keying changes passphrases, not the volume key. Freed extents
#    remain readable to someone with raw-device access and a valid passphrase.
#    Use cryptsetup reencrypt or a reinstall if you need certainty.

set -euo pipefail

PROVISIONING_DIR=/var/lib/omarchy/provisioning
PACKAGED_PATH=/usr/bin/omarchy-system-factory-reset
# sudo drops OMARCHY_PATH from the environment, and omarchy-i18n, which
# supplies every line of the prompts below, needs it there.
export OMARCHY_PATH="${OMARCHY_PATH:-/usr/share/omarchy}"
TOP_MNT=/run/omarchy-system-factory-reset/top
ESP_MNT=/run/omarchy-system-factory-reset/esp
NEXT_NAME=@omarchy-reset-next
LOG_FILE=/var/log/omarchy-system-factory-reset.log

# Never replace a newer checkout reset with a different installed implementation.
if ! /usr/bin/cmp -s -- "${BASH_SOURCE[0]}" "$PACKAGED_PATH"; then
  echo "Factory reset differs from the installed command. Install the matching Omarchy package before resetting." >&2
  exit 1
fi

# Self-elevate so the menu entry (and a bare shell invocation) need no sudo
# prefix. The caller's gum theme vars are forwarded as `env` arguments rather
# than via `sudo -E`, so styling survives even under an env_reset sudoers.
# Always elevate the packaged copy, including when invoked from a dev checkout.
if (( EUID != 0 )); then
  mapfile -t gum_env < <(env | grep '^GUM_' || true)
  exec sudo env "${gum_env[@]}" "$PACKAGED_PATH" "$@"
fi

export PATH="$OMARCHY_PATH/bin:$PATH"

# The questions read in the language of the person who asked for the reset.
# Elevation left their HOME behind, so their language file is found from the
# account sudo names, never from a path the caller passed in.
if [[ -n ${SUDO_USER:-} && -z ${OMARCHY_LANGUAGE_FILE:-} ]]; then
  caller_home=$(getent passwd "$SUDO_USER" | cut -d: -f6 || true)
  [[ -n $caller_home ]] && export OMARCHY_LANGUAGE_FILE="$caller_home/.config/omarchy/language"
fi

log() {
  echo "$1" | tee -a "$LOG_FILE" >/dev/null
  gum style --foreground 8 "  $1"
}

fail() {
  gum style --foreground 1 "Error: $1"
  exit 1
}

root_device() {
  findmnt -no SOURCE / | sed 's/\[.*\]//'
}

root_is_btrfs_at() {
  [[ $(findmnt -no FSTYPE /) == btrfs ]] && findmnt -no OPTIONS / | grep -q "subvol=/@\(,\|$\)"
}

DISPATCH="$OMARCHY_PATH/bin/omarchy-lifecycle-dispatch"

# A platform whose boot package implements the reset operations rebuilds,
# verifies and unlocks the factory root's boot chain itself; everywhere else
# the Limine UKI path below does. Deciding fails closed.
RESET_BOOT=""

reset_boot_owner() {
  [[ -z $RESET_BOOT ]] || return 0

  local operation path found=0 missing=0
  for operation in reset-prepare reset-verify reset-commit reset-rollback; do
    path=$("$DISPATCH" --resolve "$operation") || return 1
    if [[ -n $path ]]; then
      found=$((found + 1))
    else
      missing=$((missing + 1))
    fi
  done
  if (( missing == 0 )); then
    RESET_BOOT=platform
  elif (( found == 0 )); then
    RESET_BOOT=generic
  else
    echo "Error: the boot package implements only some of the factory reset operations" >&2
    return 1
  fi
}

# Runs a reset operation with its output in the log. A failure keeps the
# operation's last line for the error shown.
RESET_BOOT_ERROR=""

reset_boot_op() {
  local output status=0
  output=$("$DISPATCH" "$@" 2>&1) || status=$?
  [[ -z $output ]] || printf '%s\n' "$output" >>"$LOG_FILE"
  if (( status == 0 )); then
    return 0
  fi
  RESET_BOOT_ERROR=$(tail -n 1 <<<"$output")
  RESET_BOOT_ERROR=${RESET_BOOT_ERROR:-"$1 failed"}
  return 1
}

# Resolve the crypto_LUKS partition backing the root, or return non-zero if
# the root is not on LUKS. Prefers the cmdline cryptdevice= spec (archinstall
# writes PARTUUID=, the pre-mounted path UUID=), and falls back to walking the
# device tree for roots reached via rd.luks/crypttab with a plain /dev/mapper
# root and no cryptdevice=.
luks_device() {
  local spec
  spec=$(grep -o 'cryptdevice=[^ :]*' /proc/cmdline | head -1 | cut -d= -f2-)
  case $spec in
    UUID=*) echo "/dev/disk/by-uuid/${spec#UUID=}"; return 0 ;;
    PARTUUID=*) echo "/dev/disk/by-partuuid/${spec#PARTUUID=}"; return 0 ;;
    LABEL=*) echo "/dev/disk/by-label/${spec#LABEL=}"; return 0 ;;
    PARTLABEL=*) echo "/dev/disk/by-partlabel/${spec#PARTLABEL=}"; return 0 ;;
    /dev/*) echo "$spec"; return 0 ;;
  esac

  local src part
  src=$(findmnt -no SOURCE / | sed 's/\[.*//')
  [[ -n $src ]] || return 1
  part=$(lsblk -nsrpo NAME,FSTYPE "$src" 2>/dev/null | awk '$2=="crypto_LUKS"{print $1; exit}')
  [[ -n $part ]] && { echo "$part"; return 0; }
  return 1
}

encrypted_install() {
  luks_device >/dev/null 2>&1
}

# No trailing `| head` stage: under pipefail an infinite tr killed by
# SIGPIPE fails the substitution and errexit aborts the whole reset.
generate_passphrase() {
  local raw
  raw=$(head -c 4096 /dev/urandom | tr -dc 'a-zA-Z0-9')
  printf '%s' "${raw:0:48}"
}

cleanup() {
  # Before the switch the old root still boots: the boot package gives it back
  # its boot state before the reset's throwaway slot is revoked.
  if [[ ${reset_committed:-0} == 0 && ${RESET_BOOT_PREPARED:-0} == 1 ]]; then
    reset_boot_op reset-rollback || echo "Error: $RESET_BOOT_ERROR (see $LOG_FILE)" >&2
  fi

  # A reset that failed after touching the ESP, before the factory root took
  # over, puts the running system's boot files back while the ESP is mounted.
  # The staged root still standing apart is what says the switch hasn't
  # happened: a signal between the rename and commit_next_boot must not put
  # the old kernel back under the factory root.
  # errexit still applies in this trap, so nothing here may stop the cleanup.
  if [[ -n ${esp_restore:-} && -d $TOP_MNT/$NEXT_NAME ]]; then
    if restore_esp "$esp_restore" "$esp_backup" "${esp_new_id:-}"; then
      log "Restored this system's boot files" || true
      rm -rf "$esp_backup" || true
    else
      log "Could not restore this system's boot files; copies are in $esp_backup" || true
    fi
  elif [[ -n ${esp_backup:-} ]]; then
    rm -rf "$esp_backup" || true
  fi

  if mountpoint -q "$ESP_MNT" 2>/dev/null; then
    umount -R "$ESP_MNT" 2>/dev/null || true
  fi
  rmdir "$ESP_MNT" 2>/dev/null || true

  if mountpoint -q "$TOP_MNT" 2>/dev/null; then
    # Never delete the staged root while there is no @: after a root switch
    # that could not be undone, it is one of the two systems left to recover.
    if [[ -d $TOP_MNT/$NEXT_NAME && ${swap_done:-0} == 0 && -d $TOP_MNT/@ ]]; then
      btrfs subvolume delete --recursive "$TOP_MNT/$NEXT_NAME" >/dev/null 2>&1 || true
    fi
    umount -R "$TOP_MNT" 2>/dev/null || true
  fi
  rmdir "$TOP_MNT" 2>/dev/null || true
  if [[ ${reset_committed:-0} == 0 ]]; then
    revoke_reset_luks
  fi
}
trap cleanup EXIT

confirm_reset() {
  echo
  gum style --bold --foreground 1 "$(omarchy-i18n reset.question)"
  echo
  gum style "$(omarchy-i18n reset.willErase)"
  gum style "  • $(omarchy-i18n reset.eraseAccounts)"
  gum style "  • $(omarchy-i18n reset.erasePackages)"
  gum style "  • $(omarchy-i18n reset.eraseIdentity)"
  echo
  gum style "$(omarchy-i18n reset.nextBoot)"
  gum style --foreground 8 "$(omarchy-i18n reset.notSecureErase)"
  echo

  local typed
  typed=$(gum input --placeholder "$(omarchy-i18n reset.typeToContinue reset)" --prompt "> ") || exit 1
  [[ $typed == "reset" ]] || fail "Reset not confirmed."
}

# The slot the reset's throwaway key opens, or nothing. Restricting cryptsetup
# to a token type no token has keeps an enrolled token from answering for it.
reset_luks_slot() {
  local -
  set +x
  LC_ALL=C cryptsetup open --test-passphrase --verbose --token-type passphrase-only \
    --key-file <(printf '%s' "$RESET_THROWAWAY") "$RESET_LUKS_DEVICE" 2>&1 |
    grep -o 'Key slot [0-9]* unlocked' | grep -o '[0-9]*' | head -1 || true
}

# Add the throwaway slot where the platform's boot package owns the unlock.
# Called once reset-verify passed, so a failed rebuild adds no credential.
# reset-commit writes the boot-time key only once the factory root is active:
# a power loss before the switch leaves the old root asking for its password,
# never unlocked unattended.
add_reset_luks_slot() {
  local -
  set +x
  [[ -n ${RESET_LUKS_DEVICE:-} && -n ${RESET_LUKS_AUTH:-} && -n ${RESET_THROWAWAY:-} ]] || fail "the reset is missing its throwaway passphrase"
  RESET_LUKS_ADDED=1
  { cryptsetup luksAddKey --key-file <(printf '%s' "$RESET_LUKS_AUTH") "$RESET_LUKS_DEVICE" <(printf '%s' "$RESET_THROWAWAY") ; } 2>>"$LOG_FILE" ||
    fail "could not add the throwaway LUKS keyslot"
  RESET_LUKS_SLOT=$(reset_luks_slot)
  [[ -n $RESET_LUKS_SLOT ]] || fail "could not identify the throwaway LUKS keyslot"
}

# Only the slot this attempt added goes, found by its key if the add was not
# confirmed. The platform's boot-time key is written only after activation, so
# a failed reset has none to revoke.
revoke_reset_luks() {
  local -
  set +x
  [[ ${RESET_LUKS_ADDED:-0} == 1 && -n ${RESET_LUKS_AUTH:-} ]] || return 0
  [[ -n ${RESET_LUKS_SLOT:-} ]] || RESET_LUKS_SLOT=$(reset_luks_slot)
  if [[ -n $RESET_LUKS_SLOT ]]; then
    { cryptsetup luksKillSlot -q --key-file <(printf '%s' "$RESET_LUKS_AUTH") "$RESET_LUKS_DEVICE" "$RESET_LUKS_SLOT" ; } 2>>"$LOG_FILE" || true
  else
    echo "Could not find the reset's throwaway LUKS slot on $RESET_LUKS_DEVICE to revoke; check its keyslots" >>"$LOG_FILE"
  fi
  RESET_LUKS_ADDED=0
  RESET_LUKS_SLOT=""
}

# Re-key to a throwaway passphrase whose keyfile auto-unlocks boot during the
# provisioning window. $1 is the root of the staged factory system. Where the
# platform's boot package owns the unlock, only the staged key is written here:
# reset-prepare stages the unlock with the rest of the boot files, and the
# throwaway slot follows their verification.
stage_luks_rekey() {
  local next="$1" device passphrase

  device=$(luks_device) || fail "encrypted install, but no usable cryptdevice= in /proc/cmdline"
  [[ -e $device ]] || fail "LUKS device $device not found"

  echo
  gum style "$(omarchy-i18n reset.confirmPassphrase)"
  local current
  while true; do
    current=$(gum input --password --placeholder "$(omarchy-i18n reset.passphrasePlaceholder)" --prompt "$(omarchy-i18n reset.passphrasePrompt)") || exit 1
    if cryptsetup open --test-passphrase --token-type passphrase-only --key-file <(printf '%s' "$current") "$device" 2>/dev/null; then
      break
    fi
    gum style --foreground 1 "$(omarchy-i18n reset.passphraseWrong "$device")"
  done

  passphrase=$(generate_passphrase)
  reset_boot_owner || fail "could not tell how this machine's boot chain is reset"
  RESET_LUKS_AUTH=$current
  RESET_LUKS_DEVICE=$device
  RESET_THROWAWAY=$passphrase
  if [[ $RESET_BOOT == "generic" ]]; then
    # Revoked like the platform's slot if the reset fails before activation.
    RESET_LUKS_ADDED=1
    cryptsetup luksAddKey --key-file <(printf '%s' "$current") "$device" <(printf '%s' "$passphrase")
  fi

  install -d -m 755 "$next$PROVISIONING_DIR"
  printf '%s' "$passphrase" >"$next$PROVISIONING_DIR/luks-key"
  chmod 600 "$next$PROVISIONING_DIR/luks-key"

  [[ $RESET_BOOT == "generic" ]] || return 0

  install -d -m 755 "$next/etc/omarchy"
  printf '%s' "$passphrase" >"$next/etc/omarchy/provisioning.key"
  chmod 600 "$next/etc/omarchy/provisioning.key"

  install -d "$next/etc/limine-entry-tool.d" "$next/etc/mkinitcpio.conf.d"
  echo 'KERNEL_CMDLINE[default]+=" cryptkey=rootfs:/etc/omarchy/provisioning.key"' \
    >"$next/etc/limine-entry-tool.d/99-omarchy-provisioning-unlock.conf"
  echo 'FILES+=(/etc/omarchy/provisioning.key)' \
    >"$next/etc/mkinitcpio.conf.d/99-omarchy-provisioning-key.conf"
}

install_provisioning_units() {
  local next="$1" unit_src="$2"

  install -Dm644 "$unit_src/omarchy-provision-owner.service" \
    "$next/etc/systemd/system/omarchy-provision-owner.service"
  install -Dm644 "$unit_src/omarchy-system-factory-reset-finish.service" \
    "$next/etc/systemd/system/omarchy-system-factory-reset-finish.service"

  install -d "$next/etc/systemd/system/multi-user.target.wants" \
    "$next/etc/systemd/system/sysinit.target.wants"
  ln -sf /etc/systemd/system/omarchy-provision-owner.service \
    "$next/etc/systemd/system/multi-user.target.wants/omarchy-provision-owner.service"
  ln -sf /etc/systemd/system/omarchy-system-factory-reset-finish.service \
    "$next/etc/systemd/system/sysinit.target.wants/omarchy-system-factory-reset-finish.service"
}

# Start the ESP's limine.conf settings over from the shipped template and
# hand this installation's boot entry to the new identity. limine-entry-tool
# keys OS entries by machine-id, so after a reset gives the machine a fresh
# identity, the previous entry (with the now-stale UKI hash) would survive,
# sort first, and make Limine refuse the rebuilt UKI with a hash-mismatch
# warning.
#
# A shared ESP also holds other systems' entries (Windows, other Linux
# installs), which must survive. So every entry stays where it is, and only
# the previous identity's entry is emptied and re-keyed to the new machine-id:
# limine-update then refills it in place, where default_entry points.
# limine-entry-tool appends a new OS entry after all the others, so starting
# a fresh one instead would move this system behind them. The previous
# identity's machine-id directory stays until commit_esp_staging.
reset_limine_config() {
  local root="$1" esp="$2" old_id="$3" template="" candidate
  local conf="$root$esp/limine.conf"

  for candidate in "$root/usr/share/omarchy/install/assets/limine/limine.conf" \
                   "$root/usr/share/omarchy/default/limine/limine.conf"; do
    if [[ -f $candidate ]]; then
      template=$candidate
      break
    fi
  done
  [[ -n $template ]] || fail "no limine.conf template in $root/usr/share/omarchy"

  local machine_id
  machine_id=$(cat "$root/etc/machine-id" 2>/dev/null || true)
  [[ $machine_id =~ ^[0-9a-f]{32}$ ]] || fail "the factory system has no machine-id"

  # Keep every top-level entry as it is, except the previous identity's: its
  # first copy keeps only its header, re-keyed; its kernel and snapshot
  # entries and any further copies are dropped.
  local entries=""
  if [[ -f $conf ]]; then
    entries=$(awk -v old="$old_id" -v new="$machine_id" '
      function flush() {
        if (is_old && !rekeyed) { printf "%s\n", header; rekeyed = 1 }
        else if (!is_old) printf "%s", block
        block = header = ""; is_old = nested = 0
      }
      /^\/[^\/]/ { flush(); in_entry = 1 }
      !in_entry { next }
      {
        line = $0
        if (old != "" && /^comment: machine-id=/ && $0 ~ ("machine-id=" old "([[:space:]]|$)")) {
          is_old = 1
          sub("machine-id=" old, "machine-id=" new, line)
        }
        if (/^[[:space:]]*\/\//) nested = 1
        block = block $0 "\n"
        if (!nested) header = header line "\n"
      }
      END { flush() }
    ' "$conf")
  fi

  { cat "$template"; [[ -z $entries ]] || printf '%s\n' "$entries"; } >"$conf.new"
  mv "$conf.new" "$conf"
}

# limine-update rewrites limine.conf, the bootloader binaries and the UKIs,
# and the UKIs carry one name across machine identities, so the rebuild
# overwrites the running system's own. Copy them all, so that a staging that
# fails before its entry verifies leaves this system booting as it did, and a
# retry starts again from this system's own entry.
esp_boot_paths() {
  printf '%s\n' limine.conf EFI/Linux EFI/limine EFI/BOOT
}

backup_esp() {
  local esp="$1" backup="$2" path
  install -d "$backup/EFI"
  for path in $(esp_boot_paths); do
    if [[ -e $esp/$path ]]; then
      cp -r "$esp/$path" "$backup/$path"
    fi
  done
}

# Whatever the rebuild added, such as the UKI of a kernel only the factory
# snapshot has, goes first: on a nearly full ESP the originals need its space.
# Plain cp: FAT keeps no owners, so preserving them would fail.
restore_esp() {
  local esp="$1" backup="$2" new_id="$3" path
  local -a present
  mapfile -t present < <(cd "$esp" && find $(esp_boot_paths) 2>/dev/null)
  for path in "${present[@]}"; do
    if [[ ! -e $backup/$path ]]; then
      rm -rf "${esp:?}/$path" || return 1
    fi
  done
  cp -r "$backup/." "$esp/" || return 1
  if [[ $new_id =~ ^[0-9a-f]{32}$ ]]; then
    rm -rf "${esp:?}/$new_id"
  fi
}

# The factory root is now the one that boots, so the ESP changes stand:
# disarm the restore (the cleanup trap then just discards the backup) and
# drop the previous identity's machine-id directory, whose kernels and
# snapshot UKIs nothing references any more.
commit_esp_staging() {
  local esp="$1" old_id="$2" new_id="$3"
  esp_restore=""
  if [[ $old_id =~ ^[0-9a-f]{32}$ && $old_id != "$new_id" ]]; then
    rm -rf "${esp:?}/$old_id"
  fi
}

# Every boot entry in limine.conf carries a #blake2b hash of each file it
# loads: the UKI, or the kernel and initramfs where UKIs are off (the DGX
# Spark). A mismatch makes Limine stop at a scary warning. Never hand over a
# staged reset in that state. Only this installation's entry is rebuilt, so
# only its files are checked: other systems' entries are not ours to judge.
# Snapshot entries keep their own copies under limine_history, which the
# rebuild does not touch.
verify_limine_hashes() {
  local root="$1" esp="$2" line path hash file machine_id checked=0
  machine_id=$(cat "$root/etc/machine-id")

  while IFS= read -r line; do
    path=${line#*boot():}
    path=${path%%#*}
    hash=${line##*#}
    file="$root$esp$path"
    [[ -f $file ]] || fail "limine.conf points at missing $path"
    [[ $(b2sum "$file" | cut -d' ' -f1) == "$hash" ]] ||
      fail "limine.conf hash for $path does not match the rebuilt file"
    checked=$((checked + 1))
  done < <(awk -v id="$machine_id" '
      /^\/[^\/]/ { mine = 0 }
      $0 ~ ("^comment: machine-id=" id "([[:space:]]|$)") { mine = 1 }
      mine
    ' "$root$esp/limine.conf" | grep -o 'boot():/[^#[:space:]]*#[0-9a-f]*' | grep -v '/limine_history/')

  ((checked > 0)) || fail "limine.conf has no rebuilt boot entry for this system"
}

# Rebuild the UKI from inside the next root so the deployed kernel/initramfs
# match the factory root's modules (the seller may have updated kernels since
# install) and embed the auto-unlock keyfile on encrypted machines.
rebuild_next_boot() {
  local next="$1"

  reset_boot_owner || fail "could not tell how this machine's boot chain is reset"
  if [[ $RESET_BOOT == "platform" ]]; then
    rebuild_next_boot_platform "$next"
    return
  fi

  local esp_source esp_mount
  esp_mount=$(awk '$3 == "vfat" && $2 ~ /^\/(boot|efi)/ { print $2; exit }' "$next/etc/fstab")
  esp_source=$(awk '$3 == "vfat" && $2 ~ /^\/(boot|efi)/ { print $1; exit }' "$next/etc/fstab")
  [[ -n $esp_mount && -n $esp_source ]] || fail "could not find the ESP in $next/etc/fstab"

  local esp_device=$esp_source
  if [[ $esp_source == UUID=* ]]; then
    esp_device="/dev/disk/by-uuid/${esp_source#UUID=}"
  fi
  [[ -e $esp_device ]] || fail "ESP device $esp_device not found"

  # The ESP stays mounted outside the staged root until the root switch, so
  # the cleanup trap can still restore it after the bind into the chroot is
  # gone.
  mkdir -p "$ESP_MNT"
  mount "$esp_device" "$ESP_MNT"
  mount --bind "$ESP_MNT" "$next$esp_mount"

  local dir
  for dir in proc sys dev run; do
    mount --rbind "/$dir" "$next/$dir"
    mount --make-rslave "$next/$dir"
  done

  # Btrfs snapshots do not recurse into nested subvolumes, so the factory
  # root carries only an empty /swap directory where the hibernation swapfile
  # lived. Recreate it and refresh the resume offset before the UKI build
  # bakes the cmdline; the empty placeholder directory has to go first so the
  # subvolume can be recreated.
  if [[ -x $next/usr/bin/omarchy-hibernation-setup ]] && grep -q "/swap/swapfile" "$next/etc/fstab"; then
    log "Recreating the hibernation swapfile in the factory system"
    if [[ -d $next/swap ]] && ! btrfs subvolume show "$next/swap" >/dev/null 2>&1; then
      rmdir "$next/swap" 2>/dev/null || true
    fi
    # The factory root still carries the resume drop-ins from install, which
    # make hibernation-setup conclude it has nothing to do; with the swapfile
    # gone they must go so it reconfigures from scratch. The resume-offset
    # drop-in especially: hibernation-setup only recomputes the physical
    # offset when it is absent, so a surviving one bakes the old swapfile's
    # offset into the rebuilt UKI and breaks resume.
    if [[ ! -f $next/swap/swapfile ]]; then
      rm -f "$next/etc/mkinitcpio.conf.d/omarchy_resume.conf" \
        "$next/etc/limine-entry-tool.d/resume.conf"
    fi
    if ! chroot "$next" env OMARCHY_PATH=/usr/share/omarchy \
      /usr/bin/omarchy-hibernation-setup --force --no-rebuild >>"$LOG_FILE" 2>&1; then
      fail "hibernation setup failed in the factory root (see $LOG_FILE)"
    fi
    [[ -f $next/swap/swapfile ]] ||
      fail "hibernation setup did not recreate $next/swap/swapfile (see $LOG_FILE)"
  fi

  # The running system's identity is the one the reset replaces.
  esp_old_id=$(cat /etc/machine-id 2>/dev/null || true)
  esp_new_id=$(cat "$next/etc/machine-id" 2>/dev/null || true)

  # From here until the factory root takes over, the cleanup trap restores
  # this copy of the ESP's boot files on any failure.
  esp_backup=$(mktemp -d /var/tmp/omarchy-system-factory-reset-esp.XXXXXX)
  backup_esp "$ESP_MNT" "$esp_backup"
  esp_restore=$ESP_MNT

  reset_limine_config "$next" "$esp_mount" "$esp_old_id"

  log "Rebuilding boot files from the factory system (this can take a minute)"
  # Resolved through PATH, not an absolute path: /usr/bin on Arch but
  # /usr/local/bin where Fedora carries the source-built Limine helpers
  # (see install/fedora/bootloader.sh).
  if ! chroot "$next" /bin/bash -c 'limine-update' >>"$LOG_FILE" 2>&1; then
    fail "limine-update failed in the factory root (see $LOG_FILE)"
  fi

  verify_limine_hashes "$next" "$esp_mount"

  for dir in run dev sys proc; do
    umount -R "$next/$dir" 2>/dev/null || true
  done
  umount "$next$esp_mount"
}

# The boot package stages the factory root's boot files and unlock, then proves
# they boot it; reset-rollback undoes both if the reset fails before the switch.
# The throwaway slot is added only once they are proven.
rebuild_next_boot_platform() {
  local next="$1"
  local -a luks=()
  [[ -z ${RESET_LUKS_DEVICE:-} ]] || luks=("$RESET_LUKS_DEVICE")

  RESET_BOOT_PREPARED=1
  log "Rebuilding boot files from the factory system (this can take a minute)"
  reset_boot_op reset-prepare "$next" "${luks[@]}" || fail "$RESET_BOOT_ERROR (see $LOG_FILE)"
  log "Verifying that the factory system boots"
  reset_boot_op reset-verify "$next" || fail "$RESET_BOOT_ERROR (see $LOG_FILE)"

  [[ -z ${RESET_LUKS_DEVICE:-} ]] || add_reset_luks_slot
}

# Once the factory root is in place it is what boots, so the boot files
# rebuild_next_boot left on the ESP stand and the ESP can be released. The
# reset is staged by now: a leftover directory or mount only costs space
# until the reboot, so neither may report the reset as failed.
commit_next_boot() {
  commit_esp_staging "$ESP_MNT" "${esp_old_id:-}" "${esp_new_id:-}" || true
  umount "$ESP_MNT" 2>/dev/null || true
}

# Both the staged system and the retained baseline must lose the old hashes.
scrub_factory_accounts() {
  local root="$1" user users

  users=$(awk -F: '$3 >= 1000 && $3 < 60000 { print $1 }' "$root/etc/passwd") || return 1
  for user in $users; do
    userdel --root "$root" "$user" 2>>"$LOG_FILE" || return 1
    rm -rf "${root:?}/home/$user" || return 1
  done

  # passwd --lock preserves the hash. Replace it, then remove the backups
  # that userdel and usermod leave behind.
  usermod --root "$root" --password '!' root >>"$LOG_FILE" 2>&1 || return 1
  rm -f "$root/etc/"{shadow-,gshadow-,passwd-,group-,subuid-,subgid-}
}

# Remove the seller's account material and machine identity from the retained
# @factory baseline so it can neither be mounted for recovery nor restore the
# seller's account on a future reset. Idempotent (a scrubbed baseline has no
# uid>=1000 accounts left to remove).
sanitize_factory_baseline() {
  local factory="$1"
  btrfs property set -ts "$factory" ro false

  if ! scrub_factory_accounts "$factory"; then
    btrfs property set -ts "$factory" ro true
    fail "could not remove account credentials from the factory baseline (see $LOG_FILE)"
  fi
  rm -f "$factory"/etc/ssh/ssh_host_*
  rm -f "$factory"/etc/NetworkManager/system-connections/*
  rm -rf "$factory"/var/lib/NetworkManager/* "$factory/var/lib/tailscale" "$factory/var/lib/iwd"
  rm -f "$factory/var/lib/sddm/state.conf" "$factory/etc/sddm.conf.d/autologin.conf"
  : >"$factory/etc/machine-id"

  btrfs property set -ts "$factory" ro true
}

stage_full_reset() {
  local top="$TOP_MNT" next="$TOP_MNT/$NEXT_NAME"

  log "Cloning the factory snapshot"
  [[ -d $next ]] && btrfs subvolume delete --recursive "$next" >/dev/null
  btrfs subvolume snapshot "$top/@factory" "$next" >>"$LOG_FILE"

  local unit_src="$next/usr/share/omarchy/install/provisioning"
  [[ -f $unit_src/omarchy-provision-owner.service && -x $next/usr/bin/omarchy-provision-owner ]] ||
    fail "the factory snapshot predates provisioning support; cannot reset from it"

  log "Scrubbing machine identity from the factory system"
  systemd-id128 new >"$next/etc/machine-id"
  rm -f "$next"/etc/ssh/ssh_host_*
  rm -f "$next"/etc/NetworkManager/system-connections/*
  rm -rf "$next"/var/lib/NetworkManager/* "$next/var/lib/tailscale" "$next/var/lib/iwd"
  rm -f "$next/var/lib/sddm/state.conf" "$next/etc/sddm.conf.d/autologin.conf"

  # A factory snapshot from a normal install contains the original
  # user account; first-boot setup must start from none. A leftover account
  # would keep its password hash and group memberships (including wheel), so
  # failure here has to abort the reset, not be shrugged off.
  log "Removing account credentials from the factory system"
  scrub_factory_accounts "$next" ||
    fail "could not remove account credentials from the factory system (see $LOG_FILE)"

  # @factory itself survives the wipe as the baseline for future resets. If it
  # came from a normal install it still holds the seller's account and
  # /etc/shadow, which the new wheel user could mount and read — and a second
  # reset would restore that account. Scrub it once, in place.
  sanitize_factory_baseline "$top/@factory"

  install -d -m 755 "$next$PROVISIONING_DIR"
  touch "$next$PROVISIONING_DIR/pending" "$next$PROVISIONING_DIR/wipe-pending"

  install_provisioning_units "$next" "$unit_src"

  if encrypted_install; then
    stage_luks_rekey "$next"
  fi

  rebuild_next_boot "$next"

  log "Activating the factory system"
  local old="@omarchy-old-$(date +%s)"
  mv "$top/@" "$top/$old"
  # Until the factory root is at @ the reset is not committed: cleanup restores
  # the old root's boot files, or has the platform's boot package roll them
  # back, and revokes the throwaway slot, so the old root goes back to @ first.
  if ! mv "$next" "$top/@"; then
    if mv "$top/$old" "$top/@"; then
      fail "could not activate the factory system; the current root is back at @ (see $LOG_FILE)"
    fi
    # Neither root is at @. Keep the staged one as well for recovery.
    swap_done=1
    fail "could not activate the factory system or put the current root back; from a live USB, mount the Btrfs top level, rename $old to @, and rebuild its boot files if it does not boot"
  fi
  # From here the old root is gone, so the reset's unlock must survive.
  reset_committed=1
  swap_done=1
  commit_next_boot
  sync

  # The boot package that prepared the reset gets the key on standard input,
  # never in arguments. A failure costs the first boot one prompt for the
  # current disk password.
  if [[ ${RESET_BOOT_PREPARED:-0} == 1 ]] && ! reset_boot_op reset-commit < <(printf '%s' "${RESET_THROWAWAY:-}"); then
    log "$RESET_BOOT_ERROR"
  fi
}

# A reset is a clone of @factory, so a machine without one has nothing to
# return to. Only the Quattro installer takes that snapshot.
require_factory_snapshot() {
  [[ -d $TOP_MNT/@factory ]] && return 0

  echo
  gum style --bold --foreground 3 "$(omarchy-i18n reset.noSnapshot)"
  echo
  gum style --width 80 "$(omarchy-i18n reset.noSnapshotWhy)"
  echo
  gum style --foreground 8 "$(omarchy-i18n reset.reinstallHint)"
  exit 1
}

main() {
  omarchy-cmd-present btrfs || { echo "Error: btrfs-progs is required" >&2; exit 1; }

  root_is_btrfs_at || fail "reset requires the standard Omarchy Btrfs layout (subvol=@)"

  touch "$LOG_FILE"
  chmod 600 "$LOG_FILE"
  swap_done=0

  local device
  device=$(root_device)
  [[ -n $device ]] || fail "could not determine the root device"

  mkdir -p "$TOP_MNT"
  mountpoint -q "$TOP_MNT" || mount -o subvolid=5 "$device" "$TOP_MNT"

  require_factory_snapshot

  # Decided before anything is asked: a platform whose boot package is missing
  # stops here, with the dispatcher's error naming the package.
  reset_boot_owner || fail "this machine's boot package cannot factory-reset it"

  confirm_reset

  # The running system's limine-snapper-sync must not rewrite the ESP's
  # limine.conf behind the staged rebuild (subvolume changes below can
  # trigger it). The runtime mask evaporates on the reboot that follows.
  systemctl mask --runtime --now limine-snapper-sync.service >/dev/null 2>&1 || true
  systemctl mask --runtime --now limine-snapper-sync.path >/dev/null 2>&1 || true

  stage_full_reset

  umount -R "$TOP_MNT" 2>/dev/null || true

  echo
  gum style --bold "$(omarchy-i18n reset.staged)"
  if gum confirm --affirmative "$(omarchy-i18n reset.rebootNow)" --negative "$(omarchy-i18n reset.rebootLater)" "$(omarchy-i18n reset.rebootQuestion)"; then
    systemctl reboot
  else
    gum style --foreground 3 "$(omarchy-i18n reset.stopUsing)"
  fi
}

main "$@"
