#!/usr/bin/bash
# vim: sw=3:ts=3:et
set -e

RAWHIDE_VERSION=43
FEDORA_VERSION=$(rpm -qf --qf '%{version}' /etc/os-release)
TARGET_VERSION=$((FEDORA_VERSION + 1))
UPGRADE_FINISHED=0
TMPFS_MOUNTED=0
DISABLED_PLUGINS="--disableplugin=needs-restarting --disableplugin=tracer"

function check_installation() {
  [[ -e /usr/sbin/fedora-upgrade ]]
}

function pause() {
   # clear the stdin buffer and pause with question
   read -t 1 -n 10000 discard || [ $? -gt 128 ]
   read -p "Hit Enter to continue or Ctrl + C to cancel."
}

function continue_or_skip() {
   read -t 1 -n 10000 discard || [ $? -gt 128 ]
   echo -e $1
   echo "This step is highly recommended but can be safely skipped."
   ANSWER='XXX'
   while [ "$ANSWER" != "" -a "$ANSWER" != "S" ] ; do
     read -p "Hit Enter to continue, Ctrl + C to cancel or S + Enter to skip. " ANSWER
     ANSWER=$(echo $ANSWER | tr "[:lower:]" "[:upper:]")
   done
}

function dnf_install_deps() {
  # TODO add -q to all dnf and create some kind of progress meter
  # but now be verbose
  install_if_missing rpmconf
  install_if_missing dnf-plugins-core
}

function install_if_missing() {
  echo "Checking if $1 is installed."
  rpm -q $1 >/dev/null || dnf install -y -q $1
}

function upgrade_before_upgrade() {
  continue_or_skip "\nGoing to run 'dnf upgrade' before upgrading."
  if [ "$ANSWER" != "S" ] ; then
    dnf upgrade
  fi
}

function dnf_upgrade_before_upgrade() {
  continue_or_skip "\nGoing to run 'dnf upgrade' before upgrading."
  if [ "$ANSWER" != "S" ] ; then
    dnf upgrade
  fi
}

function rpmconf_before_upgrade() {
  continue_or_skip "\nGoing to resolve old .rpmsave and .rpmnew files before upgrading."
  if [ "$ANSWER" != "S" ] ; then
    rpmconf -fvimdiff -a
  fi
}

function import_keys() {
  echo "Importing new RPM-GPG keys."
  rpm --import /usr/share/distribution-gpg-keys/fedora/RPM-GPG-KEY-fedora-$TARGET_VERSION-primary
  if [ -f /etc/yum.repos.d/rpmfusion-free.repo -a -e /usr/share/distribution-gpg-keys/rpmfusion/RPM-GPG-KEY-rpmfusion-free-fedora-$TARGET_VERSION ]; then
    rpm --import /usr/share/distribution-gpg-keys/rpmfusion/RPM-GPG-KEY-rpmfusion-free-fedora-$TARGET_VERSION
  fi
  if [ -f /etc/yum.repos.d/rpmfusion-nonfree.repo -a -e /usr/share/distribution-gpg-keys/rpmfusion/RPM-GPG-KEY-rpmfusion-nonfree-fedora-$TARGET_VERSION ]; then
    rpm --import /usr/share/distribution-gpg-keys/rpmfusion/RPM-GPG-KEY-rpmfusion-nonfree-fedora-$TARGET_VERSION
  fi
}

function dnf_install_base() {
  continue_or_skip "\nGoing to install missing packages from group 'Minimal Install'"
  if [ "$ANSWER" != "S" ] ; then
    dnf groupupdate 'Minimal Install'
  fi
}

function rpmconf_after_upgrade() {
  continue_or_skip "\nGoing to resolve .rpmsave and .rpmnew files after upgrade."
  if [ "$ANSWER" != "S" ] ; then
    rpmconf -fvimdiff -a
    rpmconf --clean
  fi
}

function remove_old_gpg_keys() {
  continue_or_skip "\nGoing to remove old GPG keys."
  if [ "$ANSWER" != "S" ] ; then
      /usr/sbin/fedora-remove-old-gpg-keys || true
  fi
}

function reset_service_priorities() {
  continue_or_skip "\nGoing to resets all installed unit files to the defaults configured\nin the preset policy file."
  if [ "$ANSWER" != "S" ] ; then
    systemctl preset-all || true
  fi
}

function EXCEPTION_reset_libgit2_module() {
  continue_or_skip "\nGoing to reset libgit2, exa, bat Fedora Modules. (dnf module reset libgit2 exa bat)"
  if [ "$ANSWER" != "S" ] ; then
    dnf module reset libgit2 exa bat
  fi
}

