function hashicorp_release {
   # Arguments:
   #   $1 - Path to directory containing all of the release artifacts
   #
   # Returns:
   #   0 - success
   #   * - failure
   #
   # Notes:
   #   Requires the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables
   #   to be set
   
   status "Uploading files"
   hc-releases upload "${1}" || return 1
   
   status "Publishing the release"
   hc-releases publish || return 1
   
   return 0
}

function confirm_git_remote {
   # Arguments:
   #   $1 - Path to git repo
   #   $2 - remote name
   #
   # Returns:
   #   0 - success
   #   * - error
   #
   
   local remote="$2"
   local url=$(git_remote_url "$1" "${remote}")
   
   echo -e "\n\nConfigured Git Remote: ${remote}"
   echo -e     "Configured Git URL:    ${url}\n"
   
   local answer=""
      
   while true
   do
      case "${answer}" in
         [yY]* )
            status "Remote Accepted"
            return 0
            break
            ;;
         [nN]* )
            err "Remote Rejected"
            return 1
            break
            ;;
         * )
            read -p "Is this Git Remote correct to push ${CONSUL_PKG_NAME} to? [y/n]: " answer
            ;;
      esac
   done
}

function confirm_git_push_changes {
   # Arguments:
   #   $1 - Path to git repo
   #
   # Returns:
   #   0 - success
   #   * - error
   #
   
   if ! test -d "$1"
   then
      err "ERROR: '$1' is not a directory. confirm_git_push_changes must be called with the path to a git repo as the first argument'" 
      return 1
   fi
   
   pushd "${1}" > /dev/null
   
   
   declare -i ret=0
   git_log_summary || ret=1
   if test ${ret} -eq 0
   then
      # put a empty line between the git changes and the prompt
      echo ""
      
      local answer=""
      
      while true
      do
         case "${answer}" in
            [yY]* )
               status "Changes Accepted"
               ret=0
               break
               ;;
            [nN]* )
               err "Changes Rejected"
               ret=1
               break
               ;;
            ?)
               # bindata_assetfs.go will make these meaningless
               git_diff "$(pwd)" ":!agent/uiserver/bindata_assetfs.go"|| ret 1
               answer=""
               ;;
            * )
               read -p "Are these changes correct? [y/n] (or type ? to show the diff output): " answer
               ;;
         esac
      done
   fi
   
   popd > /dev/null
   return $ret
}

function extract_consul_local {
   # Arguments:
   #   $1 - Path to the zipped binary to test
   #   $2 - Version to look for
   #
   # Returns:
   #   0 - success
   #   * - error
   
   local zfile="${1}/${CONSUL_PKG_NAME}_${2}_$(go env GOOS)_$(go env GOARCH).zip"
   
   if ! test -f "${zfile}"
   then
      err "ERROR: File not found or is not a regular file: ${zfile}"
      return 1
   fi
   
   local ret=0
   local tfile="$(mktemp) -t "${CONSUL_PKG_NAME}_")"
   
   unzip -p "${zfile}" "consul" > "${tfile}"
   if test $? -eq 0
   then
      chmod +x "${tfile}"
      echo "${tfile}"
      return 0
   else
      err "ERROR: Failed to extract consul binary from the zip file"
      return 1
   fi
}

function confirm_consul_version {
   # Arguments:
   #   $1 - consul exe to use
   # 
   # Returns:
   #   0 - success
   #   * - error
   local consul_exe="$1"
   
   if ! test -x "${consul_exe}"
   then
      err "ERROR: '${consul_exe} is not an executable"
      return 1
   fi
   
   "${consul_exe}" version
   
   # put a empty line between the version output and the prompt
   echo ""
   
   local answer=""
   
   while true
   do
      case "${answer}" in
         [yY]* )
            status "Version Accepted"
            ret=0
            break
            ;;
         [nN]* )
            err "Version Rejected"
            ret=1
            break
            ;;
         * )
            read -p "Is this Consul version correct? [y/n]: " answer
            ;;
      esac
   done
}

