#!/bin/sh
# shellcheck disable=SC3043

set -e

# Disable yash POSIXly-correct mode
# Dash can't handle 'set +o posixly-correct' so we check first
_set_code="$(set -o | grep posixly-correct2>/dev/null || echo $?)"
if [ "$_set_code" = 0 ]; then
	# shellcheck disable=SC3040
	set +o posixly-correct 2>/dev/null
fi

trap 'rm -rf $TMPDIR || :' EXIT

TMPDIR=$(mktemp -d)
CACHE="${XDG_CACHE_HOME:-$HOME/.cache}/coldbrew"
DATA="${COLDBREW_DATA:-${XDG_DATA_HOME:-$HOME/.local/share}/coldbrew}"

usage() {
	printf 'Usage: coldbrew [-e VERSION] [-c] {install,remove,run,clear,wrap,ls,upgrade,outdated} PKG|TOOL\n'
	printf '    -e VERSION: use specific Alpine version (e.g. edge, 3.21, etc)\n'
	printf '    -m MIRROR : Alpine mirror URL (default: http://dl-cdn.alpinelinux.org/alpine)\n'
	printf '    -c        : run the tool outside of the chroot, it may fail to find configs\n'
}

if ! command -v bwrap >/dev/null; then
	printf 'bubblewrap not found!\n'
	exit 1
fi

VERSION=edge
TOOL_CHROOT=true
MIRROR="https://dl-cdn.alpinelinux.org/alpine"

while getopts ":e:m:ch" opt; do
	case $opt in
	e)
		VERSION=$OPTARG
		;;
	m)
		MIRROR=$OPTARG
		;;
	c)
		TOOL_CHROOT=false
		;;
	h)
		usage
		exit 0
		;;
	*)
		usage
		exit 1
		;;
	esac
done
shift $((OPTIND - 1))

mkdir -p "$CACHE/apk-cache"
ROOTFS="$DATA/alpine-root-$VERSION"
mkdir -p "$ROOTFS"

# Show an ascii spinner while a background process runs.
# $1: pid of process to monitor
# $2: message to show before the spinner
spinner() {
	pid=$1
	msg=$2
	local i=0
	local frames="|/-\\"
	# check if pid exists
	while kill -0 "$pid" 2>/dev/null; do
		# get current frame char by position
		frame=$(printf '%s' "$frames" | cut -c$((i+1)))
		printf '\r%s %s' "$msg" "$frame"
		i=$(( (i + 1) % 4 ))
		sleep 0.2
	done
	printf '\r%s done\n' "$msg"
}

install_apk() {
	local ver

	# Find the package version in the APKBUILD
	ver=$(wget -q -O - "$MIRROR/edge/main/$(uname -m)/APKINDEX.tar.gz" | tar -xzO APKINDEX | grep -A 1 apk-tools-static | tail -n +2 | cut -d":" -f2)
	printf 'Installing apk.static version %s\n' "$ver"
	wget -q -O "$TMPDIR/apk-tools-static-${ver}.tar.xz" "$MIRROR/edge/main/$(uname -m)/apk-tools-static-${ver}.apk"
	tar -C "$DATA" -xzf "$TMPDIR/apk-tools-static-${ver}.tar.xz" sbin/apk.static
}

if [ -f "$DATA/sbin/apk.static" ]; then
	APK="$DATA/sbin/apk.static"
fi
if [ -z "$APK" ]; then
	install_apk
	APK="$DATA/sbin/apk.static"
fi

sbox() {
	bwrap "$@"
}

run() {
	sbox \
		--bind / / \
		--dev-bind /dev "$ROOTFS/dev" \
		--proc "$ROOTFS/proc" \
		-- "$@"
}

run_chroot() {
	sbox \
		--bind "$ROOTFS" / \
		--dev-bind /dev /dev \
		--proc /proc \
		--bind /sys /sys \
		--bind /home /home \
		--bind /run /run \
		--bind /tmp /tmp \
		--ro-bind /etc/passwd /etc/passwd \
		--ro-bind /etc/group /etc/group \
		--setenv PATH "/bin:/usr/bin:/sbin:/usr/sbin:$PATH" \
		-- "$@"
}

run_no_chroot() {
	sbox \
		--ro-bind "$ROOTFS" "$ROOTFS" \
		--bind / / \
		--dev-bind /dev /dev \
		--proc /proc \
		--bind /home /home \
		--setenv LD_LIBRARY_PATH "$ROOTFS/lib:$ROOTFS/usr/lib" \
		--setenv PATH "$ROOTFS/bin:$ROOTFS/usr/bin:$ROOTFS/sbin:$ROOTFS/usr/sbin" \
		-- "$@"
}

get_repos() {
	local version="$1"

	if [ "$version" = "edge" ]; then
		echo "--repository $MIRROR/edge/main"
		echo "--repository $MIRROR/edge/community"
		echo "--repository $MIRROR/edge/testing"
	else
		echo "--repository $MIRROR/v$version/main"
		echo "--repository $MIRROR/v$version/community"
	fi
}

