#!/bin/sh

# surplus on wheels (s+ow): a pure shell script to run surplus with mdtest using the termux-api

# shellcheck disable=SC2059
LOCATION_FALLBACK="%d%d%d\nSingapore?"
# shellcheck disable=SC2269
LOCATION_PRIORITISE_NETWORK="$LOCATION_PRIORITISE_NETWORK"
LOCATION_TIMEOUT=${LOCATION_TIMEOUT:-50}

# shellcheck disable=SC2269
JID_NOMINAL_TARGET="$JID_NOMINAL_TARGET"
# shellcheck disable=SC2269
JID_ERRORED_TARGET="$JID_ERRORED_TARGET"

MDTEST_BIN="$HOME/.local/bin/mdtest"
MDTEST_DIR="$HOME/.local/share/mdtest"

SPOW_CACHE_DIR="$HOME/.cache/s+ow"

# per-tool session logs
SPOW_NETLC_OUT="$SPOW_CACHE_DIR/location.net.json"
SPOW_GPSLC_OUT="$SPOW_CACHE_DIR/location.gps.json"
SPOW_LOCTN_OUT="$SPOW_CACHE_DIR/location.json"
SPOW_SPLUS_OUT="$SPOW_CACHE_DIR/surplus.out.log"
SPOW_SPLUS_ERR="$SPOW_CACHE_DIR/surplus.err.log"

# per-session collated logs
SPOW_SESH_OUT="$SPOW_CACHE_DIR/out.log"
SPOW_SESH_ERR="$SPOW_CACHE_DIR/err.log"

# per-week collated logs
SPOW_WEEK_PRE="$SPOW_CACHE_DIR/$(date +%Y)W$(date +"%V")"
SPOW_WEEK_OUT="$SPOW_WEEK_PRE.out.log"
SPOW_WEEK_ERR="$SPOW_WEEK_PRE.err.log"

# last successful surplus output
SPOW_LAST_OUT="$SPOW_CACHE_DIR/last"

# list of fakes
# shellcheck disable=SC2034
SPOW_FAKE_OUT="$SPOW_CACHE_DIR/fake"

# check for network location priority
if [ "$LOCATION_PRIORITISE_NETWORK" = "n" ]; then
	LOCATION_PRIORITISE_NETWORK=""
fi

# ensure commands exist
if ! command -v termux-location >/dev/null 2>&1; then
	printf "s+ow: error: termux-location is not installed.\ninstall it with 'pkg install termux-api' and with installing the termux:api app from the play store or f-droid.\n"
	exit 1
fi

if ! command -v surplus >/dev/null 2>&1; then
	printf "s+ow: error: surplus is not installed.\ninstall it with 'pip install https://github.com/markjoshwel/surplus/releases/latest/download/surplus-latest-py3-none-any.whl'\n"
	exit 1
fi

if ! command -v ~/.local/bin/mdtest >/dev/null 2>&1; then
	printf "s+ow: error: mdtest is not installed.\ninstall it by getting a release or building it from source from https://github.com/markjoshwel/whatsmeow-termux\n"
	exit 1
fi

# ensure directories
mkdir -p "$SPOW_CACHE_DIR" "$MDTEST_DIR"

# create new session logs
rm -f "$SPOW_LOCTN_OUT" "$SPOW_SPLUS_OUT" "$SPOW_SPLUS_ERR" \
	"$SPOW_SESH_OUT" "$SPOW_SESH_ERR"
touch "$SPOW_NETLC_OUT" "$SPOW_GPSLC_OUT" "$SPOW_LOCTN_OUT" \
	"$SPOW_SPLUS_OUT" "$SPOW_SPLUS_ERR" \
	"$SPOW_SESH_OUT" "$SPOW_SESH_ERR" \
	"$SPOW_WEEK_OUT" "$SPOW_WEEK_ERR"

status=0 # 0 is nominal
# 1 is an termux-location error
# 2 is a surplus error

# helper functions

