#!/usr/bin/env bash
# vim: ft=bash
#
# Runs setup.
#
# Run "bin/setup help" for usage.
#
# —————————————————————————————————————————————————————————————————————————————————————
# NOTE: These setup scripts rely on an open source BASH framework BashMatic.
#       https://github.com/kigster/bashmatic
#
# The framework is pretty light-weight, and is installed in your $HOME/.bashmatic folder.
# You can safely remove that folder after the setup if you wish, although re-running the
# setup will re-install it.
# —————————————————————————————————————————————————————————————————————————————————————

source "bin/deps"

#—————————————————————————————————————————————————————————————————————————————————————————————————————————————
# Private Functions
#—————————————————————————————————————————————————————————————————————————————————————————————————————————————

__setup.actions() {
  local sep="${1}"
  printf "$(array.join "${sep}" $(util.functions-matching "setup." | sed 's/ main//g'))"
}

#—————————————————————————————————————————————————————————————————————————————————————————————————————————————
# Public Functions
#—————————————————————————————————————————————————————————————————————————————————————————————————————————————

setup.rbenv() {
  local rbenv_home="${HOME}/.rbenv"
  local ruby_version="$(cat .ruby-version | tr -d '\n')"

  if [[ -n $(command -v ruby) ]]; then
    local installed_version="$(ruby -e 'puts RUBY_VERSION' | tr -d '\n')"
    if [[ ${installed_version} == ${ruby_version} ]]; then
      info "RUBY already installed, current version: ${bldylw}${ruby_version}"
      info "Setting up rbenv anyway"
    fi
  fi

  [[ -d "${rbenv_home}" && -n "$(command -v rbenv)" ]] || {
    if [[ -z ${CI} ]]; then
      warning "You are on a local system without any RBENV..." \
              "We are going to move your ~/.rbenv to a backup location and" \
              "install it from scratch to ensure it is up to date."
      run.ui.ask "Should we proceed with building a new Ruby and backing up your ~/.rbenv?"
    fi

    [[ -d ${rbenv_home} ]] && {
      local rbenv_backup="${rbenv_home}.backup.$(time.now.db)"
      info "Moving your current ${rbenv_home} to ${bldylw}${rbenv_backup}"
      run "mv -v ${rbenv_home} ${rbenv_backup}"
    }
    run "git clone https://github.com/rbenv/rbenv.git ${rbenv_home}"
    export PATH="${rbenv_home}/bin:${rbenv_home}/shims:${PATH}"
  }

  export PATH="${rbenv_home}/bin:${rbenv_home}/shims:${PATH}"

  [[ $(command -v rbenv) == "${rbenv_home}/bin/rbenv" || $(command -v rbenv) == "/usr/local/bin/rbenv" ]] || {
    error "Can't find rbenv in the PATH — which rbenv returns: $(which rbenv)"
    error "PATH: ${PATH}"
    return 1
  }

  # Set our current ruby version to the desired one, even if it's not yet there.
  # This allows the next block to auto-detect it and skip the remainder.
  run "rbenv global ${ruby_version} || true"
  # see if we even need to install anything:
  local ruby_sdk_marker="$(rbenv versions | grep "${ruby_version}" | cut -d ' ' -f 1)"
  if [[ ${ruby_sdk_marker} == "*" ]]; then
    info "Ruby Version ${ruby_version} is already present, and is RBENV default."
    info "Skipping the rest of RBENV setup."
    return 0
  fi

  eval "$(rbenv init -)"
  run "mkdir -p ${rbenv_home}/plugins"
  run "git clone https://github.com/rbenv/ruby-build.git ${rbenv_home}/plugins/ruby-build"
  run "rbenv rehash"
  hash -r

  run "rbenv install -s ${ruby_version}"
  run "rbenv global ${ruby_version}"
}

setup.gems() {
  for gem in rubocop relaxed-rubocop; do
    gem.install "${gem}"
  done
}

setup.remove-git-hook() {
  set -e
  [[ -L .git/hooks/pre-commit ]] && {
    info 'Removing git commit hook...'
    run "rm -f .git/hooks/pre-commit"
    echo
  }
  set +e
}

setup.git-hook() {
  set -e
  if [[ ! -L .git/hooks/pre-commit ]]; then
    info 'Installing git pre-commit hook'
    run "cd .git/hooks && ln -nfs ../../bin/linter pre-commit && cd -"
  else
    info: "git pre-commit hook is already installed."
  fi
  set +e
}

setup.os-specific() {
  local os="$(uname -s | tr '[:upper:]' '[:lower:]')"
  local setup_script
  setup_script="./bin/setup-${os}"

  if [[ -x "${setup_script}" ]]; then
    set -e
    source "${setup_script}"
    # run it's main function
    eval "setup.${os} \"$*\""
    echo
    if [[ -z ${CI} ]]; then
      info "Please note — to print your Bazel environment run "
      info "❯ ${bldylw}bin/show-env"
    else
      /usr/bin/env bash bin/show-env || true
    fi
  else
    error "Operating system ${os} is not currently supported." >&2
    return 1
  fi
  echo
  return 0
}

setup.main() {
  local action="$1"
  local is_help=0
  local code=0

  [[ "${action}" == "-h" || ${action} == "--help" ]] && {
    action="help"
    is_help=1
  }

  local func="setup.${action}"

  if [[ -n ${action} ]]; then
    if util.is-a-function "${func}"; then
      ((is_help)) || h2 "Executing partial setup for action ${bldylw}${action}"
      shift
      ${func} "$@"
      code=$?
      [[ ${code} -eq 0 ]] && {
        ((is_help)) || success "Setup for ${action} was successful"
      }
      [[ ${code} -ne 0 ]] && error "Error setting up ${action}"
    else
      error "Invalid action provided — '${action}'"
      info "Valid actions are: [ ${bldylw}${action_list}$(txt-info) ]"
      echo
      code=1
    fi
  else
    set +e
    h2 "Installing required development dependencies for working with rules_ruby and Bazel."
    setup.rbenv
    setup.gems
    [[ -z ${CI} ]] && setup.git-hook
    setup.os-specific "$@"
  fi

  ((is_help)) || bin/show-env

  exit ${code}
}

# shellcheck disable=SC2059,SC2155,SC2154
setup.help() {
  trap 'exit 0' INT

  printf "
$(help-header USAGE)
  ${bldblk}# without any arguments runs a complete setup.${clr}
$(help-usage "bin/setup")

  ${bldblk}# alternatively, a sub-setup function name can be passed:${clr}
$(help-usage "bin/setup [ ${action_list} ]")

$(help-header DESCRIPTION:)
  Runs full setup without any arguments.

  Accepts one optional argument — one of the actions that typically run
  as part of setup, with one exception — ${bldylw}remove-git-hook${clr}.
  This action removes the git commit hook installed by the setup.

$(help-header EXAMPLES:)
$(help-example "bin/setup")

  Or, to run only one of the sub-functions (actions), pass
  it as an argument:

$(help-example "bin/setup help")
$(help-example "bin/setup remove-git-hook")

"
}

action_list="$(__setup.actions " | " | sed 's/setup\.//g')"
export action_list

bashmatic.detect-subshell || setup.main "$@"
