#!/usr/bin/env bash

set -o errexit
#set -o errtrace
set -o nounset
#set -eou pipefail

# ==============================================================================

VERSION="0.1.0"
AUTHOR="Robert W. Pearce <me@robertwpearce.com> (https://robertwpearce.com)"

SCRIPT_NAME="${0##*/}"
VERSION_SHORT="${SCRIPT_NAME} version ${VERSION}"
VERSION_LONG="\
${VERSION_SHORT}
Author: ${AUTHOR}"

HELP="\
Retry a given command a specified number of times.

USAGE
  ${SCRIPT_NAME} [flags] <command>

FLAGS
  -h, -?, --help
    Print the help menu
  -v, --version
    Print version information
  -s <arg>, --sleep <arg>
    Specify time to wait before retrying command (default: 2)
  -t <arg>, --times <arg>
    Specify number of times to retry command (default: 3)

EXAMPLES
  $ ${SCRIPT_NAME} -h
  $ ${SCRIPT_NAME} --version
  $ ${SCRIPT_NAME} some-cmd-that-may-fail --flag1 --flag2
  $ ${SCRIPT_NAME} --sleep 1 --times 5 some-cmd-that-may-fail --flag1 --flag2
"

# ==============================================================================

function print_help {
  echo "${HELP}"
}

function print_version {
  echo "${VERSION_LONG}"
}

function print_error {
  echo "$1" >&2
}

# ==============================================================================

FLAG_ERR_MSG_TIMES="error: --times requires a number argument > 0"
FLAG_ERR_MSG_SLEEP="error: --sleep requires a number argument >= 0"

function parse_args {
  if [ -z "$*" ]; then
    echo "error: no arguments provided"
    exit 1
  fi

  while :; do
    local flag="${1:-}"
    local flag_val="${2:-}"

    case "$flag" in
      # Help menu
      -h|-\?|--help)
        print_help
        return 0
        ;;
      # Version
      -v|--version)
        print_version
        return 0
        ;;
      # Sleep amount flag
      -s|--sleep)
        if [[ "$flag_val" =~ ^[0-9]+$ ]]; then
          FLAG_SLEEP_NUM=${flag_val}
          shift # past arg
          shift # past val
        else
          print_error "${FLAG_ERR_MSG_SLEEP}"
          return 1
        fi
        ;;
      # Sleep: handle equals syntax
      --sleep=?*)
        flag_val=${1#*=} # Delete everything up to "=" and assign the remainder
        if [[ "$flag_val" =~ ^[0-9]+$ ]]; then
          FLAG_SLEEP_NUM=${flag_val}
          shift # past arg
        else
          print_error "${FLAG_ERR_MSG_SLEEP}"
          return 1
        fi
        ;;
      # Sleep: handle equals syntax wih no value
      --sleep=)
        print_error "${FLAG_ERR_MSG_SLEEP}"
        return 1
        ;;
      # Times to retry flag
      -t|--times)
        if [[ "${flag_val}" =~ ^[0-9]+$ ]]; then
          FLAG_TIMES_NUM=${flag_val}
          shift # past arg
          shift # past val
        else
          print_error "${FLAG_ERR_MSG_TIMES}"
          return 1
        fi
        ;;
      # Times: handle equals syntax
      --times=?*)
        flag_val=${1#*=} # Delete everything up to "=" and assign the remainder
        if [[ "${flag_val}" =~ ^[0-9]+$ ]]; then
          FLAG_TIMES_NUM=${flag_val}
          shift # past arg
        else
          print_error "${FLAG_ERR_MSG_TIMES}"
          return 1
        fi
        ;;
      # Times: handle equals syntax wih no value
      --times=)
        print_error "${FLAG_ERR_MSG_TIMES}"
        return 1
        ;;
      # End of all flags
      --)
        shift # past arg
        shift # past val
        break
        ;;
      # Unknown flag
      -?*)
        print_error "warn: unknown flag (ignored): ${flag}"
        shift # past arg
        ;;
      # Default case, no more options
      *)
        break
        ;;
    esac
  done

  # Set remaining command after parsing flags
  CMD=("$@")

  return 0
}

# ==============================================================================

function run_cmd {
  if (( "${FLAG_TIMES_NUM}" > 0 )); then
    local i

    for ((i = 1; i <= "${FLAG_TIMES_NUM}"; i++)); do
      if "$@"; then
        return 0
      fi

      echo "retry count: ${i}"
      sleep "${FLAG_SLEEP_NUM}"
    done

    echo "retries failed for: $*"
    return 1
  else
    echo "error: --times must be greater than 0"
    return 1
  fi

}

# ==============================================================================

CMD=()
FLAG_TIMES_NUM=3
FLAG_SLEEP_NUM=2

function main {
  parse_args "$@" && \
    run_cmd "${CMD[@]}" && \
    return 0
}

main "$@"