#!/bin/bash -p

# omarchy:summary=Update Omarchy and system packages
# omarchy:alias=omarchy up
# omarchy:args=[-y]
# omarchy:examples=omarchy update | omarchy update -y
# omarchy:requires-sudo=true

if [[ $- != *p* ]]; then
  echo "Refusing an unsafe Bash startup." >&2
  exit 126
fi

security_entrypoint=$(/usr/bin/readlink -e -- "${BASH_SOURCE[0]}") || exit 126
source "${security_entrypoint%/*}/omarchy-security-functions" || exit 126
omarchy_security_require_privileged_bash_startup || exit 126
set -e
omarchy_security_sanitize_bash_environment "$0" "$@"
omarchy_security_require_source_root "$0"
# Logging and lock acquisition re-exec this command with a sanitized PATH.
# Preserve the caller's path only for the later unprivileged hook/mise phases.
user_path=${OMARCHY_UPDATE_USER_PATH:-$PATH}
unset OMARCHY_UPDATE_USER_PATH
# Traps first, so a signal or failure during the entry revocation still
# exits through the cleanup path.
omarchy_security_install_sudo_cleanup_traps
omarchy_security_revoke_sudo_timestamp || exit 1
omarchy_security_enable_no_update_sudo

update_stay_awake_stopped=0
cleanup_update() {
  local status=$?
  trap - EXIT HUP INT TERM
  if ! omarchy_security_revoke_sudo_timestamp; then
    echo "Could not invalidate sudo before update cleanup." >&2
    omarchy_security_exit_with_revoked_sudo 1
  fi
  if (( update_stay_awake_stopped == 0 )); then
    omarchy-update-stay-awake stop || status=1
  fi
  omarchy_security_exit_with_revoked_sudo "$status"
}

if [[ -z ${OMARCHY_UPDATE_LOGGED:-} ]]; then
  script_command=$(printf '%q ' "$0" "$@")
  exec env OMARCHY_UPDATE_LOGGED=1 OMARCHY_UPDATE_USER_PATH="$user_path" script -qefc "$script_command" "/tmp/omarchy-update.log"
fi

if ! omarchy-update-lock held; then
  exec env OMARCHY_UPDATE_USER_PATH="$user_path" omarchy-update-lock run "$0" "$@"
fi

trap 'echo ""; echo -e "\033[0;31mSomething went wrong during the update!\n\nPlease review the output above carefully, correct the error, and retry the update.\n\nIf you need assistance, get help from the community at https://omarchy.org/discord\033[0m"' ERR
trap cleanup_update EXIT
omarchy_security_install_signal_exit_traps

omarchy-update-requires-free-space

# -y suppresses Omarchy confirmation prompts; sudo authorization is still
# required. Interactive review steps report and move on instead of waiting.
[[ ${1:-} != "-y" ]] || export OMARCHY_UPDATE_UNATTENDED=1

if [[ ${1:-} == "-y" ]] || omarchy-update-confirm; then
  # Before the snapshot: the cache is on the snapshotted subvolume, so pruning
  # after it frees nothing until that snapshot ages out.
  omarchy-update-pkg-prune

  # 127 means Snapper is deliberately absent. Any other failure already said
  # what went wrong, and a missing snapshot is not worth blocking an update
  # over, but it must not pass for one either.
  omarchy-snapshot create || (($? == 127)) ||
    echo -e "\e[33mContinuing the update without a snapshot.\e[0m" >&2

  omarchy-update-stay-awake start

  # Preserve the established development-checkout update ordering.
  omarchy-update-dev
  omarchy-update-keyring

  # Migrations ship with the packages installed here and are written against
  # them, so everything below waits on this finishing. An upgrade that stopped
  # takes the update with it rather than migrating against what is still on disk.
  omarchy-update-system-pkgs

  # Historical migrations are strictly ordered and mix user hooks/downloaded
  # tooling with privileged repairs. The no-update sudo wrapper has covered the
  # whole update, so neither the package transaction nor a later repair can
  # publish a timestamp to a detached migration child.
  omarchy_security_revoke_sudo_timestamp
  omarchy-migrate
  omarchy-update-orphan-pkgs

  omarchy-update-analyze-logs
  omarchy-update-status

  # Service restart helpers can need sudo. Run them before any user-controlled
  # update tooling; the reboot-only phase below performs no privileged work.
  omarchy-update-restart --services-only

  # AUR package installation must also use the no-update wrapper. Finish
  # update-owned system work before build code, hooks, or mise can run.
  omarchy_security_revoke_sudo_timestamp
  omarchy-update-aur-pkgs
  omarchy_security_revoke_sudo_timestamp

  # Hooks and mise execute user-controlled code. Give each a cold credential
  # boundary and run mise last so it cannot wait for a legitimate hook sudo.
  # Only the unprivileged reboot prompt follows them.
  PATH="$OMARCHY_PATH/default/omarchy/sudo-no-update:$user_path" "$OMARCHY_PATH/bin/omarchy-hook" post-update
  omarchy_security_revoke_sudo_timestamp
  PATH="$OMARCHY_PATH/default/omarchy/sudo-no-update:$user_path" "$OMARCHY_PATH/bin/omarchy-update-mise"
  omarchy_security_revoke_sudo_timestamp

  # The sleep inhibitor covers AUR builds, hooks and mise as well; releasing it
  # needs no privilege because the held command already dropped to this user.
  # Release it before offering a reboot: a confirmed reboot can terminate this
  # process before its EXIT trap gets a chance to remove the persistent Stay
  # Awake marker.
  omarchy-update-stay-awake stop
  update_stay_awake_stopped=1

  "$OMARCHY_PATH/bin/omarchy-update-restart" --reboot-only
fi
