#!/bin/sh
# Author: Theodore Zacharia
# V0.1: 29/04/2026 - Initial Release
#
# This script provides a threadsafe sequence number to replace
# th oracle sequence number feature which we lost when CID src sim went.
#
# Script can easily be extended to support others
#
# Returns EL=0 if good, otherwise non-zero
#
# Test this under load to make sure it works, the following will do 200 in total with 50 at a time:
# seq 1 200 | xargs -n1 -P50 sh src/getseqnum.sh claim > ck1
# wc -l ck1
# sort ck1 | uniq | wc -l
#

# *** Globals
TRACE=0
MYARG=""
SEQNUMFILEPATH="./data"
RETVALUE=""
EL=0


# *** Functions
_usage()
{
	echo "usage: $0 [-h] [-t] [-a arg] positional_params"
	echo "where"
	cat <<- _EOF_
	  -h        Displays this help message
	  -t        Set trace on
_EOF_
}

# at this point only 1 process and or thread may look at this file, read it and update it, 
# otherwise may get duplicates
_getthreadsafefromfile()
{
	SEQDB="${SEQNUMFILEPATH}/${1}"

	# in the getNext scripts we use a file, but a dir is more atomic
	LOCKDIR="${SEQDB}.lock"
	TIMEOUT=30	# Seconds (actually LOOPS * sleep time in loop) to wait before giving up
	WAITED=0

	if [ $TRACE -gt 0 ] ; then echo "Attempting to acquired Lock." >&2 ; fi

	# After testing with Mike, we noted a SIGKILL would not ne caught so ...
	# Check if the directory exists AND was modified more than 5 minutes ago (+5)
	if [ -d "$LOCKDIR" ]; then
	    if [ -n "$(find "$LOCKDIR" -prune -mmin +5 2>/dev/null)" ]; then
	        echo "Stale lock detected for $LOCKDIR (older than 5 minutes). Forcing cleanup..." >&2
	        rmdir "$LOCKDIR" 2>/dev/null
	    fi
	fi

	# 1. ATTEMPT TO ACQUIRE LOCK
	# mkdir is atomic in POSIX as only one process can succeed at a time.
	until mkdir "$LOCKDIR" 2>/dev/null; do
	    if [ "$WAITED" -ge "$TIMEOUT" ]; then
	        echo "ERROR: Could not acquire lock after $TIMEOUT seconds." >&2
	        exit 99
	    fi
	    
	    # Wait n second before trying again, will adjust based on futher testing
	    sleep 1
	    WAITED=$((WAITED + 1))
	done

	# 2. ENSURE CLEANUP
	# This runs automatically when the script exits, errors out, or is interrupted.
	trap 'rmdir "$LOCKDIR"' EXIT INT TERM

	# LOCK IS HELD. STARTING MAIN LOGIC.

	if [ $TRACE -gt 0 ] ; then echo "Lock acquired! Waited $WAITED .  Starting work in the main thread..." >&2 ; fi

	# this allows testing lockouts and fails by setting the sleep to more or less than TIMEOUT above
	#sleep 30

	if [ -f ${SEQDB}.seqnum ]
	then
		RETVALUE=$(cat ${SEQDB}.seqnum)
		RETVALUE=$((RETVALUE + 1))
		echo "$RETVALUE" > ${SEQDB}.seqnum

		if [ $TRACE -gt 0 ] ; then echo "Work finished. Releasing lock automatically." >&2 ; fi
	else
		# DB file does not exists so create one and start at 0
		# echo "0" > ${SEQDB}.seqnum

		# DB file does not exists, better to throw an error
		echo "ERROR: expected DB not found: ${SEQDB}.seqnum" >&2
		EL=12
	fi

}


# *** Mainline

# process input parameters
while getopts tha: AOPT
do
case $AOPT in
	t) TRACE=1 ;; # set TRACE mode
	a) MYARG=$OPTARG ;;
	h) _usage
	   exit 1 ;;
	*) echo "$AOPT is an invalid option" >&2
	   exit 2 ;;
esac
done

shift $((OPTIND-1))

# you MUST supply a single parameter, the identifier for the DB
if [ $# -lt 1 ]
then
	exit 9
fi

# first get the value
case $1 in
	date)
		RETVALUE=$(date +%s)
		;;
	*)	_getthreadsafefromfile $1 ;;
esac

# now format the value, if required
case $1 in
	claimsx)	# example
		RETVALUE=$(echo ${RETVALUE}"X")
		;;
	*)	;;	# default is no formatting
esac


# only output value if no error
if [ $EL -eq 0 ]
then
	echo $RETVALUE
fi

# check the exit value, via $?, this should be 0 or there has been an issue
exit $EL
