#!/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'
	printf "    -n        : dont install CLI wrappers\n"
	printf "    -r ROOTFS : use specified rootfs path\n"
}

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

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

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

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

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() {
	# /home is a symlink to /var/home on bootc systems
	# The dbus socket might be in /tmp instead of /run
	local dbus_socket

	dbus_socket="$(echo "$DBUS_SESSION_BUS_ADDRESS" | cut -d= -f2)"
	if [ -z "$dbus_socket" ]; then
		dbus_socket="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/bus"
	fi

	sbox \
		--bind "$ROOTFS" / \
		--dev-bind /dev /dev \
		--proc /proc \
		--bind /sys /sys \
		--bind-try /var/home /var/home \
		--bind-try "$dbus_socket" "$dbus_socket" \
		--bind-try /etc/resolv.conf /etc/resolv.conf \
		--bind /home /home \
		--bind /run /run \
		--tmpfs /tmp \
		--ro-bind /etc/passwd /etc/passwd \
		--ro-bind /etc/group /etc/group \
		--die-with-parent \
		--setenv PATH "/bin:/usr/bin:/sbin:/usr/sbin:$PATH" \
		--chdir "$PWD" \
		-- "$@"
}

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

	# include the progress flag for apk cmds that support it
	case "$cmd" in
	add|upgrade|del) progress="--progress" ;;
	*) progress="" ;;
	esac

	# shellcheck disable=SC2046
	run "$APK" "$cmd" \
		$(get_repos "$VERSION") \
		--cache-dir "$CACHE/apk-cache" \
		--no-interactive \
		--root "$ROOTFS" \
		"$progress" \
		"$@" 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 args
	local packages

	# Separate extra args to apk add so we don't also
	# pass them to apk info
	while [ $# -gt 0 ]; do
		case $1 in
		-*)
			args="$args $1"
			;;
		*)
			packages="$packages $1"
			;;
		esac

		shift 1
	done

	printf 'Installing: %s\n' "$packages"

	# Avoid globbing and preserve quoting
	eval "set -- $args $packages"
	apk add --no-script "$@"

	if $NO_WRAP; then
		return
	fi

	# Avoid globbing and preserve quoting
	eval "set -- $packages"
	for bin in $(apk info -L "$@" | grep -E "^(bin|sbin|usr/bin|usr/sbin)/"); do
		wrap_tool "$bin"
		printf 'Installed binary /%s\n' "$bin"
	done
}

# if no arguments at all are passed then just show usage
if [ "$#" -eq 0 ]; then
	usage
	exit 1
fi

coldbrew_cmd=$1
# remove $coldbrew_cmd from list
shift 1

case $coldbrew_cmd in
clear)
	# Clear with no args to clear the rootfs
	if [ -z "$*" ]; 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 tools
	else
		wrapped_tools=""
		for tool in "$@"; do
			wrapped=$HOME/.local/bin/$tool
			if [ ! -f "$wrapped" ]; then
				printf 'No wrapper found for %s!\n' "$tool"
			else
				wrapped_tools="$wrapped_tools $wrapped"
			fi
		done
	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
		printf 'Initializing base rootfs: %s \n' "$ROOTFS"
		apk_args="--no-script --initdb"
		test "$(id -u)" = "0" || apk_args="$apk_args --usermode"
		eval "apk add $apk_args --allow-untrusted alpine-keys"
		printf 'Setting up base rootfs...\n'
		apk add --no-script alpine-baselayout alpine-release apk-tools
		get_repos "$VERSION" | cut -d" " -f2 > "$ROOTFS/etc/apk/repositories"

		# Ensure /var/home mountpoint is there if needed
		mkdir -p "$ROOTFS"/var/home

		# Set up user /run dir for cbp
		if [ "$(id -u)" = "0" ]; then
			rundir="$ROOTFS"/run/user/"$(id -u "$SUDO_USER")"
			mkdir -p "$rundir"
			chown "$SUDO_USER":"$SUDO_GID" "$rundir"
		fi
	fi

	case $coldbrew_cmd in
	install|add)
		if [ "$#" -eq 0 ]; then
			usage
			exit 1
		fi
		apk_install "$@"
		;;
	remove|del)
		if [ "$#" -eq 0 ]; then
			usage
			exit 1
		fi
		if ! $NO_WRAP; then
			for bin in $(apk info -L "$@" | 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
		fi
		printf 'Uninstalling: %s\n' "$*"
		apk del "$@"
		;;
	run)
		if [ "$#" -eq 0 ]; then
			usage
			exit 1
		fi
		if $TOOL_CHROOT; then
			run_chroot "$@"
		else
			tool="$1"
			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 "$@"
		fi
		;;
	wrap)
		if [ "$#" -eq 0 ]; then
			usage
			exit 1
		fi
		# for each tool passed, we wrap individually
		for tool in "$@"; do
			wrap_tool "$tool"
		done
		;;
	search)
		if [ "$#" -eq 0 ]; then
			usage
			exit 1
		fi
		apk search "$@"
		;;
	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
