#!/usr/bin/env bash

REQUIRED_CONFIGS+=("APP")
REQUIRED_CONFIGS+=("BUILD_HOST")
REQUIRED_CONFIGS+=("BUILD_USER")
REQUIRED_CONFIGS+=("BUILD_AT")
OPTIONAL_CONFIGS+=("FROM")
OPTIONAL_CONFIGS+=("WITH")
OPTIONAL_CONFIGS+=("TO")
OPTIONAL_CONFIGS+=("REBAR_CONFIG")

TO="${TO:=$(get_latest_commit)}"

help() {
  echo -e "
${bldwht}Usage:${txtrst}
  edeliver build upgrade [Options]

${txtbld}Options:${txtrst}
  --with=<release-version-from-store> ${txtylw}The release version to build the upgrade from${txtrst}.
             This release must have been built somewhen by the ${bldwht}build release${txtrst} command
             and must exist in the release store.
  --from=<git-revision> The revision to build the upgrade from ${txtylw}if that release
             is not avaiable any more${txtrst} (in the release store). ${txtylw}Must be in the branch${txtrst}
             passed as ${bldwht}--branch=${txtrst} argument or in the master (default).
  --to=<git-tag> The revision to build the upgrade for. Default is the HEAD
             of the master branch or the branch passed as ${bldwht}--branch=${txtrst} argument.
  --branch=<git-branch> The branch to build. ${txtylw}Default is the master branch${txtrst}.
             ${txtgrn}The branch doesn't need to be pushed to remote${txtrst} before building.
             The revision passed as ${bldwht}--from=${txtrst} or ${bldwht}--to=${txtrst} argument must be in
             that branch.
  --mix-env=<env> Build the upgrade with a custom mix environment.
             Default is ${bldwht}prod${txtrst}.
  --skip-relup-mod Skip modification of the grenerated relup instructions.
             No custom relup instructions are added.
  --relup-mod=<module-name> The name of the module to modify the relup
             if there are several modules. The module must implement the
              Behaviour ${bldwht}Edeliver.Relup.Modification${txtrst}.
  --auto-version=[git-]revision|[git-]commit-count[-all[-branches]|-branch]
             |[git-]branch[-unless-master]|[build-]date|mix-env ${txtgrn}Appends${txtrst} the git
             sha1 revision, the current commit count either across all branches
             (default) or for the current branch, the current branch name
             (optionally only unless it is the master branch) and/or the
             build date as ${txtgrn}metadata to the current upgrade version${txtrst}. To append
             that metadata ${txtgrn}automatically for each build${txtrst} set the ${bldwht}AUTO_VERSION${txtrst}
             env in the config. Values can also be combined using the ${bldwht}+${txtrst} char,
             e.g. ${bldwht}git-revison+git-branch${txtrst} creates version 1.2.3-82a5834-master.
  --increment-version=major|minor|patch  Increments the major, minor or patch
             version ${txtylw}for the current build${txtrst}.
  --set-version=<release-version> Sets the release version for ${txtylw}the current build${txtrst}.

${txtbld}Faster Builds:${txtrst} ${txtblu}(Faster builds might fail during build or during runtime)${txtrst}
  --skip-git-clean Don't build from a clean state for faster builds. Can be
             adjusted by the ${bldwht}GIT_CLEAN_PATHS${txtrst} environment variable. Should be
             at least ${bldwht}rel${txtrst} for elixir releases.
  --skip-mix-clean Skip the 'mix clean step' for faster builds. Can be used
             in addition to ${bldwht}--skip-git-clean${txtrst} Option for incremental builds.

${bldylw}Info:${txtrst}
  Builds an upgrade on the build host and copies it to the release store.
  It can be used by the edeliver ${bldwht}deploy upgrade${txtrst} command to deploy it on
  staging or production hosts.
  ${txtgrn}The built upgrade can be used for hot code upgrades without a restart${txtrst}
  of the node. It is ${txtylw}required${txtrst} that the ${txtylw}release${txtrst} of the running node
  ${txtylw}is available${txtrst} (${bldwht}--with=${txtrst} Option) ${txtylw}or can be built${txtrst} (${bldwht}--from=${txtrst} Option) to be
  able to create the hot code upgrade strategy (relup).

  It ${txtylw}might be necessary to adjust the upgrade instructions${txtrst} (relup)
  by using the ${bldwht}edit relup${txtrst} command.
  "
}


set_build_hosts

