#!/usr/bin/env bash

REQUIRED_CONFIGS+=("APP")
REQUIRED_CONFIGS+=("NODE_ACTION")
REQUIRED_CONFIGS+=("NODE_ENVIRONMENT")


require_node_config

run() {
  [[ "$NODE_ACTION" = version ]] && status "getting release versions from $NODE_ENVIRONMENT servers" || status "${NODE_ACTION}ing $NODE_ENVIRONMENT servers"
  authorize_hosts
  authorize_remote_hosts
  __exec_if_defined execute_custom_node_command $NODE_ACTION || execute_node_command $NODE_ACTION
}

# executes a node command asynchronously on all
# remote nodes and prints the result for each node.
# if there is only one single node and the node command
# is start, the progress of the start command output
# is printed continuously to the screen.
execute_node_command() {
  local _node_command=$1
  if [[ $(echo $NODES | wc -w) -eq 1 ]] && [[ "$_node_command" = "start" ]]; then
    __execute_node_command_synchronously "$_node_command"
  else
    __execute_node_command_asynchronously "$_node_command"
  fi
}

# executes a node command asynchronously on all
# remote nodes and prints the result for each node.
__execute_node_command_asynchronously() {
  local _node_command=$1
  echo
  background_jobs_pids=()
  local i=0;
  for _node in $NODES;
  do
    {
      local _output=""
      local _lines=-1
      local _result
      while read line; do
        _result=$line
        _output="${_output}${line}\n"
        _lines=$(( $_lines + 1 ))
      done
      _output=$(echo -e "$_output" | head -n $_lines)
      __print_node_command_result "$i" "$_result" "$_node" "$_output"
      exit $result
    } < <(__execute_node_command $i "$_node" "$_node_command") &
    background_jobs_pids+=("$!")
    i=$((i+1))

  done

  local _had_errors=0
  for (( i = 0 ; i < ${#background_jobs_pids[@]} ; i++ ))
  do
    wait ${background_jobs_pids[$i]}
    [[ "$?" = 0 ]] || _had_errors=1
  done
  return $_had_errors
}


# executes a node command synchronously on a single
# remote node and prints the output of the command
# continuously.
__execute_node_command_synchronously() {
  local _node_command=$1
  local _node=${NODES# *}
  __print_node_command_result "" "0" "$_node" ""
  # {
  #   local _result
  #   local _lines=0
  #   while read line; do
  #     if [[ "$_lines" = "0" ]] && [[ "$line" =~ ": " ]]; then
  #       local _node=${line%: *}
  #       line=${line#*: }
  #       echo "  node    : $_node"
  #       echo -e "  response: $line\n"
  #     elif [[ "$_lines" = "0" ]]; then
  #       echo -e "  response: $line\n"
  #     else
  #       echo -e "            $line"
  #     fi
  #     _lines=$(($_lines+1))
  #   done
  # } < <(__execute_node_command "single_node" "$_node" "$_node_command")
  __execute_node_command "single_node" "$_node" "$_node_command"
}


# executes a node command on a given node.
__execute_node_command() {
  local _node_index=$1
  local _node_name=$2
  local _node_command=$3

  local _config; local _config_arg
  local _user=${_node_name%@*}
  local _host=${_node#*@}
  local _path=${_host#*:}
  [[ "$_path" =~ .*\|.* ]] && _config=${_path#*|} || _config=""
  _path=${_path%|*}
  _host=${_host%:*}

  [[ -n "$_config" ]] && _config_arg="--config=${_config}" || _config_arg=""
  [ "${_node_index}" != "single_node" ] && [ "${_node_command}" = "start" ] &&  _config_arg="$_config_arg --short"

  _remote_job="
    set -e
    [ -f ~/.profile ] && source ~/.profile
    cd ${_path}/${APP} $SILENCE
    bin/${APP} ${_node_command} ${_config_arg}
  "

  [ "${_node_index}" = "single_node" ] && local _terminal_option=-t || local _terminal_option=
  ssh $_terminal_option -o ConnectTimeout="$SSH_TIMEOUT" "${_user}@${_host}" "$_remote_job"
  local _status_code="$?"
  [ "${_node_index}" != "single_node" ] && echo -e "\n${_status_code}"
  [[ "$_status_code" = "0" ]] && return 0 || return 1
}

# prints the result from the execution of a node command on a remote node.
__print_node_command_result() {
  local _node_index=$1
  local _status_code=$2
  local _node_name=$3
  local _node_action_response=$4
  local _config; local _config_arg
  local _message=""
  _message="${bldgrn}$NODE_ENVIRONMENT${txtrst} node: $_node_index\n\n"
  echo
  local _user=${_node_name%@*}
  local _host=${_node_name#*@}
  local _path=${_host#*:}
  [[ "$_path" =~ .*\|.* ]] && _config=${_path#*|} || _config=""
  _path=${_path%|*}
  _host=${_host%:*}

  _message="${_message}  user    : $_user\n"
  _message="${_message}  host    : $_host\n"
  _message="${_message}  path    : $_path\n"
  [[ -n "$_config" ]] && _message="${_message}  config  : $_config\n"

  if [[ "$_node_action_response" =~ ": " ]]; then
    local _node=${_node_action_response%: *}
    _node_action_response=${_node_action_response#*: }
    _node=$(echo ${_node} | grep -oe "[^ @]\+@.*")
    _message="${_message}  node    : $_node\n"
  fi

  if [[ "$_status_code" = "0" ]] && [[ -n "$_node_index" ]]; then
    _message="${_message}  response: ${bldylw}${_node_action_response}${txtrst}\n"
  elif [[ -n "$_node_index" ]]; then
    _message="${_message}  response: ${bldred}failed${txtrst}\n";
    [[ -n "$_node_action_response" ]] && {
      local lines=0
      while read line; do
        [[ "$lines" = "0" ]] && _message="${_message}  error   : ${line}\n" || _message="${_message}            ${line}\n"
        lines=$(($lines+1))
      done < <(echo -e "$_node_action_response")
    }
    _message="$_message\n${txtrst}"
  fi
  echo -e "${_message}"
  [[ "$_status_code" = "0" ]] && return 0 || return 1
}



