#!/bin/zsh
use --group remote bastion
use --group remote config

DEPENDENCIES+=(timeout ssh)
#####################################################################

USAGE__options='
	-n, --name                session name to test
	-s, --connection-string   explicit session host / ssh connection string to test
	-t, --maximum-timeout     maximum connection timeout before failure (in seconds)
	-c, --command             testing command; performs echo by default

	--use-bastion   true / false override for bastion preference
	                (default: session.name.bastion.preferred or "false")
'

USAGE__description='
	Tests whether you can connect to a particular session or
	host string.
'

#####################################################################

MAIN() {
	local CONNECTION_STRING REMOTE_NAME
	local TIMEOUT_SECONDS=5
	local COMMAND='echo OK &>/dev/null'
	local USE_BASTION
	while [[ $# -gt 0 ]]
	do
		local _S=1
		case $1 in
			-n | --name )
				((_S+=1))
				REMOTE_NAME=$2
				CONNECTION_STRING=$(remote.config.get-connection-string ${REMOTE_NAME})
				;;
			-s | --connection_string )
				((_S+=1))
				CONNECTION_STRING="$2"
				;;
			-t | --maximum-timout )
				((_S+=1))
				TIMEOUT_SECONDS=$2
				[[ ${TIMEOUT_SECONDS} -gt 0 ]] \
					|| echo.error "invalid timeout seconds '${TIMEOUT_SECONDS}'"
				;;
			-c | --command )
				((_S+=1))
				COMMAND=$2
				;;
			--use-bastion )
				((_S+=1))
				USE_BASTION=$2
				case ${USE_BASTION} in
					true | false ) ;;
					* ) echo.error "invalid setting for '--use-bastion' (must be 'true' or 'false')" ;;
				esac
				;;
			* ) echo.error "unrecognized argument '$1'" ;;
		esac
		[[ $_S -le $# ]] \
			&& shift $_S \
			|| echo.error "missing argument for '$1'" \
			|| shift $#
	done

	[ ${CONNECTION_STRING} ] \
		|| echo.error "unable to determine connection string"


	[ ${USE_BASTION} ] || {
		USE_BASTION=$(\
			remote.config.query-connection-with-fallback \
				".sessions.${REMOTE_NAME}.bastion.preferred" \
				'false' \
				;
			)
	}

	utils.check-errors --fail


	##########################################

	local BASTION_HOST
	[[ ${USE_BASTION} =~ true ]] && {
		BASTION_HOST=$(\
			remote.config.query-connection-with-fallback \
				".sessions.${REMOTE_NAME}.bastion.session" \
			)
	}

	[[ ${USE_BASTION} =~ true ]] && {
		[ ${BASTION_HOST} ] \
			|| echo.error "cannot connect to ${REMOTE_NAME}; no configured bastion host" \
			|| return 1
	}

	case ${CONNECTION_STRING} in
		( localhost | $USER@localhost )
			CONNECTION_TEST() { return 0; } # current user on local machine can always connect
			;;
		* )
			case ${USE_BASTION} in
				( true )
					CONNECTION_TEST() {
						echo.debug "MAIN -n ${BASTION_HOST} -c \"$(remote.bastion.get-passthrough-prefix) remote test -- -n ${REMOTE_NAME} -c \"${COMMAND}\"\""
						BASTION_TARGET="${REMOTE_NAME}" MAIN -n ${BASTION_HOST} -c "$(remote.bastion.get-passthrough-prefix) remote test -- -n ${REMOTE_NAME} -c \"${COMMAND}\""
						return $?
					}
					;;

				( * )
					CONNECTION_TEST() {
						local REMOTE_ARGS=()
						REMOTE_ARGS+=($(remote.config.get-ssh-args --type ssh ${REMOTE_NAME}))
						REMOTE_ARGS+=(-o BatchMode=yes)
						echo.debug "attempting\ntimeout ${TIMEOUT_SECONDS} ssh ${REMOTE_ARGS} ${CONNECTION_STRING} "'\'"\"${COMMAND}"'\'"\"" >&2
						timeout --foreground ${TIMEOUT_SECONDS} ssh ${REMOTE_ARGS[@]} "${CONNECTION_STRING}" "${COMMAND}" >&2
					}
					;;
			esac
			;;
	esac

	[ ${REMOTE_NAME} ] && {
		[[ $(remote.config.query-connection .sessions.${REMOTE_NAME}.enabled) =~ false ]] && {
			echo.error "automatic connection to ${REMOTE_NAME} is disabled"
			return 1
		}
	}

	[ ${REMOTE_NAME} ] || REMOTE_NAME=explicit
	echo.status "testing connection ${CONNECTION_STRING} (${REMOTE_NAME}$([ ${BASTION_TARGET} ] && echo " -> ${BASTION_TARGET}"))" \
		&& CONNECTION_TEST \
		&& echo.success "successfully connected to '${CONNECTION_STRING}' (${REMOTE_NAME})" \
		|| echo.error "connection to '${CONNECTION_STRING} (${REMOTE_NAME})' failed" \
}
