#!/bin/sh -e
SERVICE_NAME="${__object_id}"

OS="$(cat "${__global}/explorer/os")"

case "${OS}" in
	debian|devuan)
		SUPER_USER_GROUP=root
		ETC_DIR="/etc"
	;;
	*bsd)
		SUPER_USER_GROUP=wheel
		ETC_DIR="/usr/local/etc"
	;;
	*)
		echo "Your OS '${OS}' is currently not supported." >&2
		exit 1
	;;
esac
INIT="$(cat "${__global}/explorer/init")"

case "${INIT}" in
	systemd)
		service_definition_require="__systemd_unit/${SERVICE_NAME}.service"
		service_command="service ${SERVICE_NAME} %s"
	;;
	runit|sysvinit)
		# We will use runit to manage these services
		__runit
		export require="__runit"
		service_definition_require="__runit_service/${SERVICE_NAME}"
		service_command="sv %s ${SERVICE_NAME}"
	;;
	*)
		echo "Init system ${INIT}' is currently not supported." >&2
		exit 1
	;;
esac

BIN_DIR="/usr/local/bin"

# Ensure the target bin dir exists
#   Care, we never want to remove it :-D
__directory "${BIN_DIR}" \
	--state "exists" \
	--mode 0755
export require="${require} __directory${BIN_DIR}"

STATE="$(cat "${__object}/parameter/state")"
USER="$(cat "${__object}/parameter/user")"
GROUP="$(cat "${__object}/parameter/group" 2>/dev/null || true)"
if [ -z "${GROUP}" ]; then
	if [ "${USER}" != "root" ]; then
		GROUP="${USER}"
	else
		GROUP="${SUPER_USER_GROUP}"
	fi
fi


BINARY="$(cat "${__object}/parameter/binary" 2>/dev/null || true)"
if [ -z "${BINARY}" ]; then
	BINARY="${SERVICE_NAME}"
fi
EXTRA_BINARIES="$(cat "${__object}/parameter/extra-binary" 2>/dev/null || true)"
# This only makes sense for file archives
if [ -n "${EXTRA_BINARIES}" ] && [ -f "${__object}/parameter/unpack" ]; then
	cat >&2 <<-EOF
	You cannot specify extra binaries without the --unpack argument.
	Make sure that the --url argument points to a file archive.
EOF
fi

SERVICE_EXEC="$(cat "${__object}/parameter/service-exec" 2>/dev/null || true)"
if [ -z "${SERVICE_EXEC}" ]; then
	SERVICE_EXEC="${BIN_DIR}/${BINARY}"
fi
SERVICE_ARGS="$(cat "${__object}/parameter/service-args")"
SERVICE_EXEC="${SERVICE_EXEC} ${SERVICE_ARGS}"

SERVICE_DESCRIPTION="$(cat "${__object}/parameter/service-description" \
	2>/dev/null || true)"
if [ -z "${SERVICE_DESCRIPTION}" ]; then
	SERVICE_DESCRIPTION="cdist-managed '${SERVICE_NAME}' service"
fi

SERVICE_DEFINITION="$(cat "${__object}/parameter/service-definition" 2>/dev/null || true)"

WORKING_DIRECTORY_PATH="$(cat "${__object}/parameter/working-directory" 2>/dev/null || true)"
if [ -n "${WORKING_DIRECTORY_PATH}" ]; then
	WORKING_DIRECTORY_SYSTEMD="WorkingDirectory=${WORKING_DIRECTORY_PATH}"
	WORKING_DIRECTORY_RUNIT="cd '${WORKING_DIRECTORY_PATH}'"
fi

DOWNLOAD_URL="$(cat "${__object}/parameter/url")"
CHECKSUM="$(cat "${__object}/parameter/checksum")"
SHOULD_VERSION="$(cat "${__object}/parameter/version")"

