#!/usr/bin/env bash

__version() {
  echo -e "\n${txtbld}edeliver v$DELIVER_VERSION${txtrst} | $HOMEPAGE\n"
}

__help() {
  __version

  echo -e "${txtbld}Usage:${txtrst}
  edeliver <build-command|deploy-command|node-command|local-command> command-info [Options]
  edeliver --help|--version

  ${txtbld}Build Commands:${txtrst}
  edeliver build release [--revision=<git-revision>|--tag=<git-tag>] [--branch=<git-branch>] [Options]
  edeliver build appups|upgrade --from=<git-tag-or-revision>|--with=<release-version-from-store>
                               [--to=<git-tag-or-revision>] [--branch=<git-branch>] [Options]

  ${txtbld}Deploy Commands:${txtrst}
  edeliver deploy release|upgrade [[to] staging|production] [--version=<release-version>] [Options]

  ${txtbld}Node Commands:${txtrst}
  edeliver start|stop|restart|ping|version [staging|production] [Options]
  edeliver migrate [staging|production] [up|down] [--version=<migration-version>]
  edeliver [show] migrations [on] [staging|production]

  ${txtbld}Local Commands:${txtrst}
  edeliver check release|config [--version=<release-version>]
  edeliver show releases|appups
  edeliver show relup <xyz.upgrade.tar.gz>
  edeliver edit relup|appups [--version=<release-version>]
  edeliver upload|download [release|upgrade <release-version>]|<src-file-name> [<dest-file-name>]
  edeliver increase [major|minor] versions [--from=<git-tag-or-revision>] [--to=<git-tag-or-revision>]
  edeliver unpack|pack release|upgrade [--version=<release-version>]

"
  __exec_if_defined "print_custom_commands_help"
  echo -e "${txtbld}Options:${txtrst}
  -C, --compact         Displays every task as it's run, silences all output. (default mode)
  -V, --verbose         Same as above, does not silence output.
  -P, --plain           Displays every task as it's run, silences all output. No colouring. (CI)
  -D, --debug           Runs in shell debug mode, displays everything.
  -S, --skip-existing   Skip copying release archives if they exist already on the deploy hosts.
  -F, --force           Do not ask, just do, overwrite, delete or destroy everything
      --clean-deploy    Delete the release, lib and erts-* directories before deploying the release
      --skip-git-clean  Don't build from a clean state for faster builds. See $GIT_CLEAN_PATHS env
      --skip-mix-clean  Skip the 'mix clean step' for faster builds. Use in addition to --skip-git-clean
      --start-deploy    Starts the deployed release. If release is running, it is restarted!
      --host=[u@]vwx.yz Run command only on that host, even if different hosts are configured
      --mix-env=<env>   Build with custom mix env $MIX_ENV. Default is 'prod'

${txtbld}Miscellaneous:${txtrst}
  Sometimes you will be asked, if you omit a required argument (e.g --from for the build upgrade task).
  You can overwrite any config at runtime:

  BUILD_HOST=build-2.acme.com edeliver build release
  GIT_CLEAN_PATHS='_build rel priv/generated' edeliver build release
"
}

__available_strategies() {
  __version

  echo -e "${txtbld}Available strategies:${txtrst}"

  for strategy in $STRATEGIES_NAME
  do
    echo "  * $strategy"
  done

  echo ""
}

