#!/usr/bin/env bash
set -euo pipefail

if ${X_MODE:-false}; then
   set -x
fi

# =====================
# paths
# =====================

export CARGO_DEFAULT_BIN="${HOME}/.cargo/bin"
export BIN_DIR="${BIN_DIR:-"$CARGO_DEFAULT_BIN"}"


# =====================
# logging
# =====================

echoerr() {
   echo "$@" 1>&2
}

tap() {
   local -r x="$(cat)"
   echoerr "$x"
   echo "$x"
}

log::ansi() {
   local bg=false
   case "$@" in
      *reset*) echo "\e[0m"; return 0 ;;
      *black*) color=30 ;;
      *red*) color=31 ;;
      *green*) color=32 ;;
      *yellow*) color=33 ;;
      *blue*) color=34 ;;
      *purple*) color=35 ;;
      *cyan*) color=36 ;;
      *white*) color=37 ;;
   esac
   case "$@" in
      *regular*) mod=0 ;;
      *bold*) mod=1 ;;
      *underline*) mod=4 ;;
   esac
   case "$@" in
      *background*) bg=true ;;
      *bg*) bg=true ;;
   esac

   if $bg; then
      echo "\e[${color}m"
   else
      echo "\e[${mod:-0};${color}m"
   fi
}

_log() {
   local template="$1"
   shift
   echoerr "$(printf "$template" "$@")"
}

_header() {
   local TOTAL_CHARS=60
   local total=$TOTAL_CHARS-2
   local size=${#1}
   local left=$((($total - $size) / 2))
   local right=$(($total - $size - $left))
   printf "%${left}s" '' | tr ' ' =
   printf " $1 "
   printf "%${right}s" '' | tr ' ' =
}

log::header() { _log "\n$(log::ansi bold)$(log::ansi purple)$(_header "$1")$(log::ansi reset)\n"; }
log::success() { _log "$(log::ansi green)✔ %s$(log::ansi reset)\n" "$@"; }
log::error() { _log "$(log::ansi red)✖ %s$(log::ansi reset)\n" "$@"; }
log::warning() { _log "$(log::ansi yellow)➜ %s$(log::ansi reset)\n" "$@"; }
log::note() { _log "$(log::ansi blue)%s$(log::ansi reset)\n" "$@"; }

# TODO: remove
header() {
   echoerr "$*"
   echoerr
}

die() {
   log::error "$@"
   exit 42
}

no_binary_warning() {
   log::note "There's no precompiled binary for your platform: $(uname -a)"
}

installation_finish_instructions() {
   local -r shell="$(get_shell)"
   log::note -e "Finished. To call navi, restart your shell or reload the config file:\n   source ~/.${shell}rc"
   local code
   if [[ "$shell" == "zsh" ]]; then
      code="navi widget ${shell} | source"
   else
      code='source <(navi widget '"$shell"')'
   fi
   log::note -e "\nTo add the Ctrl-G keybinding, add the following to ~/.${shell}rc:\n   ${code}"
}


# =====================
# security
# =====================

sha256() {
   if command_exists sha256sum; then
      sha256sum
   elif command_exists shasum; then
      shasum -a 256
   elif command_exists openssl; then
      openssl dgst -sha256
   else
      log::note "Unable to calculate sha256!"
      exit 43
   fi
}


# =====================
# github
# =====================

latest_version_released() {
   curl -s 'https://api.github.com/repos/denisidoro/navi/releases/latest' \
      | grep -Eo 'releases/tag/v([0-9\.]+)' \
      | sed 's|releases/tag/v||'
}

asset_url() {
   local -r version="$1"
   local -r variant="${2:-}"

   if [[ -n "$variant" ]]; then
      echo "https://github.com/denisidoro/navi/releases/download/v${version}/navi-v${version}-${variant}.tar.gz"
   else
      echo "https://github.com/denisidoro/navi/archive/v${version}.tar.gz"
   fi
}

download_asset() {
   local -r tmp_dir="$(mktemp -d -t navi-install-XXXX)"
   local -r url="$(asset_url "$@")"
   log::note "Downloading ${url}..."
   cd "$tmp_dir"
   curl -L "$url" -o navi.tar.gz
   tar xvzf navi.tar.gz
   mkdir -p "${BIN_DIR}" &>/dev/null || true
   mv "./navi" "${BIN_DIR}/navi"
}

sha_for_asset_on_github() {
   local -r url="$(asset_url "$@")"
   curl -sL "$url" | sha256 | awk '{print $1}'
}

error_installing() {
   log::error "Unable to install navi. Please check https://github.com/denisidoro/navi for alternative installation instructions"
   exit 33
}


# =====================
# code
# =====================

version_from_toml() {
   cat "${NAVI_HOME}/Cargo.toml" \
      | grep version \
      | head -n1 \
      | awk '{print $NF}' \
      | tr -d '"' \
      | tr -d "'"
}


# =====================
# platform
# =====================

command_exists() {
   type "$1" &>/dev/null
}

get_target() {
   local -r unamea="$(uname -a)"
   local -r archi="$(uname -sm)"

   local target
   case "$unamea $archi" in
      *arwin*) target="x86_64-apple-darwin" ;;
      *inux*x86*) target="x86_64-unknown-linux-musl" ;;
      *ndroid*aarch*|*ndroid*arm*) target="aarch64-linux-android" ;;
      *inux*aarch*|*inux*arm*) target="armv7-unknown-linux-musleabihf" ;;
      *) target="" ;;
   esac

   echo "$target"
}