locate() {
	# spawn termux-location processes
	(
		termux-location -p "network" >"$SPOW_NETLC_OUT"
		if [ -s "$SPOW_NETLC_OUT" ]; then
			printf "net" | tee -a "$SPOW_SESH_ERR"
		else
			printf "net?" | tee -a "$SPOW_SESH_ERR"
		fi
		cat "$SPOW_NETLC_OUT" >> "$SPOW_SESH_OUT"
	) &
	tl_net_pid="$!"
	sleep 1
	(
		termux-location -p "gps" >"$SPOW_GPSLC_OUT"
		if [ -s "$SPOW_GPSLC_OUT" ]; then
			printf "gps" | tee -a "$SPOW_SESH_ERR"
		else
			printf "gps?" | tee -a "$SPOW_SESH_ERR"
		fi
		cat "$SPOW_GPSLC_OUT" >> "$SPOW_SESH_OUT"
	) &
	tl_gps_pid="$!"

	# wait until timeout or both finished
	printf "running termux-location" | tee -a "$SPOW_SESH_ERR"
	while [ "$LOCATION_TIMEOUT" -gt 0 ]; do
		# get process statuses
		kill -0 "$tl_net_pid" >/dev/null 2>&1
		tl_net_status="$?"
		kill -0 "$tl_gps_pid" >/dev/null 2>&1
		tl_gps_status="$?"

		# break if both finished
		if [ "$tl_net_status" -eq 1 ] && [ "$tl_gps_status" -eq 1 ]; then
			break
		fi

		# exception: if network is proritised: just use that
		if [ "$tl_net_status" -eq 1 ] && [ -n "$LOCATION_PRIORITISE_NETWORK" ]; then
			# break only if theres an actual response
			if [ -s "$SPOW_NETLC_OUT" ]; then
				break
			fi
			# else just keep on waiting for gps to finish
		fi

		sleep 1
		printf "." | tee -a "$SPOW_SESH_ERR"
		LOCATION_TIMEOUT=$((LOCATION_TIMEOUT - 1))
	done
	if [ "$LOCATION_TIMEOUT" -eq 0 ]; then
		printf " errored (timeout)\n" | tee -a "$SPOW_SESH_ERR"
	else
		printf " nominal\n" | tee -a "$SPOW_SESH_ERR"
	fi

	# check outputs
	printf "determining output: " | tee -a "$SPOW_SESH_ERR"
	if [ -s "$SPOW_NETLC_OUT" ] && [ -s "$SPOW_GPSLC_OUT" ]; then
		printf "both succeeded, "
		acc_net="$(grep "\"accuracy\"" <"$SPOW_NETLC_OUT" | awk -F ': ' '{print $2}' | tr -d ',')"
		acc_gps="$(grep "\"accuracy\"" <"$SPOW_GPSLC_OUT" | awk -F ': ' '{print $2}' | tr -d ',')"

		# compare accuracy
		if awk -v n1="$acc_net" -v n2="$acc_gps" 'BEGIN { if (n1 < n2) exit 0; else exit 1; }'; then
			printf "choosing network (%s < %s)" "$acc_net" "$acc_gps" | tee -a "$SPOW_SESH_ERR"
			cat "$SPOW_NETLC_OUT" >"$SPOW_LOCTN_OUT"
		else
			printf "choosing gps (%s < %s)" "$acc_gps" "$acc_net" | tee -a "$SPOW_SESH_ERR"
			cat "$SPOW_GPSLC_OUT" >"$SPOW_LOCTN_OUT"
		fi

		cat "$SPOW_GPSLC_OUT" >"$SPOW_LOCTN_OUT"
	else
		# one or none succeeded
		if [ -s "$SPOW_NETLC_OUT" ]; then
			if [ -n "$LOCATION_PRIORITISE_NETWORK" ]; then
				printf "using network (prioritised)" | tee -a "$SPOW_SESH_ERR"
			else
				printf "using network" | tee -a "$SPOW_SESH_ERR"
			fi
			cat "$SPOW_NETLC_OUT" >"$SPOW_LOCTN_OUT"
		fi
		if [ -s "$SPOW_GPSLC_OUT" ]; then
			printf "using gps" | tee -a "$SPOW_SESH_ERR"
			cat "$SPOW_GPSLC_OUT" >"$SPOW_LOCTN_OUT"
		fi
	fi
	if [ ! -s "$SPOW_LOCTN_OUT" ]; then
		printf "none (error)" | tee -a "$SPOW_SESH_ERR"
	fi
	printf "\n" | tee -a "$SPOW_SESH_ERR"
}

gensharetext() {
	surplus -td "$1" >"$SPOW_SPLUS_OUT" 2>"$SPOW_SPLUS_ERR"
	ret="$?"
	cat "$SPOW_SPLUS_OUT" >>"$SPOW_SESH_OUT"
	cat "$SPOW_SPLUS_ERR" >>"$SPOW_SESH_ERR"
	return "$ret"
}

send() {
	(cd "$MDTEST_DIR" && "$MDTEST_BIN" send "$1" "$2")
}

notify_start() {
	termux-notification \
		--priority "min" \
		--ongoing \
		--id "s+ow" \
		--title "surplus on wheels" \
		--content "s+ow has started running."
}