# parse command
if (( $# )); then
  arg="$1" && shift
  case "${arg}" in
    (build)
      COMMAND="${arg}"
      if (( $# )); then
        arg="$1" && shift
        case "${arg}" in
          (release|upgrade)
            COMMAND_INFO="${arg}"
          ;;
          (appups|appup)
            COMMAND_INFO="appup"
          ;;
          (*)
            __help; error_message "Unknown build type ${arg}. Use release|upgrade\n"; exit 2
          ;;
        esac
      else # no build type found
        __help; error_message "No build type found. Use release|upgrade\n"; exit 2
      fi
    ;;
    (deploy)
      COMMAND="${arg}"
      if (( $# )); then
        arg="$1" && shift
        case "${arg}" in
          (release|upgrade)
            COMMAND_INFO="${arg}"
          ;;
          (*)
            __help; error_message "Unknown deploy type ${arg}. Use release|upgrade\n"; exit 2
          ;;
        esac
      else # no build type found
        __help; error_message "No deploy type found. Use release|upgrade\n"; exit 2
      fi
    ;;
    (start|stop|restart|ping|version|migrate|migrations)
      COMMAND="${arg}"
      if (( $# )); then
        [[ "$1" == "on" ]] && shift
        arg="$1" && shift
        case "${arg}" in
          (*)
            if [[ "${arg}" =~ ^-- ]]; then
              NODE_ENVIRONMENT="staging"
            else
              NODE_ENVIRONMENT="$arg"
            fi
          ;;
        esac
      else # no environment found, use staging as default
        NODE_ENVIRONMENT="staging"
      fi
    ;;
    (check)
      COMMAND="${arg}"
      if (( $# )); then
        arg="$1" && shift
        case "${arg}" in
          (release|config)
            CHECK_CMD="${arg}"
          ;;
          (*)
            __help; error_message "Unknown check type ${arg}. Use release|config\n"; exit 2
          ;;
        esac
      else
        __help; error_message "No check type found. Use release|config\n"; exit 2
      fi
    ;;
    (unpack|pack)
      COMMAND="${arg}"
      if (( $# )); then
        arg="$1" && shift
        case "${arg}" in
          (release|upgrade)
            COMMAND_INFO="${arg}"
          ;;
          (*)
            __help; error_message "Unknown release type ${arg}. Use release|upgrade\n"; exit 2
          ;;
        esac
      else # no build type found
        __help; error_message "No release type found. Use release|upgrade\n"; exit 2
      fi
    ;;
    (show)
      COMMAND="${arg}"
      if (( $# )); then
        arg="$1" && shift
        case "${arg}" in
          (release|releases)
            COMMAND_INFO="releases"
          ;;
          (appup|appups)
            COMMAND_INFO="appups"
          ;;
          (relup|relups)
            COMMAND_INFO="relup"
            if (( $# )); then
              RELEASE_FILE="$1" && shift
            else
              __help; error_message "No upgrade tar.gz passed as argument.\n"; exit 2
            fi
          ;;
          (migrations)
            COMMAND="${arg}"
            if (( $# )); then
              [[ "$1" == "on" ]] && shift
              arg="$1" && shift
              case "${arg}" in
                (*)
                  if [[ "${arg}" =~ ^-- ]]; then
                    NODE_ENVIRONMENT="staging"
                  else
                    NODE_ENVIRONMENT="$arg"
                  fi
                ;;
              esac
            else # no environment found, use staging as default
              NODE_ENVIRONMENT="staging"
            fi
          ;;
          (*)
            __help; error_message "Unknown show command ${arg}. Use releases|appups|relups\n"; exit 2
          ;;
        esac
      else
        __help; error_message "No show command found. Use releases|appups|relups\n"; exit 2
      fi
    ;;
    (upload|download)
      COMMAND="${arg}"
      if (( $# )); then
        arg="$1" && shift
        case "${arg}" in
          (release|upgrade)
            COMMAND_INFO="${arg}"
            if (( $# )); then
              arg="$1" && shift
              VERSION="${arg}"
              (( $# )) && ! [[ "$1" =~ ^- ]] && DESTINATION_FILE="$1" && shift
            else
              __help; error_message "No $COMMAND_INFO version given\n"; exit 2
            fi
          ;;
          (*)
            RELEASE_FILE="${arg}"
            (( $# )) && ! [[ "$1" =~ ^- ]] && DESTINATION_FILE="$1" && shift
          ;;
        esac
      else # no build type found
        __help; error_message "No file to $COMMAND found. Use release|upgrade <version> or file name\n"; exit 2
      fi
    ;;
    (edit)
      COMMAND="${arg}"
      if (( $# )); then
        arg="$1" && shift
        case "${arg}" in
          (appup|appups)
            COMMAND_INFO="appups"
          ;;
          (relup|relups)
            COMMAND_INFO="relup"
          ;;
          (*)
            __help; error_message "Unknown edit command ${arg}. Use appup|relup\n"; exit 2
          ;;
        esac
      else
        __help; error_message "No edit command found. Use appup|relup\n"; exit 2
      fi
    ;;
    (increase)
      COMMAND="${arg}"
      if (( $# )); then
        arg="$1" && shift
        case "${arg}" in
          (versions|version)
            INCREASE_VERSION_TYPE="version"
            COMMAND_INFO="versions"
          ;;
          (minor|major)
            INCREASE_VERSION_TYPE="$arg"
            COMMAND_INFO="versions"
          ;;
          (*)
            __help; error_message "Unknown increase command ${arg}. Use versions|major|minor\n"; exit 2
          ;;
        esac
      else
        INCREASE_VERSION_TYPE="minor"
        COMMAND_INFO="versions"
      fi
    ;;
    (-h|--help)
      __help
      exit 0
    ;;
    (-v|--version)
      __version
      exit 0
    ;;
    (*)
      COMMAND="${arg}";
    ;;
  esac
else # no command found
  __help; error_message "No command found.\n"; exit 2
fi

while (( $# ))
do
  arg="$1" && shift
  case "${arg}" in
    (-C|--compact)
      MODE="compact"
    ;;
    (-D|--debug)
      MODE="debug"
    ;;
    (-V|--verbose)
      MODE="verbose"
    ;;
    (-S|--skip-existing)
      SKIP_COPYING_EXISTING_FILES="skip"
    ;;
    (-P|--plain)
      # this was already captured in libexec/output
      # noop
    ;;
    (-F|--force)
      FORCE="true"
    ;;
    (--clean-deploy)
      CLEAN_DEPLOY="true"
    ;;
    (--skip-git-clean)
      SKIP_GIT_CLEAN="true"
    ;;
    (--skip-mix-clean)
      SKIP_MIX_CLEAN="true"
    ;;
    (--start-deploy)
      START_DEPLOY="true"
    ;;
    (--runs-as-mix-task)
      RUNS_AS_MIX_TASK="true"
    ;;
    (-h|--help)
      __help
      exit 0
    ;;
    (to)
    ;;
    (staging)
      DEPLOY_ENVIRONMENT="staging"
    ;;
    (production)
      DEPLOY_ENVIRONMENT="production"
    ;;
    (check)
      CHECK=true
    ;;
    (*)
      if [[ "${arg}" =~ ^--from= ]]; then
        FROM="${arg##--from=}"
      elif [[ "${arg}" =~ ^--with= ]]; then
        WITH="${arg##--with=}"
      elif [[ "${arg}" =~ ^--to= ]]; then
        TO="${arg##--to=}"
      elif [[ "${arg}" =~ ^--revision= ]]; then
        REVISION="${arg##--revision=}"
      elif [[ "${arg}" =~ ^--tag= ]]; then
        TAG="${arg##--tag=}"
        REVISION="$(git rev-parse $TAG)"
      elif [[ "${arg}" =~ ^--branch= ]]; then
        BRANCH=${arg##--branch=}
      elif [[ "${arg}" =~ ^--version= ]]; then
        VERSION=${arg##--version=}
      elif [[ "${arg}" =~ ^--host= ]]; then
        HOST=${arg##--host=}
      elif [[ "${arg}" =~ ^--mix-env= ]]; then
        TARGET_MIX_ENV=${arg##--mix-env=}
      else
        [[ -n "${COMMAND}" ]] && __exec_if_defined "accepts_custom_command_argument" "${COMMAND}" "${arg}" || hint_message "Unknown argument ${arg} ignored"
      fi
    ;;
  esac
done

case "${MODE}" in
  (compact)
    VERBOSE=""
    SILENCE="&> /dev/null"
  ;;
  (verbose)
    VERBOSE=true
    SILENCE=""
  ;;
  (debug)
    set -x
    VERBOSE=true
    SILENCE=""
  ;;
esac



case "${COMMAND}" in
  (build)
    STRATEGY="erlang-${COMMAND}-${COMMAND_INFO}"
    if [[ ! ${COMMAND_INFO} = "release" ]] && [[ -z "$FROM" ]] && [[ -z "$WITH" ]]; then
      __help; error_message "build command requires --from= or --with argument.\n"; exit 2
    fi
  ;;
  (deploy)
    STRATEGY="erlang-deploy-${COMMAND_INFO}"
  ;;
  (start|stop|restart|ping|version|migrate|migrations)
    STRATEGY="erlang-node-execute"
    NODE_ACTION="${COMMAND}"
  ;;
  (upload|download)
    STRATEGY="erlang-release-store-copy"
  ;;
  (unpack|pack)
    STRATEGY="erlang-unpack-pack"
  ;;
  (increase)
    STRATEGY="erlang-increase-versions"
  ;;
  (*)
    STRATEGY="erlang-${COMMAND}"
    [[ -n "${COMMAND_INFO}" ]] && STRATEGY="${STRATEGY}-${COMMAND_INFO}"
    if ! [[ -f "${BASE_PATH}/strategies/${STRATEGY}" ]]; then
      if ! [[ -f "$ORIGIN_DIR/.deliver/strategies/${STRATEGY}" ]]; then
        if [[ "${COMMAND}" = "publish" ]] && [[ -f "${BASE_PATH}/strategies/publish-edeliver" ]]; then
          STRATEGY="publish-edeliver"
        else
          __help; error_message "Unknown command ${COMMAND}.\n"; exit 2
        fi
      fi
    fi
  ;;
esac

