#!/bin/bash

showHelp() {
	echo "netupgrade [--help] [-f] [-y] [configfilename]"
	echo ""
	echo "  --help         Show this help message"
	echo "  -f             Select all nodes"
	echo "  -y             No confirmation for supported package managers"
	echo "  configfilename Path to a cfg file"
}

checkDependencies() {
	local -a REQUIRED_CMDS=(ssh whiptail sed tee rm touch)
	local -a MISSING_CMDS=()
	local CMD

	for CMD in "${REQUIRED_CMDS[@]}"; do
		if ! command -v "${CMD}" >/dev/null 2>&1; then
			MISSING_CMDS+=("${CMD}")
		fi
	done

	if [ ${#MISSING_CMDS[@]} -gt 0 ]; then
		echo "Error: missing required dependencies: ${MISSING_CMDS[*]}"
		exit 1
	fi
}

resolveLogViewer() {
	if [ -n "${EDITOR}" ] && command -v "${EDITOR}" >/dev/null 2>&1; then
		LOGVIEWER="${EDITOR}"
		return
	fi

	local -a CANDIDATES=(nano vi less)
	local CANDIDATE
	for CANDIDATE in "${CANDIDATES[@]}"; do
		if command -v "${CANDIDATE}" >/dev/null 2>&1; then
			LOGVIEWER="${CANDIDATE}"
			return
		fi
	done

	LOGVIEWER=""
}

pressAnyKey(){
	read -n1 -r -p "Press any key to continue."
}

loadConfig(){
	if [ -e "${CONFIGFILENAME}" ]; then
		. "${CONFIGFILENAME}"
	else
		echo "Error : Config file not found :"
		echo "${CONFIGFILENAME}"
		exit 1
	fi
}

selectNodes(){
	local -a OPTIONS=()
	local -i INDEX=1
	for NODE in "${NODES[@]}"; do
		# shellcheck disable=SC2206
		local FIELDS=(${NODE//;/ })
		local DESKSKIP=0
		local DESC=""
		for FIELD in "${FIELDS[@]}"; do
			if [ ${DESKSKIP} -gt 1 ]; then
				if [ "${DESC}" == "" ]; then
					DESC="${FIELD/:*/}"
				else
					DESC="${DESC}|${FIELD/:*/}"
				fi
			fi
			DESKSKIP=$(( DESKSKIP + 1))
		done
		OPTIONS+=("${INDEX}:${FIELDS[0]}" "${FIELDS[1]} [${DESC}]" "${FULL}")
		INDEX+=1
	done
	if ! SEL=$(whiptail --title "NetUpgrade" --checklist "" 0 0 0 \
		"${OPTIONS[@]}" \
		3>&1 1>&2 2>&3); then
		exit 0
	fi
	if [ ${#SEL} == 0 ]; then
		exit 0
	fi

	if [ -f "${LOGFILENAME}" ]; then
		rm "${LOGFILENAME}"
	fi
	touch "${LOGFILENAME}"
	local RESULT="\n"

	for ITM in ${SEL}; do
		INDEX=1
		for NODE in "${NODES[@]}"; do
			# shellcheck disable=SC2206
			local FIELDS=(${NODE//;/ })
			if [ "${ITM}" = "\"${INDEX}:${FIELDS[0]}\"" ]; then
				for ((I = 2; I < ${#FIELDS[@]}; ++I)); do
					if runCmd "${FIELDS[0]}" "${FIELDS[1]}" "${FIELDS[${I}]}"; then
						RESULT+="Ok: ${FIELDS[1]} @ ${FIELDS[0]} : ${FIELDS[${I}]}\n"
					else
						RESULT+="Error: ${FIELDS[1]} @ ${FIELDS[0]} : ${FIELDS[${I}]}\n"
					fi
				done
			fi
			INDEX+=1
		done
	done

	sed -i "1s/^/${RESULT//\//\\\/}\n\n\n\n/" "${LOGFILENAME}"
	sed -i "1s/^/---------\n\n/" "${LOGFILENAME}"
	sed -i "1s/^/Results :\n/" "${LOGFILENAME}"
	if [ -n "${LOGVIEWER}" ]; then
		"${LOGVIEWER}" "${LOGFILENAME}"
	else
		echo "Warning: no log viewer found, showing log path instead: ${LOGFILENAME}"
	fi
	rm -i "${LOGFILENAME}"
	echo ""
	echo "Results :"
	echo "---------"
	echo -e "${RESULT}"
}

runCmd() { #$1=host $2=name #3=cmd
	local -r HOST=${1}
	local -r NAME=${2}
	local -r CMD=${3//:*}
	local -r CMDVAL=${3//*:}
	local -i ERROR=0
	echo "${NAME} @ ${HOST} : ${CMD}" | tee -a "${LOGFILENAME}"
	local TITLELENGTH=$((${#NAME} + ${#HOST} + ${#CMD} + 6))
	local SUBTITLE="-----------------------------------------------------------------------------"
	echo ${SUBTITLE:0:${TITLELENGTH}} | tee -a "${LOGFILENAME}"
	date +'%Y-%m-%d %H:%M:%S %A' | tee -a "${LOGFILENAME}"
	echo "" | tee -a "${LOGFILENAME}"
	set -o pipefail
	local YESARG=""
	case ${CMD} in
		reboot)
			ssh root@"${HOST}" reboot | tee -a "${LOGFILENAME}"
		;;
		apt)
			if [ ${YES} == 1 ]; then
				YESARG="-y"
			fi
			echo "apt-get ${YESARG} update" | tee -a "${LOGFILENAME}"
			if ! ssh root@"${HOST}" apt-get ${YESARG} update | tee -a "${LOGFILENAME}"; then
				ERROR=1
			else
				echo "" | tee -a "${LOGFILENAME}"
				echo "apt-get ${YESARG} dist-upgrade" | tee -a "${LOGFILENAME}"
				if ! ssh root@"${HOST}" apt-get ${YESARG} dist-upgrade | tee -a "${LOGFILENAME}"; then
					ERROR=1
				fi
				echo "" | tee -a "${LOGFILENAME}"
				echo "apt-get ${YESARG} autoremove" | tee -a "${LOGFILENAME}"
				ssh root@"${HOST}" apt-get ${YESARG} autoremove | tee -a "${LOGFILENAME}"
				echo "" | tee -a "${LOGFILENAME}"
				echo "apt-get ${YESARG} autoclean" | tee -a "${LOGFILENAME}"
				ssh root@"${HOST}" apt-get ${YESARG} autoclean | tee -a "${LOGFILENAME}"
				echo "" | tee -a "${LOGFILENAME}"
				echo "apt-get ${YESARG} clean" | tee -a "${LOGFILENAME}"
				ssh root@"${HOST}" apt-get ${YESARG} clean | tee -a "${LOGFILENAME}"
				echo "" | tee -a "${LOGFILENAME}"
				echo "apt-get ${YESARG} purge" | tee -a "${LOGFILENAME}"
				ssh root@"${HOST}" apt-get ${YESARG} purge | tee -a "${LOGFILENAME}"
				echo "" | tee -a "${LOGFILENAME}"
			fi
		;;
		yum)
			if [ ${YES} == 1 ]; then
				YESARG="-y"
			fi
			echo "yum ${YESARG} update" | tee -a "${LOGFILENAME}"
			if ! ssh root@"${HOST}" yum ${YESARG} update | tee -a "${LOGFILENAME}"; then
				ERROR=1
			fi
		;;
		pkg)
			if [ ${YES} == 1 ]; then
				YESARG="-y"
			fi
			echo "pkg ${YESARG} update" | tee -a "${LOGFILENAME}"
			if ! ssh root@"${HOST}" pkg ${YESARG} update | tee -a "${LOGFILENAME}"; then
				ERROR=1
			else
				echo "" | tee -a "${LOGFILENAME}"
				echo "pkg upgrade ${YESARG}" | tee -a "${LOGFILENAME}"
				if ! ssh root@"${HOST}" pkg upgrade ${YESARG} | tee -a "${LOGFILENAME}"; then
								ERROR=1
				fi
				echo "" | tee -a "${LOGFILENAME}"
				echo "pkg autoremove ${YESARG}" | tee -a "${LOGFILENAME}"
				ssh root@"${HOST}" pkg autoremove ${YESARG} | tee -a "${LOGFILENAME}"
				echo "" | tee -a "${LOGFILENAME}"
				echo "pkg clean ${YESARG}" | tee -a "${LOGFILENAME}"
				ssh root@"${HOST}" pkg clean ${YESARG} | tee -a "${LOGFILENAME}"
				echo "" | tee -a "${LOGFILENAME}"
			fi
		;;
		pacman)
			if [ ${YES} == 1 ]; then
				YESARG="--noconfirm"
			fi
			echo "pacman -Sy ${YESARG} archlinux-keyring" | tee -a "${LOGFILENAME}"
			if ! ssh root@"${HOST}" pacman -Sy ${YESARG} archlinux-keyring | tee -a "${LOGFILENAME}"; then
				ERROR=1
			fi
			echo "pacman -Syu ${YESARG}" | tee -a "${LOGFILENAME}"
			if ! ssh root@"${HOST}" pacman -Syu ${YESARG} | tee -a "${LOGFILENAME}"; then
				ERROR=1
			fi
			# shellcheck disable=SC2046
			ssh root@"${HOST}" pacman -Rns $(pacman -Qqtd) ${YESARG} | tee -a "${LOGFILENAME}"
			ssh root@"${HOST}" pacman -Sc ${YESARG} | tee -a "${LOGFILENAME}"
		;;
		apk)
			if [ ${YES} == 1 ]; then
				YESARG="-y"
			fi
			echo "apk update" | tee -a "${LOGFILENAME}"
			if ! ssh root@"${HOST}" apk update | tee -a "${LOGFILENAME}"; then
				ERROR=1
			fi
			echo "apk upgrade" | tee -a "${LOGFILENAME}"
			if ! ssh root@"${HOST}" apk upgrade | tee -a "${LOGFILENAME}"; then
				ERROR=1
			fi
		;;
		cmd)
			echo "cmd: ${CMDVAL}" | tee -a "${LOGFILENAME}"
			# shellcheck disable=SC2029
			if ! ssh root@"${HOST}" "${CMDVAL}" | tee -a "${LOGFILENAME}"; then
				ERROR=1
			fi
		;;
		docker-stacks)
			echo "docker stacks update" | tee -a "${LOGFILENAME}"
			echo "for each" | tee -a "${LOGFILENAME}"
			echo "  docker compose pull; docker compose up -d" | tee -a "${LOGFILENAME}"
			if ! ssh root@"${HOST}" 'for dir in '"${CMDVAL}"'/*; do (cd "${dir}"; docker compose pull; docker compose up -d); done; docker image prune -f' | tee -a "${LOGFILENAME}"; then
				ERROR=1
			fi
			echo "docker image prune -a -f" | tee -a "${LOGFILENAME}"
			if ! ssh root@"${HOST}" docker image prune -f | tee -a "${LOGFILENAME}"; then
				ERROR=1
			fi
		;;
		*) echo "Error: Command ${CMD} unknown" | tee -a "${LOGFILENAME}";;
	esac
	echo "" | tee -a "${LOGFILENAME}"
	echo "" | tee -a "${LOGFILENAME}"
	if [ ${ERROR} == 1 ]; then
		return 1
	fi
}


declare -i YES=0
declare -i FULL=0
declare CONFIGFILENAME="${HOME}/.config/netupgrade/index.cfg"
declare LOGFILENAME="${HOME}/netupgrade.log"
declare LOGVIEWER=""
declare -a NODES=()

while [[ ${#} -gt 0 ]]; do
	case ${1} in
		--help) showHelp; exit 0;;
		-y) YES=1; shift;;
		-f) FULL=1; shift;;
		*) CONFIGFILENAME=${1}; shift;;
	esac
done

checkDependencies
resolveLogViewer
loadConfig
selectNodes

