#!/usr/bin/env bash

#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# Determine the current working directory
_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Preserve the calling directory
_CALLING_DIR="$(pwd)"
# Options used during compilation
_COMPILE_JVM_OPTS="-Xms2g -Xmx2g -XX:ReservedCodeCacheSize=1g -Xss128m"

if [ "$CI" ]; then
  export MAVEN_CLI_OPTS="--no-transfer-progress --errors --fail-fast"
fi

# Installs any application tarball given a URL, the expected tarball name,
# and, optionally, a checkable binary path to determine if the binary has
# already been installed
## Arg1 - URL
## Arg2 - Tarball Name
## Arg3 - Checkable Binary
install_app() {
  local remote_tarball="$1/$2$4"
  local local_tarball="${_DIR}/$2"
  local binary="${_DIR}/$3"
  local max_attempts=3

  # setup `curl` and `wget` silent options if we're running on Jenkins
  local curl_opts="-L"
  local wget_opts=""
  curl_opts="--progress-bar ${curl_opts}"
  wget_opts="--progress=bar:force ${wget_opts}"

  if [ -z "$3" -o ! -f "$binary" ]; then
    local attempt=1
    while [ "${attempt}" -le "${max_attempts}" ]; do
      # Reuse an already-valid tarball (e.g. one pre-staged for an offline build, or downloaded on
      # a previous attempt); only (re)download when it is missing or not a valid gzip tarball.
      if [ ! -f "${local_tarball}" ] || ! tar -tzf "${local_tarball}" >/dev/null 2>&1; then
        if ! command -v curl >/dev/null 2>&1 && ! command -v wget >/dev/null 2>&1; then
          echo "ERROR: Cannot download $2: neither cURL nor wget is installed." 1>&2
          exit 2
        fi

        # Try curl first; remove its output if it fails so wget below gets a turn.
        rm -f "${local_tarball}"
        if command -v curl >/dev/null 2>&1; then
          echo "exec: curl ${curl_opts} ${remote_tarball}" 1>&2
          curl ${curl_opts} "${remote_tarball}" > "${local_tarball}" || rm -f "${local_tarball}"
        fi

        # Fall back to wget when curl is absent or produced no valid tarball (e.g. a curl-specific
        # proxy/TLS/CA failure that wget would survive).
        if [ ! -f "${local_tarball}" ] || ! tar -tzf "${local_tarball}" >/dev/null 2>&1; then
          if command -v wget >/dev/null 2>&1; then
            echo "exec: wget ${wget_opts} ${remote_tarball}" 1>&2
            rm -f "${local_tarball}"
            wget ${wget_opts} -O "${local_tarball}" "${remote_tarball}" || rm -f "${local_tarball}"
          fi
        fi
      fi

      # Validate the download before trusting it. A flaky Apache mirror can
      # return an HTML page (mirror chooser / error) with HTTP 200, which is
      # not a gzip tarball; extracting it later would fail with a confusing
      # exit code. `tar -tzf` lists the archive without extracting and
      # exits non-zero on a non-tarball body.
      if [ -f "${local_tarball}" ] && tar -tzf "${local_tarball}" >/dev/null 2>&1; then
        if cd "${_DIR}" && tar -xzf "$2"; then
          rm -rf "${local_tarball}"
          return 0
        fi
      fi

      # Drop the invalid download and retry, backing off only between attempts.
      rm -f "${local_tarball}"
      echo "WARN: Download of $2 from $1 was not a valid tarball" \
        "(attempt ${attempt}/${max_attempts}); retrying..." 1>&2
      attempt=$((attempt + 1))
      [ "${attempt}" -le "${max_attempts}" ] && sleep 3
    done

    echo "WARN: Failed to download a valid $2 from $1 after ${max_attempts} attempts." 1>&2
    return 1
  fi
}

# Determine the Maven version from the root pom.xml file and
# install maven under the build/ folder if needed.
install_mvn() {
  local MVN_VERSION=`grep "<maven.version>" "${_DIR}/../pom.xml" | head -n1 | awk -F '[<>]' '{print $3}'`
  MVN_BIN="$(command -v mvn)"
  if [ "$MVN_BIN" ]; then
    local MVN_DETECTED_VERSION="$(mvn --version | head -n1 | awk '{print $3}')"
  fi
  # See simple version normalization: http://stackoverflow.com/questions/16989598/bash-comparing-version-numbers
  function version { echo "$@" | awk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }'; }
  if [ $(version $MVN_DETECTED_VERSION) -ne $(version $MVN_VERSION) ]; then
    # Default to archive.apache.org: it serves the exact tarball
    # deterministically, avoiding the closer.lua mirror redirector which
    # intermittently routes to a mirror that returns an HTML page instead of
    # the binary. Override with APACHE_MIRROR to use a closer mirror.
    local APACHE_MIRROR=${APACHE_MIRROR:-'https://archive.apache.org/dist'}
    local MVN_TARBALL="apache-maven-${MVN_VERSION}-bin.tar.gz"
    local FILE_PATH="maven/maven-3/${MVN_VERSION}/binaries"

    # closer.lua needs the ?action=download query to redirect to a mirror;
    # archive.apache.org and most plain mirrors serve the file directly.
    local MIRROR_URL_QUERY=""
    case "${APACHE_MIRROR}" in
      *closer.lua*) MIRROR_URL_QUERY="?action=download" ;;
    esac

    if ! install_app \
      "${APACHE_MIRROR}/${FILE_PATH}" \
      "${MVN_TARBALL}" \
      "apache-maven-${MVN_VERSION}/bin/mvn" \
      "${MIRROR_URL_QUERY}"; then
      # Last resort: fall back to archive.apache.org, which serves the exact
      # tarball deterministically. Skip if it was already the chosen mirror.
      local ARCHIVE_MIRROR="https://archive.apache.org/dist"
      if [ "${APACHE_MIRROR%/}" != "${ARCHIVE_MIRROR}" ]; then
        echo "WARN: falling back to ${ARCHIVE_MIRROR} to download Maven" 1>&2
        install_app \
          "${ARCHIVE_MIRROR}/${FILE_PATH}" \
          "${MVN_TARBALL}" \
          "apache-maven-${MVN_VERSION}/bin/mvn" \
          "" || { echo "ERROR: Failed to download Maven; please install manually." 1>&2; exit 2; }
      else
        echo "ERROR: Failed to download Maven; please install manually." 1>&2
        exit 2
      fi
    fi

    MVN_BIN="${_DIR}/apache-maven-${MVN_VERSION}/bin/mvn"
  fi
}

install_mvn

cd "${_CALLING_DIR}"

# Set any `mvn` options if not already present
export MAVEN_OPTS=${MAVEN_OPTS:-"$_COMPILE_JVM_OPTS"}

echo "Using \`mvn\` from path: $MVN_BIN" 1>&2
${MVN_BIN} $MAVEN_CLI_OPTS "$@"