run() {
  authorize_hosts
  authorize_release_store_on_build_host
  if [[ -n "$WITH" ]]; then
    [[ "$RELEASE_CMD" = "mix" ]] && local _release_type="*" || local _release_type="release"
    validate_release_is_in_store "$_release_type" $WITH # we can skip init_app_remotely and pushing to remote if release does not exist
    if [[ $? -ne 0 ]]; then
      hint_message "You can build appups also with the --from=<git-tag-or-revision> option\nif the release is not in your release store"; exit 2
    fi
    # push current state
    init_app_remotely
    git_push
    git_reset_remote
    git_clean_remote
  elif [[ -n "$FROM" ]]; then
    # push current state
    init_app_remotely
    git_push
    git_reset_remote
    git_clean_remote
    # checkout old version
    git_checkout_remote $FROM
    # build old release
    erlang_get_and_update_deps
    # ... but don't change version of old release
    local _increment_release_version_option="$INCREMENT_RELEASE_VERSION"
    local _set_release_version_option="$SET_RELEASE_VERSION"
    INCREMENT_RELEASE_VERSION=""
    SET_RELEASE_VERSION=""
    erlang_clean_compile
    erlang_generate_release
    [[ "$RELEASE_CMD" = "rebar" ]] && rename_release_add_version
    [[ "$RELEASE_CMD" = "mix" ]] && __detect_remote_release_dir && __detect_remote_release_version
    # reset the option to auto-increment or setting the version for building the curent release for the upgrade
    INCREMENT_RELEASE_VERSION="$_increment_release_version_option"
    SET_RELEASE_VERSION="$_set_release_version_option"
    OLD_RELEASE_VERSION=${RELEASE_VERSION}
  else
    error_message "no old version specified"; exit 2
  fi

  # checkout new version
  git_checkout_remote $TO
  # build new release
  erlang_get_and_update_deps
  erlang_clean_compile

  # check already now whether the built release will
  # have a different release version as the original release
  if [[ "$RELEASE_CMD" = "mix" ]]; then
    status "Checking version of new release"
    if [[ "$AUTO_RELEASE_VERSION" =~  ^(patch|minor|major)$ ]]; then
      local _version_command="release.version increase $AUTO_RELEASE_VERSION --verbose | grep -oe ' to .*' | cut -b 5-"
    elif [[ "$AUTO_RELEASE_VERSION" == "git" ]]; then
      local _version_command="release.version append-git-revision --verbose | grep -oe ' to .*' | cut -b 5-"
    elif [[ -n "$AUTO_RELEASE_VERSION" ]]; then
      local _version_command="release.version set \"$AUTO_RELEASE_VERSION\" --verbose | grep -oe ' to .*' | cut -b 5-"
    else
      local _version_command="release.version show"
    fi
    local _version_that_will_be_built=$(__remote "
      [ -f ~/.profile ] && source ~/.profile
      cd $BUILD_AT $SILENCE
      MIX_ENV=\"$TARGET_MIX_ENV\" $MIX_CMD $_version_command
    ")
    [[ -n "$WITH" ]] && local _old_version="$WITH" || local _old_version="$OLD_RELEASE_VERSION"
    [[ "$_version_that_will_be_built" = "$_old_version" ]] \
      && __show_upgrade_version_does_not_differ_error_message \
      && exit 1
  fi

  if [[ -n "$WITH" ]] && [[ "$RELEASE_CMD" != "rebar" ]]; then
    # when generating upgrades with `mix release` (exrm) the old
    # release must be uploaded before generating the release.
    # `erlang_generate_release` generates the upgrade then automatically
    RELEASE_DIR="${RELEASE_DIR:-"${DELIVER_TO}/_build/${TARGET_MIX_ENV}/rel/${APP}"}"
    local _release_destination_path="$(dirname $RELEASE_DIR)"
    [[ "$RELEASE_FILE" =~ upgrade\.tar\.gz$ ]] && _release_destination_path="${_release_destination_path%%/}/${APP}"
    upload_release_archive "$_release_destination_path" "$RELEASE_FILE" "$WITH"
    remote_extract_release_archive "$WITH" "$(dirname $RELEASE_DIR)"
    if [[ "$RELEASE_FILE" =~ upgrade\.tar\.gz$ ]]; then
      status "Removing old releases which were included in upgrade package"
      __remote "
        [ -f ~/.profile ] && source ~/.profile
        set -e
        cd \"$(dirname $RELEASE_DIR)/${APP}/releases\" $SILENCE
        for release_dir in \$(ls -1); do
          [[ \"\$release_dir\" != \"$WITH\"  ]] && [[ -n \"\$release_dir\" ]] && [[ -d \"\$release_dir\" ]] && echo \"removing \$release_dir\" $SILENCE && rm -rf \"\$release_dir\" || :
        done
      "
    fi
    __detect_remote_release_dir
    __detect_remote_release_version
    OLD_RELEASE_VERSION=${RELEASE_VERSION}
  fi


  if [[ "$RELEASE_CMD" = "relx" ]]; then
    relx_generate_relup
  else
    erlang_generate_release
  fi

  if [[ -n "$WITH" ]] && [[ "$RELEASE_CMD" = "rebar" ]]; then
    __detect_remote_release_dir
    # backup new release
    rename_release_add_version "new"
    NEW_RELEASE_VERSION=$RELEASE_VERSION;
    OLD_RELEASE_VERSION="$WITH"
    # upload and extract old release
    upload_release_archive "$(dirname $RELEASE_DIR)" "${APP}_${WITH}.release.tar.gz" "$WITH"
    remote_extract_release_archive "$WITH" "$(dirname $RELEASE_DIR)"
    # rename old release
    rename_release_add_version "old" "$WITH"
    # restore new release
    rename_release_remove_version $NEW_RELEASE_VERSION
  fi

  # generate default appups
  [[ "$RELEASE_CMD" = "rebar" ]] && rebar_generate_appup $OLD_RELEASE_VERSION
  # overwrite default appups with custom appups
  [[ "$RELEASE_CMD" = "rebar" ]] && copy_appups_from_release_store_to_build_host $OLD_RELEASE_VERSION
  # generate upgrade package and copy it to release store
  if [[ "$RELEASE_CMD" = "relx" ]]; then
    erlang_archive_release
  elif [[ "$RELEASE_CMD" = "mix" ]]; then
    remove_built_release_archive "$OLD_RELEASE_VERSION"
    RELEASE_VERSION="" # redetect new version
    __detect_remote_release_version
  else
    rebar_generate_upgrade $OLD_RELEASE_VERSION
  fi

  copy_release_to_release_store "upgrade"
}