function confirm_consul_info {
   # Arguments:
   #   $1 - Path to a consul exe that can be run on this system
   #
   # Returns:
   #   0 - success
   #   * - error
   
   local consul_exe="$1"
   local log_file="$(mktemp) -t "consul_log_")"
   "${consul_exe}" agent -dev > "${log_file}" 2>&1 &
   local consul_pid=$!
   sleep 1
   status "First 25 lines/1s of the agents output:"
   head -n 25 "${log_file}"
   
   echo ""
   local ret=0
   local answer=""
   
   while true
   do
      case "${answer}" in
         [yY]* )
            status "Consul Agent Output Accepted"
            break
            ;;
         [nN]* )
            err "Consul Agent Output Rejected"
            ret=1
            break
            ;;
         * )
            read -p "Is this Consul Agent Output correct? [y/n]: " answer
            ;;
      esac
   done
   
   if test "${ret}" -eq 0
   then
      status "Consul Info Output"
      "${consul_exe}" info
      echo ""
      local answer=""
      
      while true
      do
         case "${answer}" in
            [yY]* )
               status "Consul Info Output Accepted"
               break
               ;;
            [nN]* )
               err "Consul Info Output Rejected"
               return 1
               break
               ;;
            * )
               read -p "Is this Consul Info Output correct? [y/n]: " answer
               ;;
         esac
      done
   fi
   
   if test "${ret}" -eq 0
   then
      local tfile="$(mktemp) -t "${CONSUL_PKG_NAME}_")"
      if ! curl -o "${tfile}" "http://localhost:8500/ui/"
      then
         err "ERROR: Failed to curl http://localhost:8500/ui/"
         return 1
      fi
      
      local ui_vers=$(ui_version "${tfile}")
      if test $? -ne 0
      then
         err "ERROR: Failed to determine the ui version from the index.html file"
         return 1
      fi
      status "UI Version: ${ui_vers}"
      local ui_logo_type=$(ui_logo_type "${tfile}")
      if test $? -ne 0
      then
         err "ERROR: Failed to determine the ui logo/binary type from the index.html file"
         return 1
      fi
      status "UI Logo: ${ui_logo_type}"
      
      echo ""
      local answer=""
      
      while true
      do
         case "${answer}" in
            [yY]* )
               status "Consul UI/Logo Version Accepted"
               break
               ;;
            [nN]* )
               err "Consul UI/Logo Version Rejected"
               return 1
               break
               ;;
            * )
               read -p "Is this Consul UI/Logo Version correct? [y/n]: " answer
               ;;
         esac
      done
   fi
   

   
   status "Requesting Consul to leave the cluster / shutdown"
   "${consul_exe}" leave
   wait ${consul_pid} > /dev/null 2>&1
   
   return $?
}

function extract_consul {
   extract_consul_local "$1" "$2"
}

function verify_release_build {
   # Arguments:
   #   $1 - Path to top level Consul source
   #   $2 - expected version (optional - will parse if empty)
   #
   # Returns:
   #   0 - success
   #   * - failure
   
   if ! test -d "$1"
   then
      err "ERROR: '$1' is not a directory. publish_release must be called with the path to the top level source as the first argument'" 
      return 1
   fi
   
   local sdir="$1"
   
   local vers="$(get_version ${sdir} true false)"
   if test -n "$2"
   then
      vers="$2"
   fi
   
   if test -z "${vers}"
   then
      err "Please specify a version (couldn't parse one from the source)." 
      return 1
   fi
   
   status_stage "==> Verifying release files"
   check_release "${sdir}/pkg/dist" "${vers}" true || return 1
   
   status_stage "==> Extracting Consul version for local system"
   local consul_exe=$(extract_consul "${sdir}/pkg/dist" "${vers}") || return 1
   # make sure to remove the temp file
   trap "rm '${consul_exe}'" EXIT
   
   status_stage "==> Confirming Consul Version"
   confirm_consul_version "${consul_exe}" || return 1
   
   status_stage "==> Confirming Consul Agent Info"
   confirm_consul_info "${consul_exe}" || return 1
}

function publish_release {
   # Arguments:
   #   $1 - Path to top level Consul source that contains the built release
   #   $2 - boolean whether to publish to git upstream
   #   $3 - boolean whether to publish to releases.hashicorp.com
   #
   # Returns:
   #   0 - success
   #   * - error
   
   if ! test -d "$1"
   then
      err "ERROR: '$1' is not a directory. publish_release must be called with the path to the top level source as the first argument'" 
      return 1
   fi
   
   local sdir="$1"
   local pub_git="$2"
   local pub_hc_releases="$3"
   
   if test -z "${pub_git}"
   then
      pub_git=1
   fi
   
   if test -z "${pub_hc_releases}"
   then
      pub_hc_releases=1
   fi
   
   local vers="$(get_version ${sdir} true false)"
   if test $? -ne 0
   then
      err "Please specify a version (couldn't parse one from the source)." 
      return 1
   fi
   
   verify_release_build "$1" "${vers}" || return 1
   
   status_stage "==> Confirming Git is clean"
   is_git_clean "$1" true || return 1

   status_stage "==> Confirming Git Changes"
   confirm_git_push_changes "$1" || return 1
   
   status_stage "==> Checking for denylisted Git Remote"
   local remote=$(find_git_remote "${sdir}") || return 1
   git_remote_not_denylisted "${sdir}" "${remote}" || return 1
   
   status_stage "==> Confirming Git Remote"
   confirm_git_remote "${sdir}" "${remote}" || return 1
   
   if is_set "${pub_git}"
   then
      status_stage "==> Pushing to Git"
      git_push_ref "$1" "" "${remote}" || return 1
      git_push_ref "$1" "v${vers}" "${remote}" || return 1
   fi
   
   if is_set "${pub_hc_releases}"
   then
      status_stage "==> Publishing to releases.hashicorp.com"
      hashicorp_release "${sdir}/pkg/dist" || return 1
   fi
   
   return 0
}
