#!/usr/bin/env bash

set -e

readonly DEV_SETUP_GIT_BRANCH=master
readonly DEV_SETUP_GIT_URL=git@github.com:BotTech/dev-setup.git
readonly DEV_SETUP_HTTP_URL=https://github.com/BotTech/dev-setup

readonly EXIT_CANNOT_PARSE_SSH_KEYGEN=1
readonly EXIT_GITHUB_SSH_FAILED=2

echo "Bootstrapping dev-setup..."

# Another file descriptor for standard out.
exec 3>&1

test_github_ssh() {
  echo "Testing whether we can connect to GitHub via SSH... "
  set +e
  ssh -T git@github.com
  local github_response="$?"
  set -e
  # This will be 1 if the SSH key has been configured correctly.
  # 255 means that an error occurred in SSH.
  if [[ "${github_response}" -eq 255 ]]; then
    return 1
  else
    return 0
  fi
}

generate_ssh_key() {
  local ssh_keygen_output="$(ssh-keygen -t rsa -b 4096 -C "$(whoami)@$(hostname)" | tee >(cat - >&3))"
  local regex=$'Your identification has been saved in ([^\n]+)\.'
  if [[ "${ssh_keygen_output}" =~ $regex ]]; then
    echo "${BASH_REMATCH[1]}"
    return 0
  else
    >&2 echo "Failed to parse ssh-keygen output"
    exit "${EXIT_CANNOT_PARSE_SSH_KEYGEN}"
  fi
}

get_ssh_key() {
  local private_key=~/.ssh/id_rsa
  if [[ ! -f "${private_key}" ]]; then
    private_key="$(generate_ssh_key)"
  fi
  echo "${private_key}"
  return 0
}

add_ssh_key_macos() {
  local private_key="$1"
  eval "$(ssh-agent -s)"
  cat << EOF >> ~/.ssh/config
Host *
  AddKeysToAgent yes
  UseKeychain yes
  IdentityFile ${private_key}
EOF
  ssh-add -K "${private_key}"
}

add_ssh_key_linux() {
  local private_key="$1"
  eval "$(ssh-agent -s)"
  # TODO: Check that this configuration is correct.
  cat << EOF >> ~/.ssh/config
Host *
  AddKeysToAgent yes
  IdentityFile ${private_key}
EOF
  ssh-add "${private_key}"
}

add_ssh_key_windows() {
  local private_key="$1"
  eval "$(ssh-agent -s)"
  # TODO: Check that this configuration is correct.
  cat << EOF >> ~/.ssh/config
Host *
  AddKeysToAgent yes
  IdentityFile ${private_key}
EOF
  ssh-add "${private_key}"
  # This assumes that the native SSH agent is being used and not the one from cygwin/msys.
  ssh-add "$(cygpath -w "${HOME}/.ssh/id_rsa")" 
}

add_ssh_key() {
  local private_key="$1"
  echo -n "Setting up SSH to GitHub... "
  case "${OSTYPE}" in
    linux-gnu)
      add_ssh_key_linux "${private_key}"
      echo "Success"
      ;;
    darwin*)
      add_ssh_key_macos "${private_key}"
      echo "Success"
      ;;
    cygwin|msys)
      add_ssh_key_windows "${private_key}"
      echo "Success"
      ;;
    *)
      echo "Failed"
      >&2 echo "Cannot add SSH key automatically for your operating system (${os})."
      ;;
  esac
}

open_browser() {
  local url="$1"
  case "${OSTYPE}" in
    linux-gnu)
      xdg-open "${url}" || gnome-open "${url}"
      ;;
    darwin*)
      open "${url}"
      ;;
    cygwin|msys)
      explorer "${url}"
      ;;
    *)
      >&2 echo "Cannot open the web browser for your operating system (${os})."
      ;;
  esac
}

copy_to_clipboard() {
  local to_copy="$1"
  case "${OSTYPE}" in
    linux-gnu)
      # This assumes that X Window is being used.
      xclip < "${to_copy}"
      ;;
    darwin*)
      pbcopy < "${to_copy}"
      ;;
    cygwin|msys)
      clip < "${to_copy}"
      ;;
    *)
      >&2 echo "Cannot copy to the clipboard for your operating system (${os})."
      ;;
  esac
}

add_key_to_github() {
  local private_key="$1"
  local public_key="${private_key}.pub"
  copy_to_clipboard "${public_key}" && echo "Copied ${public_key} to the clipboard."
  echo "Add your SSH public key to GitHub (https://github.com/settings/keys)."
  open_browser "https://github.com/settings/keys"
  read -p "Press enter when done..."
  copy_to_clipboard /dev/null
}

setup_github_ssh() {
  echo "Setting up SSH to GitHub..."
  local private_key="$(get_ssh_key)"
  add_ssh_key "${private_key}"
  add_key_to_github "${private_key}"
  if ! test_github_ssh; then
    >&2 echo "Failed to setup SSH to connect to GitHub. Please report this issue."
    >&2 echo "In the meantime, you can workaround this by setting it up manually by following the instructions on https://help.github.com/en/articles/connecting-to-github-with-ssh"
    exit "${EXIT_GITHUB_SSH_FAILED}"
  fi
}

setup_github() {
  if ! test_github_ssh; then
    setup_github_ssh
  fi
}

clone() {
  local url="${1:-${DEV_SETUP_GIT_URL}}"
  local branch="${2:-${DEV_SETUP_GIT_BRANCH}}"
  local default_dir="$(pwd)/dev-setup"
  local dir
  read -p "Enter directory to clone to (${default_dir}): " dir
  dir="${dir:-$default_dir}"
  git clone "${url}" "${dir}" > /dev/null
  git -C "${dir}" checkout "${branch}" > /dev/null
  echo "${dir}"
}

fork() {
  local url="${DEV_SETUP_HTTP_URL}"
  if [[ "$#" -eq 1 ]]; then
    url="$1"
    shift
  fi
  echo "Fork the repository (${url})."
  open_browser "${url}"
  read -p "Press enter when done..."
  local fork_url
  read -p "Enter the SSH URL to your fork: " fork_url
  local dir
  dir="$( clone "${fork_url}" "$@" )"
  ( "${dir}/bin/init-fork.sh" )
}

dev_setup() {
  local dir
  dir="$( clone "${DEV_SETUP_GIT_URL}" "$@" )"
  ( "${dir}/bin/dev-setup.sh" )
}

setup_github

if [[ "$1" == "fork" ]]; then
  shift
  fork "$@"
elif [[ "$1" == "setup" ]]; then
  shift
  dev_setup "$@"
fi