#!/usr/bin/env bash

usage()
{
cat << EOF
GAM installation script.

OPTIONS:
   -h      show help.
   -d      Directory where gam folder will be installed. Default is \$HOME/bin/
   -a      Architecture to install (x86_64, arm64). Default is to detect your arch with "uname -m".
   -o      OS we are running (linux, macos). Default is to detect your OS with "uname -s".
   -b      OS version. Default is to detect on macOS and Linux.
   -l      Just upgrade GAM to latest version. Skips project creation and auth.
   -p      Profile update (true, false). Should script add gam command to environment. Default is true.
   -u      Admin user email address to use with GAM. Default is to prompt.
   -r      Regular user email address. Used to test service account access to user data. Default is to prompt.
   -v      Version to install (latest, prerelease, draft, 3.8, etc). Default is latest.
   -s      Strip gam component from extracted files, files will be downloaded directly to $target_dir
EOF
}

target_dir="$HOME/bin"
target_folder="$target_dir/gam7"
gamarch=$(uname -m)
gamos=$(uname -s)
osversion=""
update_profile=true
upgrade_only=false
gamversion="latest"
adminuser=""
regularuser=""
strip_gam="--strip-components 0"

while getopts "hd:a:o:b:lp:u:r:v:s" OPTION
do
     case $OPTION in
         h) usage; exit;;
         d) target_dir="${OPTARG%/}"; target_folder="$target_dir/gam7";;
         a) gamarch="$OPTARG";;
         o) gamos="$OPTARG";;
         b) osversion="$OPTARG";;
         l) upgrade_only=true;;
         p) update_profile="$OPTARG";;
         u) adminuser="$OPTARG";;
         r) regularuser="$OPTARG";;
         v) gamversion="$OPTARG";;
         s) strip_gam="--strip-components 1"; target_folder="$target_dir";;
         ?) usage; exit;;
     esac
done
target_gam="$target_folder/gam"

update_profile() {
        [ "$2" -eq 1 ] || [ -f "$1" ] || return 1

        grep -F "$alias_line" "$1" > /dev/null 2>&1
        if [ $? -ne 0 ]; then
                echo_yellow "Adding gam alias to profile file $1."
                echo -e "\n$alias_line" >> "$1"
        else
          echo_yellow "gam alias already exists in profile file $1. Skipping add."
        fi
}

echo_red()
{
echo -e "\x1B[1;31m$1"
echo -e '\x1B[0m'
}

echo_green()
{
echo -e "\x1B[1;32m$1"
echo -e '\x1B[0m'
}

echo_yellow()
{
echo -e "\x1B[1;33m$1"
echo -e '\x1B[0m'
}

version_gt()
{
if [ "${1}" = "${2}" ]; then
  true
else
  test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"
fi
}

if [ "$gamversion" == "latest" ]; then
  release_url="https://api.github.com/repos/GAM-team/GAM/releases/latest"
elif [ "$gamversion" == "prerelease" -o "$gamversion" == "draft" ]; then
  release_url="https://api.github.com/repos/GAM-team/GAM/releases"
else
  release_url="https://api.github.com/repos/GAM-team/GAM/releases/tags/v$gamversion"
fi

if [ -z ${GHCLIENT+x} ]; then
  check_type="unauthenticated"
  curl_opts=( )
else
  check_type="authenticated"
  curl_opts=( "$GHCLIENT" )
fi
curl_ver=$(curl --version|head -1|cut -d " " -f 2)
if [[ "${curl_ver:0:4}" < "7.76" ]]; then
  curl_fail=( )
else
  curl_fail=( "--fail-with-body" )
fi
echo_yellow "Checking GitHub URL $release_url for $gamversion GAM release ($check_type)..."
release_json=$(curl \
	--silent \
	"${curl_opts[@]}" \
	-H "Accept: application/vnd.github+json" \
	-H "X-GitHub-Api-Version: 2022-11-28" \
	"$release_url" \
	"${curl_fail[@]}")
curl_exit_code=$?
if [ $curl_exit_code -ne 0 ]; then
  echo_red "ERROR retrieving URL: ${release_json}"
  exit
else
  echo_green "done"
fi

