#!/bin/bash

set -e
set -u
set -o pipefail

esyCommandHelp() {
  cat <<EOF
usage: esy build-release <type> <stage>

Produce/build a release of type <type>.

EOF
}

BINDIR=$(dirname "$0")

# shellcheck source=./realpath.sh
source "$BINDIR/realpath.sh"
# shellcheck source=./esyConfig.sh
source "$BINDIR/esyConfig.sh"
# shellcheck source=./esyRuntime.sh
source "$BINDIR/esyRuntime.sh"

releaseType="$1"
releaseStage="$2"

export releasePackagePath="${PWD}"
export releaseSandboxPath="${releasePackagePath}/r"
export releaseSandboxEjectPath="${releaseSandboxPath}/build-eject"

ESY_EJECT__STORE=$(esyGetStorePathFromPrefix "$releaseSandboxPath")
if [ $? -ne 0 ]; then
  echo "error: $ESY_EJECT__STORE"
  exit 1
else
  export ESY_EJECT__STORE
fi

esyR () {
  ESY__SANDBOX="${releaseSandboxPath}" "$ESY__COMMAND" "$@"
}

makeR () {
  # During "prepare" phase we use the default use Esy store which is likely
  # prewarmed. During "install" phase though we can't do that so we stick to the
  # store inside the release package.
  if [ "$releaseStage" == "prepare" ]; then
    make -s -f "$releaseSandboxEjectPath/Makefile" "$@"
  elif [ "$releaseStage" == "install" ]; then
    make -s ESY_EJECT__PREFIX="$releasePackagePath" -f "$releaseSandboxEjectPath/Makefile" "$@"
  fi
}

esyRelease__installEsy () {
  local log

  # FIXME: During tests we do want to use the current esy executable... consider
  # to override $ESY__VERSION in some way compatible with npm to install from
  # the current esy checkout.
  if [ ! -z "${NODE_ENV+x}" ] && [ "$NODE_ENV" == "test" ]; then
    return
  fi

  echo '*** Installing esy for the release...'
  log=$(npm install --global --prefix "$releasePackagePath/_esy" "esy@$ESY__VERSION")
  if [ $? -ne 0 ]; then
    echo "error: failed to install esy..."
    echo "$log"
    exit 1
  fi
  # overwrite esy command with just installed esy bin
  export ESY__COMMAND="$releasePackagePath/_esy/bin/esy"
}

esyRelease__installDependencies () {
  local log

  echo '*** Installing dependencies...'
  cd "$releaseSandboxPath"
  log=$(esyR install)
  if [ $? -ne 0 ]; then
    echo "error: failed to install dependencies..."
    echo "$log"
    exit 1
  fi
  cd "$releasePackagePath"
}

esyRelease__buildEject () {
  echo '*** Ejecting build environment...'
  cd "$releaseSandboxPath"
  esyR build-eject
  mv "$releaseSandboxPath/node_modules/.cache/_esy/build-eject" "$releaseSandboxEjectPath"
  cd "$releasePackagePath"
}

esyRelease__compressBuildEject () {
  echo '*** Packing build environment...'
  tar -czf "${releaseSandboxPath}.tar.gz" r
  rm -rf "$releaseSandboxPath"
}

esyRelease__decompressBuildEject () {
  echo '*** Unpacking build environment...'
  tar -xzf "${releaseSandboxPath}.tar.gz"
}

esyRelease__build () {
  echo '*** Building the release...'
  cd "$releaseSandboxPath"
  makeR -j build
  cd "$releasePackagePath"
}

esyRelease__compressBuilds () {
  echo '*** Compressing artefacts...'

  local installPaths
  installPaths=$(makeR list-install-paths)

  cd "$releaseSandboxPath"
  echo "$installPaths" | xargs -I {} -P 30 "$releaseSandboxEjectPath/bin/esyExportBuild" {}

  cd "$releasePackagePath"

  mv "$releaseSandboxPath/_export" "$releasePackagePath/_export"

  if [ -f "$releasePackagePath/deleteFromBinaryRelease" ]; then
    cat "$releasePackagePath/deleteFromBinaryRelease" | while read pattern; do
      echo "excluding from binary release: $pattern"
      rm -f $releasePackagePath/_export/$pattern
    done
  fi

  makeR clean

  mv "$releaseSandboxPath/node_modules" "$releaseSandboxPath/node_modules_tmp"
  mkdir -p "$releaseSandboxPath/node_modules"
  mv "$releaseSandboxPath/node_modules_tmp/.cache" "$releaseSandboxPath/node_modules/.cache"
  rm -rf "$releaseSandboxPath/node_modules_tmp"

  local relativeReleaseSandboxPath
  relativeReleaseSandboxPath=$(basename "$releaseSandboxPath")

  tar -czf "${releaseSandboxPath}.tar.gz" "$relativeReleaseSandboxPath"
  rm -rf "$releaseSandboxPath"
}

esyRelease__importIntoInstallationStore () {
  ESY__PREFIX="$releasePackagePath" \
  "$releaseSandboxEjectPath/bin/esyImportBuild" "$@"
}
export -f esyRelease__importIntoInstallationStore

esyRelease__decompressBuilds () {
  if [ -d "$ESY_EJECT__STORE" ]; then
    echo >&2 "error: $ESY_EJECT__STORE already exists. This will not work. It has to be a new directory.";
    exit 1;
  fi

  tar -xzf "$releaseSandboxPath.tar.gz"

  cd "$releasePackagePath"
  makeR esy-root

  ESY__PREFIX="$releaseSandboxPath" \
    find "$releasePackagePath/_export" -name '*.tar.gz' -print0 |\
      xargs -0 -I {} -P 30 bash -c "esyRelease__importIntoInstallationStore {}"
}

esyReleaseInstalled="$releasePackagePath/thisReleaseIsAlreadyInstalled"

esyRelease__checkIfInstalled () {
  if [ -f "$esyReleaseInstalled" ]; then
    exit 0
  fi
}

esyRelease__markAsInstalled () {
  touch "$esyReleaseInstalled"
}

if [ "$releaseType" == "dev" ]; then

  if [ "$releaseStage" == "prepare" ]; then
    true
  elif [ "$releaseStage" == "install" ]; then
    esyRelease__checkIfInstalled
    esyRelease__installEsy
    esyRelease__installDependencies
    esyRelease__buildEject
    esyRelease__build
    esyRelease__markAsInstalled
  fi

elif [ "$releaseType" == "pack" ]; then

  if [ "$releaseStage" == "prepare" ]; then
    esyRelease__installDependencies
    esyRelease__buildEject
    esyRelease__compressBuildEject
  elif [ "$releaseStage" == "install" ]; then
    esyRelease__checkIfInstalled
    esyRelease__decompressBuildEject
    esyRelease__build
    esyRelease__markAsInstalled
  fi

elif [ "$releaseType" == "bin" ]; then

  if [ "$releaseStage" == "prepare" ]; then
    esyRelease__installDependencies
    esyRelease__buildEject
    esyRelease__build
    esyRelease__compressBuilds
  elif [ "$releaseStage" == "install" ]; then
    esyRelease__checkIfInstalled
    esyRelease__decompressBuilds
    esyRelease__markAsInstalled
  fi

fi