apk() {
	local cmd
	cmd="$1"
	shift

	# shellcheck disable=SC2046
	run "$APK" "$cmd" \
		$(get_repos "$VERSION") \
		--cache-dir "$CACHE/apk-cache" \
		--no-interactive \
		--root "$ROOTFS" \
		"$@" 3>&1
}

# Install a wrapper script that runs the given program inside the coldbrew sandbox
wrap_tool() {
	local tool
	tool=$(basename "$1")
	if [ -f "$HOME/.local/bin/$tool" ]; then
		# only prompt if not already a coldbrew wrapper
		if ! grep -q "##COLDBREW_WRAPPER##" "$HOME/.local/bin/$tool"; then
			printf '%s already exists!\n' "$HOME/.local/bin/$tool"
			printf 'Recreate it? [y/N] '
			read -r response
			case "$response" in
				[Yy]*) ;;
				*) exit 0 ;;
			esac
		fi
	fi

	mkdir -p "$HOME/.local/bin"

	cat <<- EOF > "$HOME/.local/bin/$tool"
	#!/bin/sh
	##COLDBREW_WRAPPER##

	if [ "\$(id -u)" -eq 0 ]; then
		export COLDBREW_DATA="$DATA"
	fi

	exec "$(readlink -f "$0")" run "$tool" "\$@"
	EOF
	chmod +x "$HOME/.local/bin/$tool"
}

apk_install() {
	local tool

	tool="$1"
	apk add --no-script "$tool" >/dev/null &
	spinner $! "Installing $tool..."
	wait $!
	for bin in $(apk info -L "$tool" | grep -E "^(bin|sbin|usr/bin|usr/sbin)/"); do
		wrap_tool "$bin"
		printf 'Installed binary /%s\n' "$bin"
	done
}

coldbrew_cmd=$1
if [ $# -gt 1 ]; then
	tool=$2
	shift 2
	# $@ now contains args to pass to the tool
fi

case $coldbrew_cmd in
clear)
	# Clear with no args to clear the rootfs
	if [ -z "$tool" ]; then
		printf 'This will destroy the Alpine rootfs!\nContinue? [y/N] '
		read -r response
		case "$response" in
			[Yy]*) ;;
			*) exit 0 ;;
		esac
		printf 'Destroying Alpine rootfs!\n'
		[ -n "$ROOTFS" ] && rm -rf "$ROOTFS"

		# Find the wrapper scripts which have the ##COLDBREW_WRAPPER## indicator
		wrapped_tools="$(grep -Rl "##COLDBREW_WRAPPER##" ~/.local/bin/ | grep -v "/coldbrew$")"
	# Clear the wrapper for the named tool
	else
		wrapped_tools="$HOME/.local/bin/$tool"
		if [ -z "$wrapped_tools" ]; then
			printf 'No wrapper found for %s!\n' "$tool"
			exit 1
		fi
	fi

	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
	;;
# Commands that require root is set up
*)
	if ! [ -f "$ROOTFS/etc/os-release" ]; then
		apk add --no-script --initdb --usermode --allow-untrusted alpine-keys >/dev/null &
		spinner $! "Initializing base rootfs..."
		wait $!
		apk add --no-script alpine-base >/dev/null &
		spinner $! "Setting up base rootfs..."
		wait $!
		get_repos "$VERSION" | cut -d" " -f2 > "$ROOTFS/etc/apk/repositories"
	fi

	case $coldbrew_cmd in
	install|add)
		apk_install "$tool"
		;;
	remove|del)
		for bin in $(apk info -L "$tool" | grep -E "^(bin|sbin|usr/bin|usr/sbin)/"); do
			# only remove coldbrew wrappers
			grep -q "##COLDBREW_WRAPPER##" "$HOME/.local/bin/$(basename "$bin")" || continue
			rm -f "$HOME/.local/bin/$(basename "$bin")"
		done
		apk del "$tool" >/dev/null &
		spinner $! "Uninstalling $tool..."
		wait $!
		;;
	run)
		if $TOOL_CHROOT; then
			bin="$tool"
			run_chroot "$bin" "$@"
		else
			bin=$(find "$ROOTFS/bin" "$ROOTFS/sbin" "$ROOTFS/usr/bin" "$ROOTFS/usr/sbin" -not -type d -name "$tool" | head -n1)
			if [ -z "$bin" ]; then
				printf 'Tool %s not found! Is it installed?\n' "$tool"
				exit 1
			fi
			run_no_chroot "$bin" "$@"
		fi
		;;
	wrap)
		wrap_tool "$tool"
		;;
	search)
		apk search "$tool"
		;;
	upgrade)
		apk upgrade -a
		;;
	ls)
		progs="$(cat "$ROOTFS/etc/apk/world" | grep -ve "\(alpine-base\|alpine-keys\)")"
		printf 'Installed tools:\n'
		# shellcheck disable=SC2086
		printf ' * %s\n' $progs
		;;
	outdated)
		apk version
		;;
	*)
		usage
		exit 1
		;;
	esac
esac

