#!/bin/sh -e
# shellcheck disable=SC3043

DATA="/var/lib/coldbrew"
TOOLSDIR="$DATA"/wrappers
export COLDBREW_DATA="$DATA"

PATH="$PATH:$TOOLSDIR"
ROOTFS="$DATA/coldbrew"

require_root() {
	if [ "$(id -u)" != 0 ]; then
		echo "cbp must be run as root"
		exit 1
	fi
	# always make sure this dir exists when running root cmds
	mkdir -p "$DATA"
}

install_usage() {
	cat <<- EOF
	Usage: cbp install [packages]...

	Local wrappers are automatically generated for CLI tools in the installed packages.
	Services provided by the packages are detected and you will be prompted to attach them.

	Running install multiple times will add additional tools/services and automatically
	run portablectl reattach to pick up the new services.

	You can clear the portable services by stopping all services, running
	portablectl detach coldbrew and then deleting $ROOTFS
	EOF
}

usage() {
    cat <<-EOF
	Usage: cbp {install,run,update,attach,detach,clear} [options]
		install  <packages>...                          Install packages and create wrappers
		run      <tool> [args...]                       Run a tool inside coldbrew
		update                                          Update the rootfs
		attach                                          Attach portable services
		detach                                          Detach portable services
		clear                                           Remove tool wrappers
	EOF
}

wrap_tool() {
	local tool
	tool=$(basename "$1")
	if [ -f "$TOOLSDIR/$tool" ]; then
		# only prompt if not already a coldbrew wrapper
		if ! grep -q "##CBP_WRAPPER##" "$TOOLSDIR/$tool"; then
			printf '%s already exists!\n' "$TOOLSDIR/$tool"
			printf 'Recreate it? [y/N] '
			read -r response
			case "$response" in
				[Yy]*) ;;
				*) exit 0 ;;
			esac
		fi
	fi

	mkdir -p "$TOOLSDIR"
	cat <<-EOF > "$TOOLSDIR/$tool"
	#!/bin/sh
	##CBP_WRAPPER##

	exec "$(readlink -f "$0")" run "$tool" "\$@"
	EOF
	chmod +x "$TOOLSDIR/$tool"
}

generate_env_dropin() {
	local svc="$1"
	local unit="/etc/systemd/system.attached/coldbrew-$svc"
	local dropdir="/etc/systemd/system/coldbrew-$svc.d"
	local envfiles

	envfiles=$(grep '^EnvironmentFile=' "$unit" 2>/dev/null) || return 0

	mkdir -p "$dropdir"
	# Reset EnvironmentFile from the original unit, then re-add each
	# path with the cbp rootfs version as default and host rootfs as override
	printf '[Service]\nEnvironmentFile=\n' > "$dropdir/30-coldbrew-env.conf"

	echo "$envfiles" | while IFS= read -r line; do
		path="${line#EnvironmentFile=}"
		# strip any leading "-" prefix since it gets added below and we don't want to
		# add it twice
		path="${path#-}"
		# cbp rootfs
		printf 'EnvironmentFile=-%s%s\n' "$ROOTFS" "$path" >> "$dropdir/30-coldbrew-env.conf"
		# host rootfs
		printf 'EnvironmentFile=-%s\n' "$path" >> "$dropdir/30-coldbrew-env.conf"
	done
}

refresh_portable() {
	cmd="reattach"
	if [ "$(portablectl is-attached coldbrew 2>/dev/null)" = "detached" ]; then
		cmd="attach"
	fi

	if ls "$ROOTFS"/usr/lib/systemd/system/coldbrew-*.service >/dev/null 2>&1; then
		portablectl "$cmd" --profile=trusted --enable "$ROOTFS"
		for unit in /etc/systemd/system.attached/coldbrew-*.service; do
			[ -f "$unit" ] || continue
			svc="${unit##*/coldbrew-}"
			generate_env_dropin "$svc"
		done
	else
		portablectl detach coldbrew
	fi

	systemctl daemon-reload
}

