# shellcheck shell=sh

echo "#<<startMarker>>"
echo "Script executing under PID: $$"

VSC_TMP="${XDG_RUNTIME_DIR:-/tmp}"

# Configuration
UUID="#<<uuid>>"
COMMIT_ID="#<<commitId>>"
QUALITY="#<<quality>>"
TOKEN="#<<token>>"
VSCODE_AGENT_FOLDER="#<<vscodeAgentFolder>>"
ALLOW_CLIENT_DOWNLOAD="#<<allowClientDownload>>"
FORCE_CLIENT_DOWNLOAD="#<<forceClientDownload>>"
CLI_NAME_IN_ARCHIVE="#<<cliNameInArchive>>"
IGNORE_WGET_CONFIG_FLAG="#<<ignoreWgetConfigFlag>>"
IGNORE_CURL_CONFIG_FLAG="#<<ignoreCurlConfigFlag>>"
WGET_TRIES_SEGMENT="#<<wgetTriesSegment>>"
LISTEN_ARGS="#<<listenArgs>>"
VERBOSE="#<<verbose>>"
HTTP_PROXY_INPUT="#<<httpProxy>>"
HTTPS_PROXY_INPUT="#<<httpsProxy>>"
RECONNECTION_GRACE_TIME="#<<reconnectionGraceTime>>"

CLI_NAME_ON_DISK="${CLI_NAME_IN_ARCHIVE}-${COMMIT_ID}"
CLI_PATH="${VSCODE_AGENT_FOLDER}/${CLI_NAME_ON_DISK}"
CLI_LOG_FILE="${VSCODE_AGENT_FOLDER}/.cli.${COMMIT_ID}.log"

# Marker strings
GET_DOWNLOAD_SERVER_START_TRIGGER="#<<getDownloadServerStartTrigger>>"
GET_DOWNLOAD_SERVER_END_TRIGGER="#<<getDownloadServerEndTrigger>>"
GET_PROGRESS_DOWNLOADING="#<<getProgressDownloading>>"
GET_PROGRESS_INSTALLING="#<<getProgressInstalling>>"

UNPACK_RESULT=
DID_LOCAL_DOWNLOAD=0
DOWNLOAD_TIME=
INSTALL_TIME=
SERVER_START_TIME=

fail_with_exitcode() {
	echo "${UUID}: start"
	echo "exitCode==$1=="
	echo_common_results
	echo "${UUID}: end"
	exit 0
}

echo_common_results() {
	echo "listeningOn==$LISTENING_ON=="
	echo "osReleaseId==$OSRELEASEID=="
	echo "arch==$ARCH=="
	echo "vscodeArch==$VSCODE_ARCH=="
	echo "bitness==$BITNESS=="
	echo "tmpDir==$VSC_TMP=="
	echo "platform==$PLATFORM=="
	echo "unpackResult==$UNPACK_RESULT=="
	echo "didLocalDownload==$DID_LOCAL_DOWNLOAD=="
	echo "downloadTime==$DOWNLOAD_TIME=="
	echo "installTime==$INSTALL_TIME=="
	echo "serverStartTime==$SERVER_START_TIME=="
	echo "execServerToken==${TOKEN}=="
	echo "platformDownloadPath==$PLATFORM_DOWNLOAD_PATH=="
	#<<printVars>>
}

millis() {
	d=$(date +%s%N)
	# Test if 'd' contains any non-digit characters
	if [ -z "${d##*[!0-9]*}" ] || [ "$PLATFORM" != "linux" ]; then
	    # Fallback for MacOS or any date binary that doesn't handle '%N'
		echo $(($(date +%s)*1000))
		return
	fi
	echo $((d/1000000))
}

elapsed() {
	echo $(($(millis) - $1))
}

printenv_indent() {
	if [ "$VERBOSE" = "1" ]; then
		echo "printenv:"
		printenv | sed 's/^/    /'
	fi
}

[ -z "${http_proxy}" ] && [ "${HTTP_PROXY_INPUT}" != "" ] && export http_proxy="$HTTP_PROXY_INPUT"
[ -z "${https_proxy}" ] && [ "${HTTPS_PROXY_INPUT}" != "" ] && export https_proxy="$HTTPS_PROXY_INPUT"

#
# Get OS name
#
OSRELEASEID=$(cat /etc/os-release 2>/dev/null | grep -a -E '^ID=' | sed 's/^[Ii][Dd]=//g' | sed 's/"//g')
if [ -z "$OSRELEASEID" ]
then
	OSRELEASEID=$(cat /usr/lib/os-release 2>/dev/null | grep -a -E '^ID=' | sed 's/^[Ii][Dd]=//g' | sed 's/"//g')
	if [ -z "$OSRELEASEID" ]
	then
		OSRELEASEID=$(uname -s)
	fi
