#!/usr/bin/env bash

##############################################################################################
# THIS MODULE INCLUDES ALL THE ACTIONS INTENDED TO MANAGE THE APPS INSTALLED FROM THE DATABASE
# AND ONE OPTION IS FOR LOCAL APPIMAGES INSTEAD
# OPTIONS: BACKUP/RESTORE, DOWNGRADE, LAUNCHER, LOCK/UNLOCK, NOLIBFUSE, REMOVE
##############################################################################################

# BACKUP
_backup_name() {
	printf "\n ◆ To set date and time as a name, press ENTER (default)\n ◆ To set the version as a name, press \"1\"\n ◆ To set a custom name, write anything else\n\n"
	read -r -p " Write your choice here, or leave blank to use \"date/time\": " response
	case "$response" in
		'')
			backupname=$(date +%F-%X | sed 's/://g' | sed 's/-//g')
			;;
		'1')
			_check_version
			backupname=$(grep -w " ◆ $app_name	|" "$AMCACHEDIR"/version-args 2>/dev/null | sed 's:.*|	::')
			;;
		*)
			backupname="$(echo "$response" | sed 's/ /_/g')"
			;;
	esac
}

_backup() {
		if [ ! -f "$argpath"/remove ]; then
		echo " \"$2\" is not a valid argument or is not installed."
	else
		read -r -p " Do you wish to backup the current version of $2? (y/N) " yn
		if ! echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
			printf "\n OPERATION ABORTED!\n\n"
		else
			mkdir -p "$HOME/.am-snapshots/$2"
			app_name="$2"
			_backup_name
			if test -d "$HOME/.am-snapshots/$2/$backupname"; then
				echo " 💀 ERROR: \"$2/$backupname\" already exists, ABORTED!"
				echo "$DIVIDING_LINE"
				return 1
			else
				cp -r "$argpath" "$HOME/.am-snapshots/$2/$backupname"
			fi
			echo " SAVED in $HOME/.am-snapshots/$2/$backupname"
		fi
	fi
	echo "$DIVIDING_LINE"
}

