#!/usr/bin/env bash
# Usage: tfenv resolve-version [<version>]
# Summary: Resolve the version to action based on the environment and optional input token

set -uo pipefail;

####################################
# Ensure we can execute standalone #
####################################

function early_death() {
  echo "[FATAL] ${0}: ${1}" >&2;
  exit 1;
};

if [ -z "${TFENV_ROOT:-""}" ]; then
  # http://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac
  readlink_f() {
    local target_file="${1}";
    local file_name;

    while [ "${target_file}" != "" ]; do
      cd "$(dirname ${target_file})" || early_death "Failed to 'cd \$(dirname ${target_file})' while trying to determine TFENV_ROOT";
      file_name="$(basename "${target_file}")" || early_death "Failed to 'basename \"${target_file}\"' while trying to determine TFENV_ROOT";
      target_file="$(readlink "${file_name}")";
    done;

    echo "$(pwd -P)/${file_name}";
  };

  TFENV_ROOT="$(cd "$(dirname "$(readlink_f "${0}")")/.." && pwd)";
  [ -n "${TFENV_ROOT}" ] || early_death "Failed to 'cd \"\$(dirname \"\$(readlink_f \"${0}\")\")/..\" && pwd' while trying to determine TFENV_ROOT";
else
  TFENV_ROOT="${TFENV_ROOT%/}";
fi;
export TFENV_ROOT;

if [ -n "${TFENV_HELPERS:-""}" ]; then
  log 'debug' 'TFENV_HELPERS is set, not sourcing helpers again';
else
  [ "${TFENV_DEBUG:-0}" -gt 0 ] && echo "[DEBUG] Sourcing helpers from ${TFENV_ROOT}/lib/helpers.sh";
  if source "${TFENV_ROOT}/lib/helpers.sh"; then
    log 'debug' 'Helpers sourced successfully';
  else
    early_death "Failed to source helpers from ${TFENV_ROOT}/lib/helpers.sh";
  fi;
fi;

# Ensure libexec and bin are in $PATH
for dir in libexec bin; do
  case ":${PATH}:" in
    *:${TFENV_ROOT}/${dir}:*) log 'debug' "\$PATH already contains '${TFENV_ROOT}/${dir}', not adding it again";;
    *)
      log 'debug' "\$PATH does not contain '${TFENV_ROOT}/${dir}', prepending and exporting it now";
      export PATH="${TFENV_ROOT}/${dir}:${PATH}";
      ;;
  esac;
done;

# If on MacOS with Homebrew, use GNU grep
# This allows keybase login detection to work on Mac,
# and is required to be able to detect terraform version
# from "required_version" setting in "*.tf" files
check_dependencies;

#####################
# Begin Script Body #
#####################

declare version_requested version regex min_required version_file;

declare arg="${1:-""}";

if [ -n "${arg}" ]; then
  version_requested="${arg}";
  log 'debug' "Version requested on command line: ${version_requested}";
elif [ -n "${TFENV_TERRAFORM_VERSION:-""}" ]; then
  version_requested="${TFENV_TERRAFORM_VERSION}";
  log 'debug' "TFENV_TERRAFORM_VERSION is set: ${TFENV_TERRAFORM_VERSION}";
else
  version_file="$(tfenv-version-file)";
  log 'debug' "Version File: ${version_file}";

  if [ "${version_file}" != "${TFENV_CONFIG_DIR}/version" ]; then
    log 'debug' "Version File (${version_file}) is not the default \${TFENV_CONFIG_DIR}/version (${TFENV_CONFIG_DIR}/version)";
    version_requested="$(read_version_file "${version_file}")" \
      || log 'error' "Failed to open ${version_file}";

  elif [ -f "${version_file}" ]; then
    log 'debug' "Version File is the default \${TFENV_CONFIG_DIR}/version (${TFENV_CONFIG_DIR}/version)";
    version_requested="$(read_version_file "${version_file}")" \
      || log 'error' "Failed to open ${version_file}";

    # Absolute fallback
    if [ -z "${version_requested}" ]; then
      log 'debug' 'Version file had no content. Falling back to "latest"';
      version_requested='latest';
    fi;

  else
    log 'debug' "Version File is the default \${TFENV_CONFIG_DIR}/version (${TFENV_CONFIG_DIR}/version) but it doesn't exist";
    log 'debug' 'No version requested on the command line or in the version file search path. Installing "latest"';
    version_requested='latest';
  fi;
fi;

[[ -n "${version_requested:-""}" ]] \
  && log 'debug' "Version Requested: ${version_requested}" \
  || log 'error' 'Version could not be resolved!';

# Accept a v-prefixed version, but strip the v.
if [[ "${version_requested}" =~ ^v.*$ ]]; then
  log 'debug' "Version Requested is prefixed with a v. Stripping the v.";
  version_requested="${version_requested#v*}";
fi;

if [[ "${version_requested}" =~ ^min-required$ ]]; then
  log 'debug' 'Detecting minimum required version...';
  min_required="$(tfenv-min-required)" \
    || log 'error' 'tfenv-min-required failed';

  if [ -z "${min_required}" ]; then
    log 'debug' 'It was not possible to detect a minimum-required version. Do you have a required_version line present?';
    exit;
  fi;

  log 'debug' "Minimum required version detected: ${min_required}";
  version_requested="${min_required}";
fi;

if [[ "${version_requested}" =~ ^latest-allowed$ ]]; then
  log 'debug' 'Detecting latest allowable version...';
  version_spec="$(grep -h required_version "${TFENV_DIR:-$(pwd)}"/{*.tf,*.tf.json} 2>/dev/null | { IFS='"' read -r _ ver _; echo "${ver%%,*}"; })";
  version_num="$(echo "${version_spec}" | sed -E 's/[^0-9.]+//')";
  log 'debug' "Using ${version_num} from version spec: ${version_spec}";

  case "${version_spec}" in
    '>'*)
      version_requested=latest;
      ;;
    '<='*)
      version_requested="${version_num}";
      ;;
    '~>'*)
      version_without_rightmost="${version_num%.*}";
      version_requested="latest:^${version_without_rightmost}\.";
      ;;
    *)
      # Treat bare version numbers (e.g. "0.12.31") and = prefix as exact pins
      if [[ "${version_num}" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
        version_requested="${version_num}";
      else
        log 'error' "Unsupported version spec: '${version_spec}', only exact versions, >, >=, <=, and ~> are supported.";
      fi;
      ;;
  esac;
  log 'debug' "Determined the requested version to be: ${version_requested}";
fi;

if [[ "${version_requested}" =~ ^latest\:.*$ ]]; then
  version="${version_requested%%\:*}";
  regex="${version_requested##*\:}";
  log 'debug' "Version uses latest keyword with regex: ${regex}";
elif [[ "${version_requested}" =~ ^latest$ ]]; then
  version="${version_requested}";
  regex="^[0-9]\+\.[0-9]\+\.[0-9]\+$";
  log 'debug' "Version uses latest keyword alone. Forcing regex to match stable versions only: ${regex}";
else
  version="${version_requested}";
  regex="^${version_requested}$";
  log 'debug' "Version is explicit: ${version}. Regex enforces the version: ${regex}";
fi;

echo "${version}:${regex}";
