#!/bin/sh
# Copyright (C) 2026 Aitor <aitor@genuen.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.

set -e

S6RC_SV="${S6RC_SV:-/etc/s6-rc/sv}"
S6RC_COMPILED="${S6RC_COMPILED:-/etc/s6-rc/compiled}"
S6RC_LIVE="${S6RC_LIVE:-/run/s6-rc}"

is_s6() {
	[ -d "${S6RC_LIVE}" ] && [ -d /run/service ]
}

have_cmd() {
	command -v "$1" >/dev/null 2>&1
}

run_or_error() {
	if ! "$@"; then
		eval "${ERROR_HANDLER:-exit 1}"
	fi
}

policy_allows() {
	local action="$1"

	if [ ! -x /usr/sbin/policy-rc.d ]; then
		return 0
	fi
	set +e
	/usr/sbin/policy-rc.d "${SCRIPT}" "${action}"
	local rc=$?
	set -e
	if [ "$rc" = 101 ]; then
		echo "s6-helper: ${SCRIPT}: ${action} action denied by policy-rc.d" >&2
		return 1
	fi
	return 0
}

scrub_bundle_membership() {
	local bundle_dir="${S6RC_SV}/${1}/contents.d"

	if [ ! -d "$bundle_dir" ]; then
		return 0
	fi
	rm -f "${bundle_dir}/${SCRIPT}" 2>/dev/null || true
}

scrub_all_bundles() {
	local bundle

	for bundle in "${S6RC_SV}"/*/; do
		[ -d "$bundle" ] || continue
		scrub_bundle_membership "$(basename "$bundle")"
	done
}

configure_bundle() {
	if [ -z "${BUNDLE:-}" ]; then
		return 0
	fi
	if [ ! -d "${S6RC_SV}/${BUNDLE}" ]; then
		echo "warning: s6-helper: bundle ${BUNDLE} not found under ${S6RC_SV}" >&2
		return 0
	fi
	mkdir -p "${S6RC_SV}/${BUNDLE}/contents.d"
	: > "${S6RC_SV}/${BUNDLE}/contents.d/${SCRIPT}"
}

recompile_and_update() {
	local staging live_db prev_boot_db

	if [ -x /etc/s6-rc/recompile ]; then
		run_or_error /etc/s6-rc/recompile
		return 0
	fi

	if ! have_cmd s6-rc-compile; then
		echo "warning: s6-helper: s6-rc-compile not available, skipping database rebuild" >&2
		return 0
	fi

	staging="${S6RC_COMPILED}.build.$$"
	rm -rf "$staging"
	run_or_error s6-rc-compile "$staging" "$S6RC_SV"

	if is_s6 && have_cmd s6-rc-update; then
		live_db=""
		if [ -e "${S6RC_LIVE}/compiled" ]; then
			live_db="$(readlink -f "${S6RC_LIVE}/compiled" 2>/dev/null || true)"
		fi
		run_or_error s6-rc-update -v2 -l "$S6RC_LIVE" "$staging"
		prev_boot_db=""
		if [ -e "$S6RC_COMPILED" ]; then
			prev_boot_db="$(readlink -f "$S6RC_COMPILED" 2>/dev/null || true)"
		fi
		rm -f "$S6RC_COMPILED"
		ln -sfn "$staging" "$S6RC_COMPILED"
		if [ -n "$prev_boot_db" ] && [ "$prev_boot_db" != "$staging" ] && [ "$prev_boot_db" != "$live_db" ]; then
			rm -rf "$prev_boot_db"
		fi
	else
		rm -rf "$S6RC_COMPILED"
		mv "$staging" "$S6RC_COMPILED"
	fi
}

start_service() {
	if ! is_s6 || ! have_cmd s6-rc; then
		return 0
	fi
	if ! policy_allows start; then
		return 0
	fi
	run_or_error s6-rc -v2 -up change "$SCRIPT"
}

stop_service() {
	if ! is_s6 || ! have_cmd s6-rc; then
		return 0
	fi
	if ! policy_allows stop; then
		return 0
	fi
	run_or_error s6-rc -v2 -down change "$SCRIPT"
}

restart_service() {
	if ! is_s6 || ! have_cmd s6-rc; then
		return 0
	fi
	if ! policy_allows restart; then
		return 0
	fi
	run_or_error s6-rc -v2 -down change "$SCRIPT"
	run_or_error s6-rc -v2 -up change "$SCRIPT"
}

preinst_chmod() {
	local action="${1}"

	if [ "$action" = install ] && [ -n "${2:-}" ] && [ -e "/etc/init.d/${SCRIPT}" ]; then
		chmod +x "/etc/init.d/${SCRIPT}" >/dev/null || true
	fi
}

preinst_stop() {
	local action="${1}"

	if [ "$action" = upgrade ]; then
		stop_service
	fi
}

postinst() {
	local action="${1}"

	case "$action" in
		configure|abort-upgrade|abort-deconfigure|abort-remove)
			;;
		*)
			return 0
			;;
	esac

	scrub_all_bundles
	configure_bundle
	recompile_and_update
	start_service
}

postinst_restart() {
	local action="${1}"
	local previous="${2:-}"

	case "$action" in
		configure|abort-upgrade|abort-deconfigure|abort-remove)
			;;
		*)
			return 0
			;;
	esac

	scrub_all_bundles
	configure_bundle
	recompile_and_update
	if [ -n "$previous" ]; then
		restart_service
	else
		start_service
	fi
}

postinst_nostart() {
	local action="${1}"

	case "$action" in
		configure|abort-upgrade|abort-deconfigure|abort-remove)
			;;
		*)
			return 0
			;;
	esac

	scrub_all_bundles
	configure_bundle
	recompile_and_update
}

prerm_norestart() {
	local action="${1}"

	if [ "$action" = remove ]; then
		stop_service
	fi
}

prerm() {
	stop_service
}

postrm() {
	local action="${1}"

	if [ "$action" = remove ] && [ -x "/etc/init.d/${SCRIPT}" ]; then
		chmod -x "/etc/init.d/${SCRIPT}" >/dev/null || true
	fi

	if [ "$action" != remove ] && [ "$action" != purge ]; then
		return 0
	fi

	scrub_all_bundles
	recompile_and_update
}

"$@"