fi

#
# Get host platform/architecture
#
UNAME=$(uname -s)
case $UNAME in
	Linux) PLATFORM=linux;;
	Darwin) PLATFORM=macOS;;
	*)
		echo "Unsupported platform: $UNAME"
		fail_with_exitcode #<<ExitCode.UnsupportedPlatform>>
		;;
esac

BITNESS=$(getconf LONG_BIT)
ARCH=$(uname -m)
case $ARCH in
	x86_64) VSCODE_ARCH="x64";;
	armv7l | armv8l)
		VSCODE_ARCH="armhf"
		;;
	arm64 | aarch64)
		if [ "$BITNESS" = 32 ]; then
			# Can have 32-bit userland on 64-bit kernel
			VSCODE_ARCH="armhf"
		else
			VSCODE_ARCH="arm64"
		fi
		;;
	*)
		OSRELEASE=$(uname -r)
		case $OSRELEASE in
			*x86_64*) VSCODE_ARCH="x64";;
			*)
				echo "Unsupported architecture: $ARCH"
				fail_with_exitcode #<<ExitCode.UnsupportedArch>>
			;;
		esac
		;;
esac

if [ "$PLATFORM" = linux ]; then
	if [ "$VSCODE_ARCH" = armhf ]; then
		PLATFORM_DOWNLOAD_PATH=cli-linux-armhf
	else
		PLATFORM_DOWNLOAD_PATH=cli-alpine-$VSCODE_ARCH
	fi
elif [ "$VSCODE_ARCH" = "arm64" ]; then
	PLATFORM_DOWNLOAD_PATH=cli-darwin-arm64
else
	PLATFORM_DOWNLOAD_PATH=cli-darwin-x64
fi

if [ ! -d "$VSCODE_AGENT_FOLDER" ]; then
	mkdir -p "$VSCODE_AGENT_FOLDER"
	chmod 750 "$VSCODE_AGENT_FOLDER"

	error_code=$?
	if [ "${error_code}" -gt 0 ]; then
		echo "Creating the server install dir failed..."
		fail_with_exitcode #<<ExitCode.CreateInstallDirFailed>>
	fi
fi

#
# Delete old CLIs if needed
#

# shellcheck disable=SC2010
TO_DELETE=$(ls -1 -t "$VSCODE_AGENT_FOLDER" | grep -E "code(-insiders)?-[a-fA-F0-9]{40}" | tail -n +6)
for CLI_TO_DELETE in $TO_DELETE; do
	target_dir="$VSCODE_AGENT_FOLDER/$CLI_TO_DELETE"
	echo "Deleting old install $target_dir"
	rm -rf "$target_dir"
done

do_client_download() {
	DID_LOCAL_DOWNLOAD=1
	echo "Trigger local server download"
	echo "${GET_DOWNLOAD_SERVER_START_TRIGGER}"
	echo artifact==$PLATFORM_DOWNLOAD_PATH==
	echo destFolder==$VSCODE_AGENT_FOLDER==
	echo destFolder2==/vscode-cli-$COMMIT_ID.tar.gz==
	echo "${GET_DOWNLOAD_SERVER_END_TRIGGER}"
	echo "Waiting for client to transfer server archive..."
	echo "Waiting for $VSCODE_AGENT_FOLDER/vscode-cli-$COMMIT_ID.tar.gz.done and vscode-server.tar.gz to exist"

	while true; do
		if [ -f "$VSCODE_AGENT_FOLDER/vscode-cli-$COMMIT_ID.tar.gz.done" ]; then
			if [ ! -f "$VSCODE_AGENT_FOLDER/vscode-cli-$COMMIT_ID.tar.gz" ]; then
				echo "Found flag but not server tar - server transfer failed"
				fail_with_exitcode #<<ExitCode.ServerTransferFailed>>
			fi

			echo "Found flag and server on host"
			rm "$VSCODE_AGENT_FOLDER/vscode-cli-$COMMIT_ID.tar.gz.done"
			break
		else
			printf ' '
			sleep 3
		fi
	done
}

do_client_download_or_fail() {
	error_code="${1:-"1"}"
	if [ "$DID_LOCAL_DOWNLOAD" = "1" ]; then
		echo "Already attempted local download, failing"
		fail_with_exitcode "$error_code"
	elif [ $ALLOW_CLIENT_DOWNLOAD = "1" ]; then
		do_client_download
	else
		fail_with_exitcode "$error_code"
	fi
}