get_shell() {
   echo $SHELL | xargs basename
}


# =====================
# main
# =====================

export_path_cmd() {
   echo
   echo '  export PATH="${PATH}:'"$1"'"'
}

append_to_file() {
   local -r path="$1"
   local -r text="$2"
   if [ -f "$path" ]; then
      echo "$text" >> "$path"
   fi
}

get_navi_bin_path() {
   local file="${BIN_DIR}/navi"
   if [ -f "$file" ]; then
      echo "$file"
      return 0
   fi
   file="${CARGO_DEFAULT_BIN}/navi"
   if [ -f "$file" ]; then
      echo "$file"
      return 0
   fi
}

install_navi() {
   local -r target="$(get_target)"

   if command_exists navi; then
      log::success "navi is already installed"
      exit 0

   elif command_exists brew; then
      brew install navi

   elif command_exists cargo; then
      cargo install navi

   elif [[ -n "$target" ]]; then
      local -r version="$(latest_version_released)"
      download_asset "$version" "$target" || error_installing

   else
      error_installing

   fi

   hash -r 2>/dev/null || true

   local navi_bin_path="$(which navi || get_navi_bin_path)"
   ln -s "$navi_bin_path" "${BIN_DIR}/navi" &>/dev/null || true
   if [ -f "${BIN_DIR}/navi" ]; then
      navi_bin_path="${BIN_DIR}/navi"
   fi

   local -r navi_bin_dir="$(dirname "$navi_bin_path")"

   echoerr
   log::success "Finished"
   log::success "navi is now available at ${navi_bin_path}"
   echoerr

   if echo "$PATH" | grep -q "$navi_bin_path"; then
      :
   else
      local -r cmd="$(export_path_cmd "$navi_bin_path")"
      append_to_file "${HOME}/.bashrc" "$cmd"
      append_to_file "${ZDOTDIR:-"$HOME"}/.zshrc" "$cmd"
      append_to_file "${HOME}/.fishrc" "$cmd"
   fi

   log::note "To call navi, restart your shell or reload your .bashrc-like config file"
   echo
   log::note "Check https://github.com/denisidoro/navi for more info"

   export PATH="${PATH}:${navi_bin_path}"

   return 0
}

(return 0 2>/dev/null) || install_navi "$@"