function EXCEPTION_reset_modules() {
  continue_or_skip "\nGoing to reset Fedora Modules. (dnf module reset '*')"
  if [ "$ANSWER" != "S" ] ; then
    dnf module reset '*'
  fi
}

function cleanup_cache() {
  rm -rf /var/cache/yum/* /var/cache/dnf/* /var/cache/PackageKit/*
}

function unwanted_packages() {
  LAST=$1
  continue_or_skip "\nThere may be some packages which are now orphaned, do you want to see them?"
  if [ "$ANSWER" != "S" ] ; then
      RESULT=0
      package-cleanup --orphans | grep -v kernel && RESULT=1
      if [ 0$RESULT -eq 1 ]; then
	  echo "These packages are very probably orphaned. You may want to remove them."
      fi
  fi
}

function unwanted_packages2() {
  LAST=$1
  continue_or_skip "\nThere may be some packages which are now retired, do you want to remove them?\nYou will confirm each removal separately."
  if [ "$ANSWER" != "S" ] ; then
      remove-retired-packages $LAST
  fi
}

function is_prerelease() {
  # will print string "--enablerepo=updates-testing" if this is prerelease, "" otherwise
  local RELEASE=$1
  TEMP=$(mktemp -d)
  dnf download -q --destdir "$TEMP" --disablerepo=* --enablerepo=fedora --releasever=$RELEASE fedora-repos
  if rpm2cpio "$TEMP"/fedora-repos*.rpm | cpio -i --quiet --to-stdout - ./etc/yum.repos.d/fedora-updates-testing.repo | grep 'enabled=1' >/dev/null ; then
    if [ -e /etc/yum.repos.d/fedora-updates-modular.repo ]; then
      echo "--enablerepo=updates-testing --enablerepo=updates-testing-modular"
    else
      echo "--enablerepo=updates-testing"
    fi
  else
    echo ""
  fi
  rm -rf "$TEMP"
}

function warn_if_graphics_mode() {
   if [[ "$TERM" =~ "screen".* || "$TERM" =~ "tmux".* ]]; then
      true
   else
      cat << WARNING
It seems you are running upgrade from graphics mode. You should use "screen" or "tmux".
Are you sure you want to continue?
WARNING
      pause
   fi
}

function print_exit_banner() {
  umount_cache_dnf
  if [ $UPGRADE_FINISHED -eq 2 ]; then
    echo "Enabling updates repos back"
    # upgrade to rawhide failed, restore repos
    dnf config-manager --set-enabled fedora updates updates-testing updates-modular
    # FIXME we should enable
    #    rpmfusion-free-updates rpmfusion-nonfree-updates
    # but only if we previously disabled it
    echo "Disabling rawhide repo."
    dnf config-manager --set-disabled rawhide
    UPGRADE_FINISHED=0
  fi
  if [ $UPGRADE_FINISHED -eq 1 ]; then
    echo
    echo You successfully upgraded to Fedora $TARGET_VERSION
    echo Reboot is strongly suggested.
    exit 0
  else
    echo
    echo Upgrade to Fedora $TARGET_VERSION was not finished!
    echo You can safely re-run fedora-upgrade again to start over with:
    echo "    fedora-upgrade --upgrade-to=${TARGET_VERSION}"
    exit 1
  fi
}

function welcome_banner() {
  echo "Going to upgrade your Fedora to version $1."
  echo "You may want to read Release Notes:"
  echo "  http://docs.fedoraproject.org/f${TARGET_VERSION}/release-notes/"
  echo "and about commond bugs:"
  echo "  https://fedoraproject.org/wiki/Common_F${TARGET_VERSION}_bugs"
  pause
}

function warn_about_online() {
  echo "Warning: This is unofficial upgrade path. For official tool see:"
  echo "         https://fedoraproject.org/wiki/Upgrading"
  echo "         While author of fedora-upgrade thinks online upgrade is better, it is"
  echo "         not officially tested by FedoraQA."
  pause
}

function going_to_reboot() {
  echo ""
  echo "********** End of DNF plugin output **********"
  echo ""
  echo "Download complete! The downloaded packages were saved in cache till the next"
  echo "successful transaction. You can remove cached packages by executing"
  echo "'dnf clean packages'."
  echo "In next step, your computer will be REBOOTED, and packages will be upgraded."
  pause
}

function choose_upgrade_method() {
  echo "Choose upgrade method"
  echo "  * offline - this use dnf-plugin-system-upgrade plugin and requires two reboots"
  echo "            - this is official upgrade method"
  echo "  * online  - this use distro-sync and require only one reboot"
  echo "            - this is not officially tested by FedoraQA"
  echo "For more information see https://fedoraproject.org/wiki/Upgrading"
  read -t 1 -n 10000 discard || [ $? -gt 128 ]
}

function mount_cache_dnf() {
  if [ -n "$OPTS_TMPFS" ]; then
    mount -t tmpfs none -o "size=$OPTS_TMPFS" /var/cache/libdnf5/
    TMPFS_MOUNTED=1
  fi
}

function umount_cache_dnf() {
  if [ -n "$OPTS_TMPFS" -a 0$TMPFS_MOUNTED -eq 1 ]; then
    umount /var/cache/libdnf5/
    TMPFS_MOUNTED=0
  fi
}

################################ START OF THE CODE ############################

if ! check_installation; then
  echo "Please install fedora-upgrade package using dnf first"
  exit 2
fi

if [ 0$UID -ne 0 ]; then
   echo "Error: You must be a root."
   echo "Run as: sudo fedora-upgrade"
   exit 1
fi

# make obvious ending for inexperienced users
trap "print_exit_banner" SIGHUP SIGINT SIGTERM INT ERR

# parse command line options
for i in "$@"; do
  case $i in
    -u=*|--upgrade-to=*)
    OPTS_UPGRADE_TO="${i#*=}"
    TARGET_VERSION="$OPTS_UPGRADE_TO"
    if [ "$TARGET_VERSION" = "rawhide" ]; then
      TARGET_VERSION="$RAWHIDE_VERSION"
    fi

    shift # past argument=value
    ;;
    --tmpfs=*)
    OPTS_TMPFS="${i#*=}"
    shift # past argument=value
    ;;
    --help)
    OPTS_HELP=YES
    shift # past argument
    ;;
    *)
          # unknown option
    ;;
  esac
done

if [ -n "$OPTS_HELP" ]; then
  man fedora-upgrade | cat
  exit 0;
fi

if [ 0$TARGET_VERSION -eq 44 ]; then
  # Fedora 43 to 44
  warn_if_graphics_mode
  welcome_banner $TARGET_VERSION

  dnf_install_deps
  dnf_upgrade_before_upgrade
  rpmconf_before_upgrade
  import_keys

  dnf clean -q dbcache metadata
  enable_updates=""
  #echo "Checking if this is pre-release"
  #enable_updates=$(is_prerelease $TARGET_VERSION )

  choose_upgrade_method
  echo -e $1
  ANSWER='XXX'
  while [ "$ANSWER" != "offline" -a "$ANSWER" != "online" ] ; do
    read -p "What is your choice? (offline/online)  " ANSWER
    ANSWER=$(echo $ANSWER | tr "[:upper:]" "[:lower:]")
  done
  if [ "$ANSWER" == "online" ] ; then
    mount_cache_dnf
    dnf --releasever=$TARGET_VERSION --setopt=deltarpm=false --setopt=module_platform_id=platform:f$TARGET_VERSION $enable_updates $DISABLED_PLUGINS distro-sync
    umount_cache_dnf
  elif [ "$ANSWER" == "offline" ] ; then
    install_if_missing dnf-plugin-system-upgrade
    dnf system-upgrade download --setopt=module_platform_id=platform:f$TARGET_VERSION --releasever=$TARGET_VERSION
    going_to_reboot
    dnf system-upgrade reboot
  fi

  cleanup_cache
  rpmconf_after_upgrade
  #reset_service_priorities
  unwanted_packages2 $FEDORA_VERSION
  remove_old_gpg_keys
  UPGRADE_FINISHED=1
elif [ 0$TARGET_VERSION -eq 42 ]; then
  # Fedora 41 to 42
  warn_if_graphics_mode
  welcome_banner $TARGET_VERSION

  dnf_install_deps
  dnf_upgrade_before_upgrade
  rpmconf_before_upgrade
  import_keys

  dnf clean -q dbcache metadata
  enable_updates=""
  #echo "Checking if this is pre-release"
  #enable_updates=$(is_prerelease $TARGET_VERSION )

  choose_upgrade_method
  echo -e $1
  ANSWER='XXX'
  while [ "$ANSWER" != "offline" -a "$ANSWER" != "online" ] ; do
    read -p "What is your choice? (offline/online)  " ANSWER
    ANSWER=$(echo $ANSWER | tr "[:upper:]" "[:lower:]")
  done
  if [ "$ANSWER" == "online" ] ; then
    mount_cache_dnf
    dnf --releasever=$TARGET_VERSION --setopt=deltarpm=false --setopt=module_platform_id=platform:f$TARGET_VERSION $enable_updates $DISABLED_PLUGINS distro-sync
    umount_cache_dnf
  elif [ "$ANSWER" == "offline" ] ; then
    install_if_missing dnf-plugin-system-upgrade
    dnf system-upgrade download --setopt=module_platform_id=platform:f$TARGET_VERSION --releasever=$TARGET_VERSION
    going_to_reboot
    dnf system-upgrade reboot
  fi

  cleanup_cache
  rpmconf_after_upgrade
  reset_service_priorities
  unwanted_packages2 $FEDORA_VERSION
  remove_old_gpg_keys
  UPGRADE_FINISHED=1
elif [ 0$TARGET_VERSION -eq 43 ]; then
  # Fedora 42 to 43
  warn_if_graphics_mode
  welcome_banner $TARGET_VERSION

  dnf_install_deps
  dnf_upgrade_before_upgrade
  rpmconf_before_upgrade
  import_keys

  dnf clean -q dbcache metadata
  enable_updates=""
  #echo "Checking if this is pre-release"
  #enable_updates=$(is_prerelease $TARGET_VERSION )

  choose_upgrade_method
  echo -e $1
  ANSWER='XXX'
  while [ "$ANSWER" != "offline" -a "$ANSWER" != "online" ] ; do
    read -p "What is your choice? (offline/online)  " ANSWER
    ANSWER=$(echo $ANSWER | tr "[:upper:]" "[:lower:]")
  done
  if [ "$ANSWER" == "online" ] ; then
    mount_cache_dnf
    dnf --releasever=$TARGET_VERSION --setopt=deltarpm=false --setopt=module_platform_id=platform:f$TARGET_VERSION $enable_updates $DISABLED_PLUGINS distro-sync
    umount_cache_dnf
  elif [ "$ANSWER" == "offline" ] ; then
    install_if_missing dnf-plugin-system-upgrade
    dnf system-upgrade download --setopt=module_platform_id=platform:f$TARGET_VERSION --releasever=$TARGET_VERSION
    going_to_reboot
    dnf system-upgrade reboot
  fi

  cleanup_cache
  rpmconf_after_upgrade
  #reset_service_priorities
  unwanted_packages2 $FEDORA_VERSION
  remove_old_gpg_keys
  UPGRADE_FINISHED=1
elif [ 0$TARGET_VERSION -eq 0$RAWHIDE_VERSION ]; then
  warn_if_graphics_mode
  echo "Going to upgrade your Fedora to rawhide."
  echo "Fedora $TARGET_VERSION is currently under development."
  echo "Are you sure?"
  pause

  dnf_install_deps
  rpmconf_before_upgrade

  install_if_missing fedora-repos-rawhide
  dnf config-manager --set-disabled rpmfusion-free-updates rpmfusion-nonfree-updates || true
  dnf config-manager --set-disabled fedora updates updates-testing updates-modular
  dnf config-manager --set-enabled rawhide
  UPGRADE_FINISHED=2
  dnf clean -q dbcache metadata

  choose_upgrade_method
  echo -e $1
  ANSWER='XXX'
  while [ "$ANSWER" != "offline" -a "$ANSWER" != "online" ] ; do
    read -p "What is your choice? (offline/online)  " ANSWER
    ANSWER=$(echo $ANSWER | tr "[:upper:]" "[:lower:]")
  done
  if [ "$ANSWER" == "online" ] ; then
    mount_cache_dnf
    dnf --releasever=rawhide --setopt=deltarpm=false --setopt=module_platform_id=platform:f$TARGET_VERSION $DISABLED_PLUGINS distro-sync --nogpgcheck
    umount_cache_dnf
  elif [ "$ANSWER" == "offline" ] ; then
    install_if_missing dnf-plugin-system-upgrade
    dnf system-upgrade download --setopt=module_platform_id=platform:f$TARGET_VERSION --releasever=rawhide --nogpgcheck
    going_to_reboot
    dnf system-upgrade reboot
  fi

  cleanup_cache
  rpmconf_after_upgrade
  reset_service_priorities
  remove_old_gpg_keys
  unwanted_packages
  UPGRADE_FINISHED=1
else
  echo Upgrading to version $TARGET_VERSION is not supported.
  exit 1
fi
print_exit_banner