is_program_from_busybox() {
	program=$1
	if command -v busybox > /dev/null 2>&1
	then
		# Check symlink from program to busybox
		if [ -L "$(command -v "$program")" ] && [ "$(readlink -f "$(command -v "$program")")" = "$(command -v busybox)" ]
		then
			echo "Program '$program' is provided by busybox" >&2
			echo 'yes'
			return
		fi
	fi
	echo 'no'
}

supports_flag() {
    program="$1"
    flag="$2"

    if command -v "$program" > /dev/null 2>&1; then
        if "$program" --help 2>&1 | grep -q -- "$flag"
		then
			echo "Program '$program' appears to support flag '$flag'" >&2
			echo 'yes'
			return
		fi
    fi
		echo "Program '$program' is not available or does not appear to support flag '$flag'" >&2
        echo 'no'
}

do_host_download() {
	start=$(millis)
	echo "${GET_PROGRESS_DOWNLOADING}"

	UPDATE_URL="#<<updateUrl>>"
	DOWNLOAD_URL=$UPDATE_URL/commit:$COMMIT_ID/$PLATFORM_DOWNLOAD_PATH/${QUALITY}

	if command -v wget > /dev/null 2>&1
	then
		echo "Downloading with wget"
		IS_WGET_BUSYBOX=$(is_program_from_busybox wget)

		echo "wget is from busybox: $IS_WGET_BUSYBOX"

		if [ "$IS_WGET_BUSYBOX" = 'no' ]
		then
			# Not busybox.
			# Assuming its GNU wget or similar

			if [ -n "$IGNORE_WGET_CONFIG_FLAG" ] && [ "$(supports_flag 'wget' '--no-config')" = "no" ]; then
				echo "Detected that this version of wget does not support '--no-config'. Will not ignore wget default configuration files."
				IGNORE_WGET_CONFIG_FLAG=""
			fi

			WGET_ERRORS=$(2>&1 wget ${IGNORE_WGET_CONFIG_FLAG} ${WGET_TRIES_SEGMENT} --connect-timeout=7 --dns-timeout=7 -nv -O vscode-cli-$COMMIT_ID.tar.gz ${DOWNLOAD_URL})
		else
			# Is busybox.
			# Remove flags for the sake of compatibility

			WGET_ERRORS=$(2>&1 wget -O vscode-cli-$COMMIT_ID.tar.gz ${DOWNLOAD_URL})
		fi

		error_code=$?
		if [ $error_code -ne 0 ]
		then
			echo "wget download failed"
			echo "$WGET_ERRORS"
			printenv_indent

			do_client_download_or_fail #<<ExitCode.ServerDownloadFailed>>
		else
			echo "Download complete"
			DOWNLOAD_TIME=$(elapsed $start)
		fi
	else
		command -v curl > /dev/null 2>&1
		error_code=$?
		if [ $error_code -eq 0 ]
		then
			echo "Downloading with curl"
			CURL_STATUS_CODE_RESULT=$(curl ${IGNORE_CURL_CONFIG_FLAG} --connect-timeout 7 -L $DOWNLOAD_URL --output vscode-cli-$COMMIT_ID.tar.gz -w "%{http_code}")
			error_code=$?
			curl_status_code_if_2xx=$(echo "$CURL_STATUS_CODE_RESULT" | grep -E "^2[0-9]{2}$")
			if [ $error_code -ne 0 ] || [ -z "$curl_status_code_if_2xx" ]
			then
				echo "curl download failed"
				echo "HTTP status code: $CURL_STATUS_CODE_RESULT"
				printenv_indent

				do_client_download_or_fail #<<ExitCode.ServerDownloadFailed>>
			else
				echo "Download complete"
				DOWNLOAD_TIME=$(elapsed $start)
			fi
		else
			printenv_indent
			echo "Neither wget nor curl is installed"

			do_client_download_or_fail #<<ExitCode.NoDownloaderAvailable>>
		fi
	fi
}

