#!/bin/sh


main() {
  . ./check.sh
  if [[ -z "${INFRA_PROFILE:-}" ]]; then
    export INFRA_PROFILE="${1:-default.sxp}"
  else
    export INFRA_PROFILE="${INFRA_PROFILE}"
  fi
  set -eu
  download --check
  local _arch=$(_read arch | tr -d '"')
  local _ext=""
  case "$_arch" in
    *windows*)
      _ext=".exe"
      ;;
  esac
  local _url="https://packy.compiler.company/dist/${_arch}/pack"
  local _stash
  if ! _stash=".stash"; then
    # Because the previous command ran in a subshell, we must manually
    # propagate exit status.
    exit 1
  fi
  # setup default directories
  ensure mkdir -p "${_stash}/src"
  ensure mkdir -p "${_stash}/share/lisp/fasl"
  ensure mkdir -p "${_stash}/tmp"
  ensure mkdir -p "${_stash}/share/store/dist"
  ensure mkdir -p "${_stash}/bin"
  ensure mkdir -p "${_stash}/lib"
  ensure mkdir -p "${_stash}/include"

  cd "${_stash}"
  local _sbcl_pack="sbcl.tar.zst"
  # local _rocksdb_pack="rocksdb.tar.zst"
  local _core_pack="core.tar.zst"
  local _infra_core="infra.core"
  # local _core_src_pack="core-source.tar.zst"
  local _sbcl_url="${_url}/${_sbcl_pack}"
  # local _rocksdb_url="${_url}/${_rocksdb_pack}"
  local _core_url="${_url}/${_core_pack}"
  local _infra_core_url="https://packy.compiler.company/dist/${_arch}/lisp/${_infra_core}"
  # local _core_src_url="${_url}/${_core_src_pack}"
  if [ ! -f "tmp/$_sbcl_pack" ]; then
    ensure download "$_sbcl_url" "tmp/$_sbcl_pack" "$_arch"
    unzstd "tmp/${_sbcl_pack}"
    tar -C tmp -xf "tmp/sbcl.tar"
    cd tmp/sbcl && INSTALL_ROOT=$(realpath ../..) sh install.sh && cd ../..
  fi

  # ensure download "$_core_src_url" "$_core_src_pack" "$_arch"
  # unzstd "${_core_src_pack}"
  # tar -xvf "core-source.tar"
  # mv core src/
  # ensure download "$_rocksdb_url" "${_rocksdb_pack}" "$_arch"
  # unzstd "${_rocksdb_pack}"
  # tar -xvf "pack/rocksdb.tar"
  # cp -rf rocksdb/include/* include/
  # cp -rf rocksdb/*.so lib/

  if [ ! -f "bin/skel" ]; then
  cd tmp
  ensure download "$_core_url" "${_core_pack}" "$_arch"
  unzstd "${_core_pack}"
  tar -xf "core.tar"
  cd ..
  cp -rf tmp/core/bin/* bin/
  cp -rf tmp/core/share/* share/
  fi

  if [ ! -f "share/lisp/$_infra_core" ]; then
    ensure download "$_infra_core_url" "share/lisp/$_infra_core" "$_arch"
  fi

  chmod +x bin/*

  say "bootstrap complete"

  say "starting lisp..."
  cd .. && \
    .stash/bin/sbcl --core .stash/share/lisp/infra.core \
                    --load autogen.lisp \
                    --eval "(infra/autogen:autogen)" \
                    --non-interactive \
                    --no-userinit --no-sysinit

  say "OK"
}

_read() {
  grep ":$1" $INFRA_HOST_CONFIG | cut -d' ' -f 2-
}

# Check if curl supports the --retry flag, then pass it to the curl invocation.
check_curl_for_retry_support() {
  local _retry_supported=""
  # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
  if check_help_for "notspecified" "curl" "--retry"; then
    _retry_supported="--retry 3"
    if check_help_for "notspecified" "curl" "--continue-at"; then
      # "-C -" tells curl to automatically find where to resume the download when retrying.
      _retry_supported="--retry 3 -C -"
    fi
  fi
  RETVAL="$_retry_supported"
}

# Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites
# if support by local tools is detected. Detection currently supports these curl backends:
# GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty.
get_ciphersuites_for_curl() {
  if [ -n "${TLS_CIPHERSUITES-}" ]; then
    # user specified custom cipher suites, assume they know what they're doing
    RETVAL="$TLS_CIPHERSUITES"
    return
  fi
  local _openssl_syntax="no"
  local _gnutls_syntax="no"
  local _backend_supported="yes"
  if curl -V | grep -q ' OpenSSL/'; then
    _openssl_syntax="yes"
  elif curl -V | grep -iq ' LibreSSL/'; then
    _openssl_syntax="yes"
  elif curl -V | grep -iq ' BoringSSL/'; then
    _openssl_syntax="yes"
  elif curl -V | grep -iq ' GnuTLS/'; then
    _gnutls_syntax="yes"
  else
    _backend_supported="no"
  fi
  local _args_supported="no"
  if [ "$_backend_supported" = "yes" ]; then
    # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
    if check_help_for "notspecified" "curl" "--tlsv1.2" "--ciphers" "--proto"; then
      _args_supported="yes"
    fi
  fi
  local _cs=""
  if [ "$_args_supported" = "yes" ]; then
    if [ "$_openssl_syntax" = "yes" ]; then
      _cs=$(get_strong_ciphersuites_for "openssl")
    elif [ "$_gnutls_syntax" = "yes" ]; then
      _cs=$(get_strong_ciphersuites_for "gnutls")
    fi
  fi
  RETVAL="$_cs"
}

# Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites
# if support by local tools is detected. Detection currently supports these wget backends:
# GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty.
get_ciphersuites_for_wget() {
  if [ -n "${TLS_CIPHERSUITES-}" ]; then
    # user specified custom cipher suites, assume they know what they're doing
    RETVAL="$TLS_CIPHERSUITES"
    return
  fi
  local _cs=""
  if wget -V | grep -q '\-DHAVE_LIBSSL'; then
    # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
    if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then
      _cs=$(get_strong_ciphersuites_for "openssl")
    fi
  elif wget -V | grep -q '\-DHAVE_LIBGNUTLS'; then
    # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc.
    if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then
      _cs=$(get_strong_ciphersuites_for "gnutls")
    fi
  fi
  RETVAL="$_cs"
}

check_help_for() {
    local _arch
    local _cmd
    local _arg
    _arch="$1"
    shift
    _cmd="$1"
    shift
    local _category
    if "$_cmd" --help | grep -q 'For all options use the manual or "--help all".'; then
      _category="all"
    else
      _category=""
    fi

    case "$_arch" in
        *darwin*)
        if check_cmd sw_vers; then
            case $(sw_vers -productVersion) in
                10.*)
                    # If we're running on macOS, older than 10.13, then we always
                    # fail to find these options to force fallback
                    if [ "$(sw_vers -productVersion | cut -d. -f2)" -lt 13 ]; then
                        # Older than 10.13
                        echo "Warning: Detected macOS platform older than 10.13"
                        return 1
                    fi
                    ;;
                11.*)
                    # We assume Big Sur will be OK for now
                    ;;
                *)
                    # Unknown product version, warn and continue
                    echo "Warning: Detected unknown macOS major version: $(sw_vers -productVersion)"
                    echo "Warning TLS capabilities detection may fail"
                    ;;
            esac
        fi
        ;;
    esac
    for _arg in "$@"; do
        if ! "$_cmd" --help "$_category" | grep -q -- "$_arg"; then
            return 1
        fi
    done
    true # not strictly needed
}

# Return strong TLS 1.2-1.3 cipher suites in OpenSSL or GnuTLS syntax. TLS 1.2
# excludes non-ECDHE and non-AEAD cipher suites. DHE is excluded due to bad
# DH params often found on servers (see RFC 7919). Sequence matches or is
# similar to Firefox 68 ESR with weak cipher suites disabled via about:config.
# $1 must be openssl or gnutls.
get_strong_ciphersuites_for() {
  if [ "$1" = "openssl" ]; then
    # OpenSSL is forgiving of unknown values, no problems with TLS 1.3 values on versions that don't support it yet.
    echo "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"
  elif [ "$1" = "gnutls" ]; then
    # GnuTLS isn't forgiving of unknown values, so this may require a GnuTLS version that supports TLS 1.3 even if wget doesn't.
    # Begin with SECURE128 (and higher) then remove/add to build cipher suites. Produces same 9 cipher suites as OpenSSL but in slightly different order.
    echo "SECURE128:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS-ALL:-CIPHER-ALL:-MAC-ALL:-KX-ALL:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+AES-128-GCM:+CHACHA20-POLY1305:+AES-256-GCM"
  fi
}

# This wraps curl or wget. Try curl first, if not installed,
# use wget instead.
download() {
  local _dld
  local _ciphersuites
  local _err
  local _status
  local _retry
  if check_cmd curl; then
    _dld=curl
  elif check_cmd wget; then
    _dld=wget
  else
    _dld='curl or wget' # to be used in error message of need_cmd
  fi
  if [ "$1" = --check ]; then
    need_cmd "$_dld"
  elif [ "$_dld" = curl ]; then
    check_curl_for_retry_support
    _retry="$RETVAL"
    get_ciphersuites_for_curl
    _ciphersuites="$RETVAL"
    if [ -n "$_ciphersuites" ]; then
      _err=$(curl $_retry --proto '=https' --tlsv1.2 --ciphers "$_ciphersuites" --silent --show-error --fail --location "$1" --output "$2" 2>&1)
      _status=$?
    else
      echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure"
      if ! check_help_for "$3" curl --proto --tlsv1.2; then
        echo "Warning: Not enforcing TLS v1.2, this is potentially less secure"
        _err=$(curl $_retry --silent --show-error --fail --location "$1" --output "$2" 2>&1)
        _status=$?
      else
        _err=$(curl $_retry --proto '=https' --tlsv1.2 --silent --show-error --fail --location "$1" --output "$2" 2>&1)
        _status=$?
      fi
    fi
    if [ -n "$_err" ]; then
      echo "$_err" >&2
      if echo "$_err" | grep -q 404$; then
        err "installer for platform '$3' not found, this may be unsupported"
      fi
    fi
    return $_status
  elif [ "$_dld" = wget ]; then
    if [ "$(wget -V 2>&1|head -2|tail -1|cut -f1 -d" ")" = "BusyBox" ]; then
      echo "Warning: using the BusyBox version of wget.  Not enforcing strong cipher suites for TLS or TLS v1.2, this is potentially less secure"
      _err=$(wget "$1" -O "$2" 2>&1)
      _status=$?
    else
      get_ciphersuites_for_wget
      _ciphersuites="$RETVAL"
      if [ -n "$_ciphersuites" ]; then
        _err=$(wget --https-only --secure-protocol=TLSv1_2 --ciphers "$_ciphersuites" "$1" -O "$2" 2>&1)
        _status=$?
      else
        echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure"
        if ! check_help_for "$3" wget --https-only --secure-protocol; then
          echo "Warning: Not enforcing TLS v1.2, this is potentially less secure"
          _err=$(wget "$1" -O "$2" 2>&1)
          _status=$?
        else
          _err=$(wget --https-only --secure-protocol=TLSv1_2 "$1" -O "$2" 2>&1)
          _status=$?
        fi
      fi
    fi
    if [ -n "$_err" ]; then
      echo "$_err" >&2
      if echo "$_err" | grep -q ' 404 Not Found$'; then
        err "installer for platform '$3' not found, this may be unsupported"
      fi
    fi
    return $_status
  else
    err "Unknown downloader"   # should not reach here
  fi
}

main "$@" || exit 1