# Create a user for the service if it is not root
USER_HOME_DIR="/root"
if [ "${USER}" != "root" ] && \
		[ ! -f "${__object}/parameter/do-not-manage-user" ]; then
	if [ "${STATE}" = "absent" ]; then
		# When removing, ensure user is not being used
		user_require="${service_definition_require}"
	fi
	USER_HOME_DIR="$(cat "${__object}/parameter/user-home-dir")"
	if [ "${USER_HOME_DIR}" != "/nonexistent" ]; then
		USER_CREATE_HOME="--create-home"
	fi
	require="${require} ${user_require}" __user "${USER}" \
		--system \
		--state "${STATE}" \
		--home "${USER_HOME_DIR}" \
		--comment "cdist-managed service user" \
		${USER_CREATE_HOME}
	# Track dependencies
	service_require="${service_require} __user/${USER}"
fi

# Place config file if necessary
CONFIG_FILE_DEST="${ETC_DIR}/${SERVICE_NAME}.conf"
CONFIG_FILE_SOURCE="$(cat "${__object}/parameter/config-file-source" 2>/dev/null || true)"
if [ "${CONFIG_FILE_SOURCE}" = "-" ]; then
	CONFIG_FILE_SOURCE="${__object}/stdin"
fi
if [ -n "${CONFIG_FILE_SOURCE}" ] && [ "${STATE}" = "present" ]; then
	require="${require} __user/${USER}" __file \
		"${CONFIG_FILE_DEST}" \
		--owner "${USER}" \
		--group "${GROUP}" \
		--mode "0440" \
		--source "${CONFIG_FILE_SOURCE}"
	service_require="${service_require} __file${CONFIG_FILE_DEST}"
fi



# These messages will trigger a service restart (overridden for systemd)
service_config_reload_pattern="^__file${CONFIG_FILE_DEST}"

# This should setup the object in $service_definition_require
# See above.
case "${INIT}" in
	systemd)
		if [ -z "${SERVICE_DEFINITION}" ]; then
			SYSTEMD_ENV_FILE="/etc/systemd/system/${SERVICE_NAME}.env"
			__file "${SYSTEMD_ENV_FILE}" \
				--mode 0400 \
				--source "${__object}/parameter/env"
			# We need to take into account the envionment file for systemd too
			service_config_reload_pattern="(${service_config_reload_pattern}|^__file${SYSTEMD_ENV_FILE})"

			SERVICE_DEFINITION="$(cat <<EOF
[Unit]
Description=${SERVICE_DESCRIPTION}
After=network.target

[Service]
Type=simple

User=${USER}
Group=${GROUP}
ExecStart=${SERVICE_EXEC}
Restart=always
EnvironmentFile=${SYSTEMD_ENV_FILE}
${WORKING_DIRECTORY_SYSTEMD}

[Install]
WantedBy=multi-user.target
EOF
)"
		fi
		__systemd_unit "${SERVICE_NAME}.service" \
			--source "-" \
			--state "${STATE}" \
			--enablement-state "enabled" <<EOF
${SERVICE_DEFINITION}
EOF
	;;
	runit|sysvinit)
		if [ -z "${SERVICE_DEFINITION}" ]; then
			RUNIT_ENV="$(sed -Ee 's!^([[:alnum:]_]+)=(.*)$!export \1=\2!' "${__object}/parameter/env")"
			SERVICE_DEFINITION="$(cat <<EOF
#!/bin/sh -e
${WORKING_DIRECTORY_RUNIT}
# User-provided environment
${RUNIT_ENV}
# System vars
export HOME="\$(getent passwd '${USER}' | cut -d: -f6)"
export USER="${USER}"
export GROUP="${GROUP}"

exec 2>&1
exec chpst -u "${USER}:${GROUP}" ${SERVICE_EXEC}
EOF
)"
		fi
		__runit_service "${SERVICE_NAME}" \
			--state "${STATE}" \
			--log \
			--source - <<EOF
${SERVICE_DEFINITION}
EOF
	;;