notify() {
	# $1 is text
	# $2 is attempt number (if any)
	attempt_text="$1"
	if [ $# -eq 2 ]; then
		attempt_text="$1 (attempt $2)"
	fi
	termux-notification \
		--priority "min" \
		--ongoing \
		--id "s+ow" \
		--title "surplus on wheels" \
		--content "$attempt_text"
}

notify_end() {
	# $1 is s+ow status (0, 1, 2)
	# $2 is termux-location run number
	# $3 is sent type (0, 1, 2)
	# $4 is sharetext
	termux-notification \
		--priority "min" \
		--id "s+ow" \
		--title "surplus on wheels" \
		--content "$(printf 'Run has finished. (%d, %d, %d)\n\n%s' "$1" "$2" "$3" "$4")"
}

# program functions

mdtest() {
	(cd "$MDTEST_DIR" && "$MDTEST_BIN")
}

run() {
	notify_start
	printf "[run! stdout (%s)]\n" "$(date)" >>"$SPOW_SESH_OUT"
	printf "[run! stderr (%s)]\n" "$(date)" >>"$SPOW_SESH_ERR"

	# termux-location
	location=""
	for locate_run in 1 2 3; do # run three times in case :p
		notify "Running termux-location" "$locate_run"

		locate

		if [ ! -s "$SPOW_LOCTN_OUT" ]; then
			# erroneous: is empty
			echo "s+ow: error: failed to get location" >>"$SPOW_SESH_ERR"
			status=1
		else
			# nominal: is not empty
			location="$(cat "$SPOW_LOCTN_OUT")"
			status=0
			break
		fi
	done

	# surplus
	printf "running surplus... "
	notify "Running surplus -td $location"
	if [ "$status" -eq 0 ]; then
		if gensharetext "$location"; then
			# surplus ran nominally
			cp "$SPOW_SPLUS_OUT" "$SPOW_LAST_OUT"
			status=0
			printf "nominal\n"
		else
			# something happened :^)
			status=2
			printf "errored\n"
		fi
	else
		printf "skipped\n"
	fi

	# if cron: wait until its the new hour
	if [ -n "$SPOW_CRON" ]; then
		printf "waiting until the new hour...\n"
		while [ "$(date +'%M')" -eq 59 ]; do
			printf "  $(date)\n"
			sleep 1
		done
		printf "done\n"
	fi

	# mdtest/send message
	printf "sending message(s)... "
	notify "Sending message(s)"
	sent_type=0 # 0 for freshly made sharetext
	# 1 for recycling a last location
	# 2 for using fallback template
	sharetext=""
	if [ "$status" -eq 0 ]; then
		# s+ow has behaved nominally until now, send as per normal
		sharetext="$(cat "$SPOW_SPLUS_OUT")"
		printf "\n"
		send "$JID_NOMINAL_TARGET" "$sharetext"
	else
		# something has gone wrong, send an appropriate fallback
		sharetext=""
		if [ -s "$SPOW_LAST_OUT" ]; then
			# use last successful location
			sharetext="$(cat "$SPOW_LAST_OUT")"
			sent_type=1
			printf "using last...\n"
		else
			# no last location, use fallback
			# shellcheck disable=SC2059
			sharetext="$(printf "$LOCATION_FALLBACK" "$status" "$locate_run" "$sent_type")"
			sent_type=2
			printf "using fallback... \n"
		fi

		send "$JID_NOMINAL_TARGET" "$sharetext"
		send "$JID_ERRORED_TARGET" "$(cat "$SPOW_SESH_ERR")"
	fi

	done_msg="$(printf "done (%d, %d, %d)\n" "$status" "$locate_run" "$sent_type")"
	echo "$done_msg"
	echo "$done_msg" >>"$SPOW_SESH_ERR"

	# cleanup
	printf "%s\n\n" "$(cat "$SPOW_SESH_OUT")" >>"$SPOW_WEEK_OUT"
	printf "%s\n\n" "$(cat "$SPOW_SESH_ERR")" >>"$SPOW_WEEK_ERR"
	notify_end "$status" "$locate_run" "$sent_type" "$sharetext"
}

# script entry

if [ "$1" = "mdtest" ]; then
	mdtest
elif [ -z "$1" ]; then
	# ensure JID targets are set
	if [ -z "$JID_NOMINAL_TARGET" ] || [ -z "$JID_ERRORED_TARGET" ]; then
		echo "s+ow: error: JID_NOMINAL_TARGET and JID_ERRORED_TARGET are not set"
		exit 1
	fi
	run
else
	echo "usage: $0 [mdtest]

surplus on wheels: a pure shell script to run surplus with mdtest using the termux-api

choices
   $0 mdtest
	  run mdtest for testing or authentication
   $0
	  run surplus on wheels normally"
fi