do_install() {
	start=$(millis)
	echo "${GET_PROGRESS_INSTALLING}"
	UNPACK_RESULT="#<<InstallUnpackCode.Success>>"
	echo "tar --version: $(tar --version)"
	tar -xf vscode-cli-$COMMIT_ID.tar.gz --no-same-owner
	TAR_EXIT=$?
	INSTALL_TIME=$(elapsed $start)

	if [ $TAR_EXIT -ne 0 ]
	then
		echo "ERROR: tar exited with a non-zero exit code: $TAR_EXIT"
		UNPACK_RESULT="#<<InstallUnpackCode.Error>>"
		do_client_download_or_fail "#<<ExitCode.UnpackFailed>>"
		do_install
		return
	fi

	mv "$CLI_NAME_IN_ARCHIVE" "$CLI_PATH"

	# cheap sanity check

	if ! eval "$CLI_PATH --version"
	then
		UNPACK_RESULT="#<<InstallUnpackCode.MissingFiles>>"
		echo "ERROR: $CLI_PATH don't exist"
		do_client_download_or_fail "#<<ExitCode.UnpackFailed>>"
		do_install
		return
	fi

	# Delete the leftover folder which might have -web prefix, and the .tar.gz
	rm -rf vscode-cli*
}

#
# Install if needed
#
if [ ! -f "$CLI_PATH" ]
then
	echo "Installing to $VSCODE_AGENT_FOLDER..."
	STASHED_WORKING_DIR="$(pwd)"
	cd "$VSCODE_AGENT_FOLDER" || fail_with_exitcode #<<ExitCode.ChangeDirFailed>>

	if [ $FORCE_CLIENT_DOWNLOAD = "1" ]; then
		do_client_download
	else
		do_host_download
	fi

	do_install
	cd "$STASHED_WORKING_DIR" || fail_with_exitcode #<<ExitCode.ChangeDirFailed>>
else
	echo "Found existing installation at $VSCODE_AGENT_FOLDER..."
fi


#
# Start the server
#
start_server() {
	echo "Starting VS Code CLI..."
	printenv_indent
	start=$(millis)
	if [ -f $CLI_LOG_FILE ]; then
		echo "Removing old logfile at $CLI_LOG_FILE"
		rm "$CLI_LOG_FILE" # See #6265
	fi

	# Stop exporting VSCODE_AGENT_FOLDER once https://github.com/microsoft/vscode/pull/228287 is available and replace with:
	# --extensions-dir "$VSCODE_AGENT_FOLDER/extensions" --user-data-dir "$VSCODE_AGENT_FOLDER/data"
	# See https://github.com/microsoft/vscode-internalbacklog/issues/2604, https://github.com/microsoft/vscode-remote-release/issues/10255
	export VSCODE_AGENT_FOLDER

	touch $CLI_LOG_FILE
	chmod 600 $CLI_LOG_FILE

	LOG_LEVEL_FLAG=""
	if [ "$VERBOSE" = "1" ]; then
	    echo "Setting '--log trace' on CLI"
		LOG_LEVEL_FLAG="--log trace"
	fi

	RECONNECTION_FLAG=""
	if [ -n "$RECONNECTION_GRACE_TIME" ]; then
		RECONNECTION_FLAG="--reconnection-grace-time $RECONNECTION_GRACE_TIME"
	fi

	VSCODE_CLI_REQUIRE_TOKEN=${TOKEN} "$CLI_PATH" command-shell ${LOG_LEVEL_FLAG} ${RECONNECTION_FLAG} --cli-data-dir "$VSCODE_AGENT_FOLDER/cli" --parent-process-id $$ ${LISTEN_ARGS} > "$CLI_LOG_FILE" 2>&1 < /dev/null &
	CLI_PID=$!
	echo "Spawned remote CLI: $!"

	count=0
	max_retries=15
	while [ $count -lt $max_retries ]; do
		count=$((count + 1))
		LISTENING_ON=$(cat "$CLI_LOG_FILE" | grep -a -E 'Listening on .+' | grep -v grep | sed 's/Listening on //')
		if [ "$LISTENING_ON" != '' ]
		then
			break
		fi

		# "If sig is 0 (the null signal), error checking is performed but no signal is actually sent.
		# The null signal can be used to check the validity of pid.""
		# Source: https://pubs.opengroup.org/onlinepubs/007908799/xsh/kill.html
		if ! kill -0 $CLI_PID > /dev/null; then
			echo "Exec server process not found"
			cat $CLI_LOG_FILE
			if grep -q "This machine does not meet .* prerequisites, expected either..." "$CLI_LOG_FILE"; then
				fail_with_exitcode #<<ExitCode.LinuxPrereqs>>
			fi
			break
		fi

		echo "Waiting for server log..."
		sleep .03 || sleep 1
	done
	SERVER_START_TIME=$(elapsed $start)
}

start_server

# What we echo below cannot be wider than 80 characters
echo "${UUID}: start"
echo_common_results
echo "${UUID}: end"

while true; do sleep 180; printf ' '; done