esac
service_require="${service_require} ${service_definition_require}"

# Proceed after user and service description have been prepared
export require="${require} ${service_require}"

VERSION_FILE="${BIN_DIR}/.${SERVICE_NAME}.cdist.version"
IS_VERSION="$(cat "${__object}/explorer/explorer-version")"


if [ "${STATE}" = "absent" ]; then
	# Perform cleanup of generated files
	for bin_file in ${BINARY} ${EXTRA_BINARIES}; do
		__file "${BIN_DIR}/${bin_file}" --state "absent"
	done
	__file "${VERSION_FILE}" --state "absent"
	__file "${CONFIG_FILE_DEST}" --state "absent"
fi

if [ "${STATE}" != "present" ]; then
	exit
fi

sv_cmd() {
	# This is intentional
	# shellcheck disable=SC2059
	printf "${service_command}" "$1"
}

if [ "${SHOULD_VERSION}" != "${IS_VERSION}" ]; then
	# We are installing the service and there has been a version change
	# (or it is first-time install)
	TMP_PATH="/tmp/${SERVICE_NAME}-${SHOULD_VERSION}"

	# This is what will stop the service, replace the binaries and
	# start the service again
	perform_service_upgrade="$(cat <<EOF
$(sv_cmd stop) || true
if [ -f '${TMP_PATH}' ]; then
	chown root:${SUPER_USER_GROUP} '${TMP_PATH}'
	chmod 0555 '${TMP_PATH}'
	cp -af '${TMP_PATH}' '${BIN_DIR}/${BINARY}'
else
	for bin_file in ${BINARY} ${EXTRA_BINARIES}; do
		bin_path="${TMP_PATH}/\${bin_file}"
		chown root:${SUPER_USER_GROUP} "\${bin_path}"
		chmod 0555 "\${bin_path}"
		cp -af "\${bin_path}" "${BIN_DIR}/\${bin_file}"
	done
fi
$(sv_cmd start) || true
EOF
)"

	if [ -f "${__object}/parameter/unpack" ]; then
		UNPACK_EXTENSION="$(cat "${__object}/parameter/unpack-extension")"
		UNPACK_ARGS="$(cat "${__object}/parameter/unpack-args" \
			2>/dev/null || true)"
		# Download packed file
		__download "${TMP_PATH}${UNPACK_EXTENSION}" \
			--url "${DOWNLOAD_URL}" \
			--download remote \
			--sum "${CHECKSUM}"

		# Unpack file and also perform service upgrade
		# shellcheck disable=SC2086
		require="__download${TMP_PATH}${UNPACK_EXTENSION}" \
			__unpack "${TMP_PATH}${UNPACK_EXTENSION}" \
			${UNPACK_ARGS} \
			--destination "${TMP_PATH}"
		version_bump_require="__unpack${TMP_PATH}${UNPACK_EXTENSION}"
	else
		# Create temp directory
		__directory "${TMP_PATH}"
		# Download binary directoy to the temp directory with the
		# specified binary name
		require="__directory${TMP_PATH}" __download \
			"${TMP_PATH}/${BINARY}" \
			--url "${DOWNLOAD_URL}" \
			--download remote \
			--sum "${CHECKSUM}"
		version_bump_require="__download${TMP_PATH}/${BINARY}"
	fi

	# Perform update of cdist-managed version file
	# And also perform service upgrade
	# This is a bug if service_upgrade fails >,<
	printf "%s" "${SHOULD_VERSION}" | \
		require="${version_bump_require}" __file \
			"${VERSION_FILE}" \
			--onchange "${perform_service_upgrade}" \
			--source "-"
else
	# We only restart here if there was a config or env change
	# but there was not a version change
	require="${service_require}" __check_messages \
		"single_binary_service_${__object_id}" \
		--pattern "${service_config_reload_pattern}" \
		--execute "$(sv_cmd restart)"
fi