echo_yellow "Calculating download URL for this device..."
# Python is sadly the nearest to universal way to safely handle JSON with Bash
# At least this code should be compatible with just about any Python version ever
# unlike GAM itself. If some users don't have Python we can try grep / sed / etc
# but that gets really ugly
pycode="import json
import sys

attrib = sys.argv[1]
gamversion = sys.argv[2]

release = json.load(sys.stdin)
if type(release) is list:
  for a_release in release:
    if a_release['prerelease'] and gamversion != 'prerelease':
      continue
    elif a_release['draft'] and gamversion != 'draft':
      continue
    release = a_release
    break
try:
  for asset in release['assets']:
    print(asset[attrib])
  #else:
  #  print('ERROR: Attribute: {0} for version {1} not found'.format(attrib, gamversion))
except KeyError:
  print('ERROR: assets value not found in JSON value of:\n\n%s' % release)"

pycmd="python3"
$pycmd -V >/dev/null 2>&1
rc=$?
if (( $rc != 0 )); then
  pycmd="python"
fi
$pycmd -V >/dev/null 2>&1
rc=$?
if (( $rc != 0 )); then
  pycmd="/usr/bin/python3"
fi
$pycmd -V >/dev/null 2>&1
rc=$?
if (( $rc != 0 )); then
  pycmd="python2"
fi
$pycmd -V >/dev/null 2>&1
rc=$?
if (( $rc != 0 )); then
  echo_red "ERROR: No version of python installed."
  exit
fi
# also sort the URLs once so we're evaluating newest OS version first
download_urls=$(echo "$release_json" | \
	        $pycmd -c "$pycode" browser_download_url "$gamversion" | \
	        sort --version-sort --reverse)
if [[ ${download_urls:0:5} = "ERROR" ]]; then
  echo_red "${download_urls}"
  exit
fi

case $gamos in
  [lL]inux)
    gamos="linux"
    download_urls=$(echo -e "$download_urls" | grep -e "-linux-")
    if [ "$osversion" == "" ]; then
      this_glibc_ver=$(ldd --version | awk '/ldd/{print $NF}')
    else
      this_glibc_ver=$osversion
    fi
    echo "This Linux distribution uses glibc $this_glibc_ver"
    case $gamarch in
      x86_64)
	download_urls=$(echo -e "$download_urls" | grep -e "-x86_64-")
	gam_x86_64_glibc_vers=$(echo -e "$download_urls" | \
		grep --only-matching 'glibc[0-9\.]*\.tar\.xz$' \
	        | cut -c 6-9 )
	useglibc="legacy"
        for gam_glibc_ver in $gam_x86_64_glibc_vers; do
	  if version_gt $this_glibc_ver $gam_glibc_ver; then
            useglibc="glibc$gam_glibc_ver"
            echo_green "Using GAM compiled against $useglibc"
            break
          fi
        done
        download_url=$(echo -e "$download_urls" | grep "$useglibc")
	;;
      arm|arm64|aarch64)
        download_urls=$(echo -e "$download_urls" | grep -e "-arm64-\|-aarch64-")
        gam_arm64_glibc_vers=$(echo -e "$download_urls" | \
                grep --only-matching 'glibc[0-9\.]*\.tar\.xz$' | \
                cut -c 6-9)
        useglibc="legacy"
        for gam_glibc_ver in $gam_arm64_glibc_vers; do
          if version_gt $this_glibc_ver $gam_glibc_ver; then
            useglibc="glibc$gam_glibc_ver"
            echo_green "Using GAM compiled against $useglibc"
            break
          fi
        done
        download_url=$(echo -e "$download_urls" | grep "$useglibc")
	;;
      *)
        echo_red "ERROR: this installer currently only supports x86_64 and arm64 Linux. Looks like you're running on $gamarch. Exiting."
        exit
    esac
    ;;
  [Mm]ac[Oo][sS]|[Dd]arwin)
    gamos="macos"
    currentversion=$(sw_vers -productVersion | awk -F '.' '{print $1 "." $2}')
    # override osversion only if it wasn't set by cli arguments
    osversion=${osversion:-${currentversion}}
    # override osversion only if it wasn't set by cli arguments
    download_urls=$(echo -e "$download_urls" | grep -e "-macos")
    case $gamarch in
      x86_64)
        archgrep="-x86_64"
	;;
      arm|arm64|aarch64)
        archgrep="-arm64\|-aarch64"
        ;;
      *)
        echo_red "ERROR: this installer currently only supports x86_64 and arm64 macOS. Looks like you're running on ${gamarch}. Exiting."
        exit
	;;
    esac
    gam_macos_urls=$(echo -e "$download_urls" | \
                     grep -e $archgrep)
    versionless_urls=$(echo -e "$gam_macos_urls" | \
                       grep -e "-macos-")
    if [ "$versionless_urls" == "" ]; then
        # versions after 7.00.38 include macOS version info
        gam_macos_vers=$(echo -e "$gam_macos_urls" | \
                         grep --only-matching -e '-macos[0-9\.]*' | \
                         cut -c 7-10)
        for gam_mac_ver in $gam_macos_vers; do
            if version_gt $currentversion $gam_mac_ver; then
                download_url=$(echo -e "$gam_macos_urls" | grep "$gam_mac_ver")
                echo_green "You are running macOS ${currentversion} Using GAM compiled against ${gam_mac_ver}"
                break
            fi
            done
        if [ -z ${download_url+x} ]; then
	    echo_red "Sorry, you are running macOS ${osversion} but GAM on ${gamarch} requires macOS ${gam_mac_ver} or newer. Exiting."
            exit
	fi
    else
        # versions 7.00.38 and older don't include version info
        case $gamarch in
            x86_64)
	        minimum_version=13
                ;;
            arm|arm64|aarch64)
	        minimum_version=14
                ;;
        esac
        download_url=$(echo -e "$download_urls" | grep -e $archgrep)
        if version_gt "$osversion" "$minimum_version"; then
            echo_green "You are running macOS ${osversion}, good. Downloading GAM from ${download_url}."
        else
          echo_red "Sorry, you are running macOS ${osversion} but GAM on ${gamarch} requires macOS ${minimum_version}. Exiting."
          exit
        fi 
        if [ -z ${download_url+x} ]; then
          echo_red "Sorry, you are running macOS ${currentversion} but GAM on ${gamarch} requires macOS ${minimum_version}. Exiting."
          exit
        fi
    fi
    ;;
  MINGW64_NT*)
    gamos="windows"
    echo "You are running Windows"
    download_url=$(echo -e "$download_urls" | \
                   grep -e "-windows-" | \
                   grep ".zip")
    ;;
  *)
    echo_red "Sorry, this installer currently only supports Linux and macOS. Looks like you're running on ${gamos}. Exiting."
    exit
    ;;
