#!/bin/bash

# The whole script is wrapped in { ...; exit; } so bash parses all of it before
# running anything. That way, replacing this file mid-run (e.g. by git pull and
# the copy below) can't make the running copy read a mix of old and new text.
{

# Install a script atomically (copy to a temp file, then rename over the target)
install_script() {
  local tmp
  tmp="$(mktemp "$2.XXXXXX")" && cp "$1" "$tmp" && chmod 755 "$tmp" && mv -f "$tmp" "$2"
}

# Print the logo
print_logo() {
  cat << "EOF"

 ███╗   ███╗██╗███╗   ██╗██╗
 ████╗ ████║██║████╗  ██║██║
 ██╔████╔██║██║██╔██╗ ██║██║
 ██║╚██╔╝██║██║██║╚██╗██║██║
 ██║ ╚═╝ ██║██║██║ ╚████║██║
 ╚═╝     ╚═╝╚═╝╚═╝  ╚═══╝╚═╝

 ███████╗███████╗██████╗ ██╗   ██╗███████╗██████╗
 ██╔════╝██╔════╝██╔══██╗██║   ██║██╔════╝██╔══██╗
 ███████╗█████╗  ██████╔╝██║   ██║█████╗  ██████╔╝
 ╚════██║██╔══╝  ██╔══██╗╚██╗ ██╔╝██╔══╝  ██╔══██╗
 ███████║███████╗██║  ██║ ╚████╔╝ ███████╗██║  ██║
 ╚══════╝╚══════╝╚═╝  ╚═╝  ╚═══╝  ╚══════╝╚═╝  ╚═╝



EOF
}

# Clear screen and show logo
clear
print_logo

MINISERVER="$HOME/.local/share/MiniServer"

echo "Updating MiniServer..."
if [[ -z "$UPDATE_REEXECED" ]] && ! git -C "$MINISERVER" pull; then
  echo "WARNING: could not pull the latest MiniServer; running the update hooks already on disk."
  export UPDATE_PULL_FAILED=1
fi
mkdir -p "$HOME/.local/bin" "$MINISERVER/updates.d"
install_script "$MINISERVER/update" ~/.local/bin/update
install_script "$MINISERVER/refresh" ~/.local/bin/refresh
install_script "$MINISERVER/sibling" ~/.local/bin/sibling
install_script "$MINISERVER/version" ~/.local/bin/version
install_script "$MINISERVER/configure" ~/.local/bin/configure
install_script "$MINISERVER/audit" ~/.local/bin/audit

if [[ -z "$UPDATE_REEXECED" ]]; then
  export UPDATE_REEXECED=1
  exec ~/.local/bin/update
fi

# An upgrade that runs out of disk space part way through can leave the system
# half-upgraded. Set UPDATE_FORCE=1 to skip this check.
MIN_FREE_GIB=10
FREE_KIB="$(df -k --output=avail / | tail -n 1 | tr -d ' ')"
if [[ -z "$UPDATE_FORCE" && "$FREE_KIB" =~ ^[0-9]+$ ]] && (( FREE_KIB < MIN_FREE_GIB * 1024 * 1024 )); then
  echo "Only $(( FREE_KIB / 1024 / 1024 )) GiB free on /; updating needs at least $MIN_FREE_GIB GiB."
  echo "Free up space (sudo paccache -rk1 clears old cached packages), or run: UPDATE_FORCE=1 update"
  exit 1
fi

# Ask for the password once, then keep sudo's cached credentials fresh until
# update exits, so long-running hooks don't stop for a password again.
sudo -v || exit 1
while kill -0 $$ 2>/dev/null; do sudo -n -v; sleep 60; done 2>/dev/null &
SUDO_KEEPALIVE_PID=$!
trap 'kill "$SUDO_KEEPALIVE_PID" 2>/dev/null' EXIT

FAILED=()
[[ -n "$UPDATE_PULL_FAILED" ]] && FAILED+=("git pull")

# Each hook runs in its own subshell so a cd, set -e, exit, or stray variable
# in one hook can't leak into or abort the hooks after it.
for plugin in "$MINISERVER/updates.d/"*.sh; do
  [ -f "$plugin" ] || continue
  (
    unset -f is_installed run_update
    source "$plugin"
    if declare -f run_update > /dev/null; then
      if ! declare -f is_installed > /dev/null || is_installed; then
        run_update
      fi
    fi
  )
  # Checked separately: under `( ... ) ||`, bash would ignore set -e inside the hook
  [ $? -eq 0 ] || FAILED+=("$(basename "$plugin")")
done

if (( ${#FAILED[@]} > 0 )); then
  echo "Done, with errors in: ${FAILED[*]}"
  exit 1
fi

echo "Done."
exit 0
}