do_install() {
	# Make sure we get -systemd subpackages
	local packages="$*"

	if [ -z "$packages" ]; then
		install_usage
		exit 1
	fi

	while [ $# -gt 0 ]; do
		case $1 in
		-h)
			install_usage
			exit 0
			;;
		*)
			# All other arguments are packages (already captured in $packages)
			;;
		esac
		shift 1
	done

	# Install a virtual systemd package so that systemd subpackages
	# will automatically be installed
	coldbrew -r "$ROOTFS" -n install --virtual systemd

	# snapshot units before installing so we only prompt for units added
	# by this transaction
	unitdir="$ROOTFS/usr/lib/systemd/system"
	old_services=""
	if [ -d "$unitdir" ]; then
		for svc in "$unitdir"/*.service; do
			[ -f "$svc" ] || continue
			old_services="$old_services $(basename "$svc")"
		done
	fi

	# shellcheck disable=SC2086
	coldbrew -r "$ROOTFS" -n install $packages

	echo "Configuring service files and tool wrappers"

	cd "$ROOTFS"/usr/lib/systemd/system

	# discover new services
	new_services=""
	for svc in *.service; do
		[ -f "$svc" ] || continue
		# skip anything present before this install
		case " $old_services " in
			*" $svc "*) continue ;;
		esac
		new_services="$new_services $svc"
	done

	if [ -n "$new_services" ]; then
		echo "Found services:"
		for svc in $new_services; do
			echo "	$svc"
		done
		printf 'Attach? [a]ll / [n]one / [s]elect: '
		read -r action

		case "$action" in
			[Aa])
				# attach all
				for svc in $new_services; do
					case "$svc" in
						coldbrew-*) ;;
						*) ln -sf "$svc" "coldbrew-$svc" ;;
					esac
				done
				;;
			[Nn])
				# attach none
				;;
			[Ss])
				# select individually
				for svc in $new_services; do
					printf '%s? [Y/n] ' "$svc"
					read -r response
					case "$response" in
						[Nn]*) ;;
						*)
							case "$svc" in
								coldbrew-*) ;;
								*) ln -sf "$svc" "coldbrew-$svc" ;;
							esac
							;;
					esac
				done
				;;
			*)
				echo "Invalid option. Attaching none."
				;;
		esac
	fi

	cd - >/dev/null

	# only wrap binaries from explicitly requested packages
	binaries=""
	for pkg in $packages; do
		binaries="$binaries $(coldbrew -r "$ROOTFS" run apk info -L "$pkg" | grep -E "^(bin|sbin|usr/bin|usr/sbin)/")"
	done
	for bin in $binaries; do
		wrap_tool "$bin"
	done

	if ls "$ROOTFS"/usr/lib/systemd/system/coldbrew-*.service >/dev/null 2>&1; then
		echo "Attaching and enabling portable services!"
		refresh_portable
	else
		echo "No services attached."
	fi
}

do_remove() {
	local pkg="$1"

	if [ -z "$pkg" ]; then
		echo "Usage: cbp remove <package>"
		exit 1
	fi

	# remove tool wrappers for the pkg before apk del
	for bin in $(coldbrew -r "$ROOTFS" run apk info -L "$pkg" | grep -E "^(bin|sbin|usr/bin|usr/sbin)/"); do
		tool="$(basename "$bin")"
		[ -f "$TOOLSDIR/$tool" ] || continue
		grep -q "##CBP_WRAPPER##" "$TOOLSDIR/$tool" || continue
		rm -f "$TOOLSDIR/$tool"
	done

	coldbrew -r "$ROOTFS" -n remove "$pkg"

	# stop and clean up broken coldbrew-*.service symlinks
	for link in "$ROOTFS"/usr/lib/systemd/system/coldbrew-*.service; do
		[ -L "$link" ] && [ ! -e "$link" ] || continue
		svc="$(basename "$link")"
		systemctl stop "$svc" 2>/dev/null || true
		rm -rf "/etc/systemd/system/$svc.d" 2>/dev/null
		rm -f "$link"
	done

	if ls "$ROOTFS"/usr/lib/systemd/system/coldbrew-*.service >/dev/null 2>&1; then
		refresh_portable
	else
		portablectl detach coldbrew
		systemctl daemon-reload
	fi
}

do_run() {
	local tool

	tool="$1"
	shift

	coldbrew -r "$ROOTFS" run "$tool" "$@"
}

show_cmd() {
	echo "\$ $*"
	eval '"$@"'
}

do_detach() {
	local services
	local svc
	local response
	local wrapped_tools

	echo "Detaching coldbrew portable services!"

	cd "$ROOTFS"/usr/lib/systemd/system

	echo "This will stop the following services:"
	for svc in $(find . -name "coldbrew-*" | cut -d/ -f2-); do
		echo "* $svc"
		services="$services $svc"
	done

	printf 'Stop all coldbrew portable services and detach? [y/N] '
	read -r response
	case "$response" in
		[Yy]*) ;;
		*) exit 0 ;;
	esac

	echo "Stopping services..."
	for svc in $services; do
		show_cmd sudo systemctl stop "$svc"
	done

	echo "Detaching portable services"
	show_cmd sudo portablectl detach coldbrew
}

cmd="$1"
shift 1

case "$cmd" in
install)
	require_root
	do_install "$@"
	;;
remove)
	require_root
	do_remove "$@"
	;;
run)
	# tool service
	do_run "$@"
	;;
update|upgrade)
	require_root
	coldbrew -r "$ROOTFS" upgrade
	;;
attach)
	portablectl attach --profile=trusted --enable "$ROOTFS"
	;;
detach)
	require_root
	do_detach
	;;
clear)
	require_root
	# Clear wrapped tools
	wrapped_tools="$(grep -Rl "##CBP_WRAPPER##" "$TOOLSDIR" 2>/dev/null)"

	if [ -n "$wrapped_tools" ]; then
		printf 'Found tool wrappers:\n'
		# shellcheck disable=SC2086
		printf ' * %s\n' $wrapped_tools
		printf 'Remove them? [y/N] '
		read -r response
		case "$response" in
			[Yy]*) ;;
			*) exit 0 ;;
		esac
		for tool in $wrapped_tools; do
			rm "$tool"
		done
	fi
	;;
*)
    usage
    exit 1
    ;;
esac