esac

# Temp dir for archive
temp_archive_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir')

# Clean up after ourselves even if we are killed with CTRL-C
trap "rm -rf $temp_archive_dir" EXIT

# hack to grab the end of the URL which should be the filename.
name=$(echo -e "$download_url" | rev | cut -f1 -d "/" | rev)

echo_yellow "Downloading ${download_url} to $temp_archive_dir ($check_type)..."
# Save archive to temp w/o losing our path
(cd "$temp_archive_dir" && curl -O -L -s "${curl_opts[@]}" "$download_url")

mkdir -p "$target_folder"
echo_yellow "Deleting contents of $target_folder/lib"
rm -frv "$target_folder/lib"

echo_yellow "Extracting archive to $target_dir"
if [[ "$name" =~ tar.xz|tar.gz|tar ]]; then
  tar $strip_gam -xf "$temp_archive_dir"/"$name" -C "$target_dir"
elif [[ "$name" == *.zip ]]; then
  unzip -o "${temp_archive_dir}/${name}" -d "${target_dir}"
else
  echo "I don't know what to do with files like ${name}. Giving up."
  exit 1
fi
rc=$?
if (( $rc != 0 )); then
  echo_red "ERROR: extracting the GAM archive with tar failed with error $rc. Exiting."
  exit
else
  echo_green "Finished extracting GAM archive."
fi

# Update profile to add gam command
if [ "$update_profile" = true ]; then
  alias_line="alias gam=\"$target_gam\""
  if [ "$gamos" == "linux" ]; then
    update_profile "$HOME/.bash_aliases" 0 || update_profile "$HOME/.bash_profile" 0 || update_profile "$HOME/.bashrc" 0
    update_profile "$HOME/.zshrc" 0
  elif [ "$gamos" == "macos" ]; then
    update_profile "$HOME/.bash_aliases" 0 || update_profile "$HOME/.bash_profile" 0 || update_profile "$HOME/.bashrc" 0 || update_profile "$HOME/.profile" 1
    update_profile "$HOME/.zshrc" 1
  fi