# RESTORE
_overwrite() {
	if [ ! -d "$HOME/.am-snapshots/$2" ]; then
		echo " ERROR: No snapshot for \"$2\" found."
	elif [ ! -f "$argpath"/remove ]; then
		echo " \"$2\" is not a valid argument or is not installed."
	else
		read -r -p " Do you wish to overwrite $2 with an older version? (y,N) " yn
		if ! echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
			printf "\n OPERATION ABORTED! \n\n"
		else
			printf "\n Please, select a snapshot or press CTRL+C to abort:\n\n"
			sleep 1
			select d in "$HOME/.am-snapshots/$2"/*; do
				test -n "$d" && break
				echo ">>> Invalid Selection"
			done
			cp -r --backup=t "$d"/* "$argpath"/
			rm -R -f ./tmp "$argpath"/*~
			echo " RESTORE COMPLETED SUCCESSFULLY!"
		fi
	fi
	echo "$DIVIDING_LINE"
}

# DOWNGRADE
_downgrade_entries_filter() {
	grep -vi "^#\|version0=" ./AM-updater | grep "api.github.com" \
		| tr '=' '\n' | sed 's/^\$(//g' \
		| tail -1 | sed '1,${
		s/version=$(//g
		s/head -1)/head -100/g
		s#$REPO#'"$REPO"'#g
		s#releases/latest#releases#g
		s#releases #releases?per_page=100 #g
		s#/$tag/$app##g
		}'
}

_downgrade() {
		# Safety checks
	if [ ! -f "$argpath"/AM-updater ]; then
		echo " No AM-updater available for \"$2\", cannot detect any URL!"
		return 1
	elif [ -f "$argpath"/"$2"-rollback ]; then
		cd "$argpath" || return 1
		./"$2"-rollback || return 1
		return 0
	elif ! grep -q "api.github.com" "$argpath"/AM-updater; then
		echo " The option \"$1\" only works for https://github.com"
		return 1
	fi
	cd "$argpath" || return 1
	entries_list=$(_downgrade_entries_filter)
	urls="$(eval "$entries_list" 2>/dev/null | grep -vi "zsync$")"
	if ! echo "$urls" | grep -q "[0-9]"; then
		echo " ERROR: No valid links found, sorry!"
		exit 1
	fi
	echo "$DIVIDING_LINE"
	echo "You have chosen to roll back \"$2\"."
	echo "$DIVIDING_LINE"
	echo "Select a URL from this menu (read carefully) or press CTRL+C to abort:"
	echo "$DIVIDING_LINE"
	sleep 1
	select d in $urls; do
		test -n "$d" && break
		echo ">>> Invalid Selection"
	done
	cp ./AM-updater ./AM-rollback
	sed -i 's/version0/#version0/g' ./AM-rollback
	sed -i 's/appimageupdatetool -Or/#appimageupdatetool -Or/g' ./AM-rollback
	[ -f ./"$2".zsync ] && mv ./"$2".zsync ./"$2".zsync.old
	if wget --version | head -1 | grep -q ' 1.'; then
		sed -i '/wget/c\wget -q --show-progress '"$d"'' ./AM-rollback
	else
		sed -i '/wget/c\wget '"$d"'' ./AM-rollback
	fi
	./AM-rollback && rm -f ./AM-rollback || return 1
	mv ./"$2".zsync.old ./"$2".zsync 2>/dev/null
	echo "$d" > ./version
	echo "ROLLBACK SUCCESSFUL!"
}

# ICON THEME CHANGER
_icon_theme() {
	if [ "$AMCLI" = am ] && test -f /usr/local/share/applications/"$arg"*AM.desktop; then
		$SUDOCMD sed -i "s#Icon=$argpath/icons/#Icon=#g" /usr/local/share/applications/"$arg"*AM.desktop 2>/dev/null
	else
		sed -i "s#Icon=$argpath/icons/#Icon=#g" "$DATADIR"/applications/"$arg"*AM.desktop 2>/dev/null
	fi
	_icon_theme_export_to_datadir 2>/dev/null
}

# LAUNCHER
_launcher_appimage_integration() {
	"$arg" --appimage-extract *.desktop 1>/dev/null && mv ./squashfs-root/*.desktop ./"$appimage".desktop
	"$arg" --appimage-extract .DirIcon 1>/dev/null && mv ./squashfs-root/.DirIcon ./DirIcon
	COUNT=0
	while [ "$COUNT" -lt 10 ]; do # Tries to get the actual icon/desktop if it is a symlink to another symlink
		if [ -L ./"$appimage".desktop ]; then
			LINKPATH="$(readlink ./"$appimage".desktop | sed 's|^\./||' 2>/dev/null)"
			"$arg" --appimage-extract "$LINKPATH" 1>/dev/null && mv ./squashfs-root/"$LINKPATH" ./"$appimage".desktop
		fi
		if [ -L ./DirIcon ]; then
			LINKPATH="$(readlink ./DirIcon | sed 's|^\./||' 2>/dev/null)"
			"$arg" --appimage-extract "$LINKPATH" 1>/dev/null && mv ./squashfs-root/"$LINKPATH" "$DATADIR"/icons/ 1>/dev/null
		fi
		[ ! -L ./"$appimage".desktop ] && [ ! -L ./DirIcon ] && break
		COUNT=$((COUNT + 1))
	done
	sed -i "s#Exec=[^ ]*#Exec=\"$arg\"#g" ./"$appimage".desktop
	mv ./"$appimage".desktop "$DATADIR"/applications/AppImages/"$appimage"-AM.desktop
	rm -R -f ./squashfs-root ./DirIcon
}

_launcher_appimage_bin() {
	mkdir -p "$BINDIR"
	if ! echo "$PATH" | grep "$BINDIR" >/dev/null 2>&1; then
		echo "$DIVIDING_LINE"
		echo "WARNING: \"$BINDIR\" is not in PATH, apps may not run from command line." | _fit
		echo "$DIVIDING_LINE"
	fi
	read -r -p " Write a custom command to launch the app, or leave blank: " response
	if [ -z "$response" ]; then
		appimage_cmd=$(echo "$appimage" | tr '[:upper:]' '[:lower:]')
		if ! echo "$appimage" | grep -q -i ".appimage"; then
			[ -n "$BINDIR" ] && printf '#!/bin/sh\n%s' "$arg" >> "$BINDIR"/"$appimage_cmd".appimage \
			&& chmod a+x "$BINDIR"/"$appimage_cmd".appimage && echo "New command: \"$appimage_cmd.appimage\" in $BINDIR" | _fit
		else
			[ -n "$BINDIR" ] && printf '#!/bin/sh\n%s' "$arg" >> "$BINDIR"/"$appimage_cmd" \
			&& chmod a+x "$BINDIR"/"$appimage_cmd" && echo "New command: \"$appimage_cmd\" in $BINDIR" | _fit
		fi
	elif command -v "$response" 1>/dev/null; then
		if test -f "$BINDIR"/"$response"; then
			echo "WARNING, \"$BINDIR/$response\" already exists!" | _fit
			read -r -p " Do you Want to override it? (y,N) " yn
			if echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
				ln -sf "$arg" "$BINDIR"/"$response"
			else
				echo " You have chosen not to assign a command."
			fi
		else
			echo "ERROR: the \"$response\" command alredy exists, retry!" | _fit
		fi
	else
		[ -n "$BINDIR" ] && ln -sf "$arg" "$BINDIR"/"$response"
	fi
}

_launcher(){
	_clean_launchers 2>/dev/null 1>/dev/null
	if [ ! -f "$arg" ]; then
		echo "ERROR: \"$arg\" not found" | _fit
	elif ! grep -Eaoq -m 1 'github.com/AppImage/AppImageKit/wiki/FUSE' "$arg" 2>/dev/null; then
		echo "ERROR: \"$arg\" is NOT an AppImage" | _fit
	else
		printf " ◆ File: %s\n" "$arg"
		appimage=$(basename -- "$arg")
		mkdir -p "$DATADIR"/applications/AppImages
		mkdir -p "$DATADIR"/icons
		chmod a+x "$arg"
		cd "$(dirname "$arg")" || return

		_launcher_appimage_integration 2>/dev/null
		_launcher_appimage_bin
	fi
}

# LOCK/UNLOCK
_lock() {
		if [ ! -f "$argpath"/AM-updater ]; then
		echo " \"$AMCLIUPPER\" cannot manage updates for $2, \"AM-updater\" file not found!"
		return 1
	fi
	read -r -p " Do you wish to keep $2 at its current version? (y/N) " yn
	if echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
		mv "$argpath"/AM-updater "$argpath"/AM-LOCK 1>/dev/null
		echo " $2 has been locked at current version!"
	else
		echo " Operation aborted!"
		return 1
	fi
}

_unlock() {
		if ! test -f "$argpath"/AM-LOCK; then
		echo " \"$2\" cannot be unlocked, \"AM-LOCK\" file not found!"
		return 1
	fi
	read -r -p " Do you wish to unlock updates for $2? (Y/n) " yn
	if echo "$yn" | grep -i '^n' >/dev/null 2>&1; then
		echo " $2 is still locked at current version!"
		return 1
	else
		mv "$argpath"/AM-LOCK "$argpath"/AM-updater 1>/dev/null
		echo " \"$2\" can now receive updates!"
	fi
}

# NO LIBFUSE
_nolibfuse() {
	AMCLIPATH_ORIGIN="$AMCLIPATH"
	target="$(echo "${2}" | tr '[:lower:]' '[:upper:]')"
	# safety checks
	if ! cd "$argpath" 2>/dev/null; then
		echo " ⚠️ Error: \"$target\" is NOT installed."
		return 1
	fi
	if ! grep -Eaoq -m 1 'github.com/AppImage/AppImageKit/wiki/FUSE' ./"$arg" 2>/dev/null; then
		echo " ⚠️ Error: $target is NOT an AppImage."
		return 1
	elif file "$arg" 2>/dev/null | grep -qi "static"; then
		echo " ◆ $target is already a new generation AppImage."
		return 1
	elif test -f ./*.zsync; then
		echo "$DIVIDING_LINE"
		echo " Warning! Your AppImage uses \"zsync\" to update."
		echo " The .zsync file will be removed and will no longer work"
		echo " your \"AM-updater\" will likely still be able to update the AppImage"
		echo " by comparing the old vs new version url, but it is not guaranteed"
		read -r -p " Do you want to proceede anyway? (N/y) " yn
		if ! echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
				return 1
		fi
	fi

	# If appimagetool is not installed, download it
	if ! command -v appimagetool 1>/dev/null; then
		_online_check
		printf " ...downloading appimagetool\r"
		wget -q "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$ARCH.AppImage" -O ./appimagetool || return 1
		chmod a+x ./appimagetool
	fi

	# Extract the AppImage
	printf " ...extracting the AppImage\r"
	./"$2" --appimage-extract >/dev/null 2>&1 && chmod 0755 ./squashfs-root

	# Convert the AppImage to a new generation one
	printf " ...trying to convert in new generation AppImage\r"
	if [ -f ./appimagetool ]; then
		PATH="$PATH:$PWD" ARCH="$(uname -m)" ./appimagetool -n ./squashfs-root >/dev/null 2>&1
	elif command -v appimagetool 1>/dev/null; then
		ARCH="$(uname -m)" appimagetool -n ./squashfs-root >/dev/null 2>&1
	fi

	# Test if the AppImage has been created
	if ! test -f ./*.AppImage; then
		echo " 💀Error when trying to convert $target. Operation Aborted."
		rm -Rf ./appimagetool ./squashfs-root
		return 1
	fi

	mv ./"$2" ./"$2".old && mv ./*.AppImage ./"$2" || return 1
	echo " ◆ $target has been converted to a new generation AppImage."
	rm -Rf ./appimagetool ./squashfs-root ./*.zsync
	if [ -f ./AM-updater ] && ! grep -q 'nolibfuse' ./AM-updater; then
		sed -i "s/^else/	echo y | $AMCLIPATH_ORIGIN nolibfuse \"\$APP\"\n	notify-send \"\$APP has been converted too\!\"\nelse/g" ./AM-updater 2>/dev/null
		echo " The next update may replace this AppImage with a Type2 one"
		echo " so I added this command to the bottom of the \"AM-updater\" script!"
	fi
	echo " Contact the upstream developers to make them officially upgrade!"
	read -r -p " Do you wish to remove the old libfuse2 AppImage? (Y/n) " yn
	if echo "$yn" | grep -i '^n' >/dev/null 2>&1; then
		return 1
	else
		rm -f ./*.old
	fi
}

# REMOVE
_detect_path_to_remove() {
	if [ -f "$APPMAN_APPSPATH"/"$arg"/remove ]; then
		RMPATH="1"
	elif [ -f "$APPSPATH"/"$arg"/remove ]; then
		[ -w "$APPSPATH"/"$arg" ] && $SUDOCMD echo -e "\r" >/dev/null
		RMPATH="1"
	else
		RMPATH=""
	fi
	echo "$DIVIDING_LINE"
}

_remove() {
	[ "$AMCLI" = am ] && _detect_appman_apps
	_detect_path_to_remove
	[ -z "$RMPATH" ] && echo -e " \"${RED}$arg\033[0m\" is not a valid \"APPNAME\", see \"$AMCLI -f\" for more." && return 1
	read -r -p " ◆ Do you wish to remove \"$arg\"? (Y/n) " yn
	if echo "$yn" | grep -i '^n' >/dev/null 2>&1; then
		echo -e " \"${LightBlue}$arg\033[0m\" has not been removed!"
	else
		[ "$AMCLI" = am ] && "$APPMAN_APPSPATH"/"$arg"/remove 2>/dev/null || $SUDOCMD "$APPSPATH"/"$arg"/remove 2>/dev/null || return 1
		_clean_amcachedir
		sleep 0.1
		echo -e " \"${Green}$arg\033[0m\" has been removed!"
	fi
	[ -d "$DATADIR"/icons/hicolor/scalable/apps ] && find "$DATADIR"/icons/hicolor/scalable/apps -xtype l -exec rm {} \;
}

_hard_remove() {
	[ "$AMCLI" = am ] && _detect_appman_apps
	_detect_path_to_remove
	[ -z "$RMPATH" ] && echo -e " \"${RED}$arg\033[0m\" is not a valid \"APPNAME\", see \"$AMCLI -f\" for more." && return 1
	[ "$AMCLI" = am ] && "$APPMAN_APPSPATH"/"$arg"/remove 2>/dev/null || $SUDOCMD "$APPSPATH"/"$arg"/remove 2>/dev/null || return 1
	_clean_amcachedir
	sleep 0.1
	echo -e " \"${Green}$arg\033[0m\" has been removed!"
	[ -d "$DATADIR"/icons/hicolor/scalable/apps ] && find "$DATADIR"/icons/hicolor/scalable/apps -xtype l -exec rm {} \;
}

###########################################################################
# OPTIONS AVAILABLE IN THIS MODULE
###########################################################################

# Main logic
[ -z "$2" ] && echo " USAGE: $AMCLI $1 [ARGUMENT]" && exit 1
case "$1" in
	'backup'|'-b')
		# Do a snapshot of an installed app
		_determine_args
		entries="$(echo "$@" | cut -f2- -d ' ')"
		for arg in $entries; do
			argpath=$(echo "$ARGPATHS" | grep "/$arg$")
			_backup "${@}"
			shift
		done
		;;

	'downgrade'|'--rollback')
		# Downgrade the installed app to a previous version, from its online source
		_online_check
		_determine_args
		entries="$(echo "$@" | cut -f2- -d ' ')"
		for arg in $entries; do
			argpath=$(echo "$ARGPATHS" | grep "/$arg$")
			_downgrade "${@}"
			_clean_amcachedir
			_update_list_updatable_apps
			shift
		done
		;;

	'icons'|'--icons')
		# Place local AppImages into the menu by dragging and dropping them into the terminal
		_determine_args
		if [ "$2" = "--all" ]; then
			entries="$ARGS"
			read -r -p " ◆ Do you wish to allow custom icon theming for ALL the apps? (y,N) " yn
		else
			entries="$(echo "$@" | cut -f2- -d ' ')"
			read -r -p " ◆ Do you wish to allow custom icon theming for the selected apps? (y,N) " yn
		fi
		if echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
			for arg in $entries; do
				argpath=$(echo "$ARGPATHS" | grep "/$arg$")
				_icon_theme "${@}"
			done
			echo " ✔ Success!"
		else
			echo " ✖ Aborted!"
		fi
		;;

	'launcher'|'--launcher')
		# Place local AppImages into the menu by dragging and dropping them into the terminal
		entries="$(echo "$@" | cut -f2- -d ' ')"
		if [ "$2" = "-u" ] || [ "$2" = "-U" ] || [ "$2" = "update" ]; then
			entries="$(echo "$@" | cut -f3- -d ' ')"
			echo "$DIVIDING_LINE"
			_update_launchers
			printf "%b\n ◆ Updates check finished! \n" "$DIVIDING_LINE"
		fi
		for arg in $entries; do
			echo "$DIVIDING_LINE"
			_launcher "${@}"
		done
		echo "$DIVIDING_LINE"
		;;

	'lock')
		# Lock the version of an installed app
		_determine_args
		entries="$(echo "$@" | cut -f2- -d ' ')"
		for arg in $entries; do
			argpath=$(echo "$ARGPATHS" | grep "/$arg$")
			_lock "${@}"
			shift
		done
		;;

	'unlock')
		# Unlock the version of an installed app
		_determine_args
		entries="$(echo "$@" | cut -f2- -d ' ')"
		for arg in $entries; do
			argpath=$(echo "$ARGPATHS" | grep "/$arg$")
			_unlock "${@}"
			shift
		done
		;;

	'nolibfuse')
		# Convert old AppImages to a new standard and get rid of libfuse2 dependency
		_determine_args
		entries="$(echo "$@" | cut -f2- -d ' ')"
		for arg in $entries; do
			argpath=$(echo "$ARGPATHS" | grep "/$arg$")
			_nolibfuse "${@}"
			_remove_info_files
			shift
		done
		;;

	'overwrite'|'-o')
		# Restore an app to a previous version using a snapshot (see "backup" or "-b", above)
		_determine_args
		entries="$(echo "$@" | cut -f2- -d ' ')"
		for arg in $entries; do
			argpath=$(echo "$ARGPATHS" | grep "/$arg$")
			_overwrite "${@}"
			_remove_info_files
			shift
		done
		;;

	'remove'|'-r')
		# Remove apps
		entries="$(echo "$@" | cut -f2- -d ' ')"
		for arg in $entries; do
			_remove "${@}"
			_remove_info_files
			shift
		done
		echo "$DIVIDING_LINE"
		;;

	'-R')
		# Remove apps without confirmation
		entries="$(echo "$@" | cut -f2- -d ' ')"
		for arg in $entries; do
			_hard_remove "${@}"
			_remove_info_files
			shift
		done
		echo "$DIVIDING_LINE"
		;;
esac