else
  echo_yellow "skipping profile update."
fi

if [ "$upgrade_only" = true ]; then
  echo_green "Here's information about your GAM upgrade:"
  "$target_gam" version extended
  rc=$?
  if (( $rc != 0 )); then
    echo_red "ERROR: Failed running GAM for the first time with return code $rc. Please report this error to GAM mailing list. Exiting."
    exit
  fi

  echo_green "GAM upgrade complete!"
  exit
fi

while true; do
  read -p "Can you run a full browser on this machine? (usually Y for macOS, N for Linux if you SSH into this machine) " yn
  case $yn in
    [Yy]*)
      "$target_gam" config no_browser false save
      break
      ;;
    [Nn]*)
      "$target_gam" config no_browser true save
      break
      ;;
    *)
      echo_red "Please answer yes or no."
      ;;
  esac
done
echo

project_created=false
while true; do
  read -p "GAM is now installed. Are you ready to set up a Google API project for GAM? (yes or no) " yn
  case $yn in
    [Yy]*)
      if [ "$adminuser" == "" ]; then
        read -p "Please enter your Google Workspace admin email address: " adminuser
      fi
      "$target_gam" create project $adminuser
      rc=$?
      if (( $rc == 0 )); then
        echo_green "Project creation complete."
        project_created=true
        break
      else
        echo_red "Project creation failed. Trying again. Say N to skip project creation."
      fi
      ;;
    [Nn]*)
      echo -e "\nYou can create an API project later by running:\n\ngam create project\n"
      break
      ;;
    *)
      echo_red "Please answer yes or no."
      ;;
  esac
done

admin_authorized=false
while $project_created; do
  read -p "Are you ready to authorize GAM to perform Google Workspace management operations as your admin account? (yes or no) " yn
  case $yn in
    [Yy]*)
      "$target_gam" oauth create $adminuser
      rc=$?
      if (( $rc == 0 )); then
        echo_green "Admin authorization complete."
        admin_authorized=true
        break
      else
        echo_red "Admin authorization failed. Trying again. Say N to skip admin authorization."
      fi
      ;;
     [Nn]*)
       echo -e "\nYou can authorize an admin later by running:\n\ngam oauth create\n"
       break
       ;;
     *)
       echo_red "Please answer yes or no."
       ;;
  esac
done

service_account_authorized=false
while $admin_authorized; do
  read -p "Are you ready to authorize GAM to manage Google Workspace user data and settings? (yes or no) " yn
  case $yn in
    [Yy]*)
      if [ "$regularuser" == "" ]; then
        read -p "Please enter the email address of a regular Google Workspace user: " regularuser
      fi
      echo_yellow "Great! Checking service account scopes.This will fail the first time. Follow the steps to authorize and retry. It can take a few minutes for scopes to PASS after they've been authorized in the admin console."
      "$target_gam" user $regularuser check serviceaccount
      rc=$?
      if (( $rc == 0 )); then
        echo_green "Service account authorization complete."
        service_account_authorized=true
        break
      else
        echo_red "Service account authorization failed. Confirm you entered the scopes correctly in the admin console. It can take a few minutes for scopes to PASS after they are entered in the admin console so if you're sure you entered them correctly, go grab a coffee and then hit Y to try again. Say N to skip admin authorization."
      fi
      ;;
     [Nn]*)
       echo -e "\nYou can authorize a service account later by running:\n\ngam user $adminuser check serviceaccount\n"
       break
       ;;
     *)
       echo_red "Please answer yes or no."
       ;;
  esac
done

echo_green "Here's information about your new GAM installation:"
"$target_gam" version extended
rc=$?
if (( $rc != 0 )); then
  echo_red "ERROR: Failed running GAM for the first time with $rc. Please report this error to GAM mailing list. Exiting."
  exit
fi

echo_green "GAM installation and setup complete!"
if [ "$update_profile" = true ]; then
  echo_green "Please restart your terminal shell or to get started right away run:\n\n$alias_line"
fi
