#!/usr/bin/env bash

set -eo pipefail

function append_min_max_heapsize() {
  if [ -n "${MIN_HEAP_SIZE}" ]; then
      export JAVA_OPTS="${JAVA_OPTS} -Xms${MIN_HEAP_SIZE}"
  fi
  if [ -n "${MAX_HEAP_SIZE}" ]; then
      export JAVA_OPTS="${JAVA_OPTS} -Xmx${MAX_HEAP_SIZE}"
  fi
}

function findScriptDir() {
  CURRENT=$PWD

  DIR=$(dirname "$0")
  cd "$DIR" || exit
  TARGET_FILE=$(basename "$0")

  # Iterate down a (possible) chain of symlinks
  while [ -L "$TARGET_FILE" ]
  do
      TARGET_FILE=$(readlink "$TARGET_FILE")
      DIR=$(dirname "$TARGET_FILE")
      cd "$DIR" || exit
      TARGET_FILE=$(basename "$TARGET_FILE")
  done

  SCRIPT_DIR=$(pwd -P)
  # Restore current directory
  cd "$CURRENT" || exit
}

findScriptDir

if [ "$JAVA_HOME" ]; then
    JAVA="$JAVA_HOME/bin/java"
else
    JAVA="$(command -v java 2>/dev/null)"
fi

if [ -z "$JAVA" ]; then
    echo "Cannot find a way to start the JVM: neither JAVA_HOME is set nor the java command is on the PATH"
    exit 1
fi

if [[ -n "${JAVA_OPTS}" ]]; then
    export JAVA_OPTS="${JAVA_OPTS_DEFAULT} ${JAVA_OPTS}"
else
    export JAVA_OPTS="${JAVA_OPTS_DEFAULT}"
fi

export MC_RUNTIME="${SCRIPT_DIR}/../hazelcast-management-center-5.10.0.jar"

function start() {
  # --add-opens flag is required to prevent this issue: https://jira.spring.io/browse/SPR-15859
  # --add-opens flags for suppressing illegal access warn for jdk 11.0.19
  # -Dcom.sun.jndi.ldap.object.trustSerialData=false is required to mitigate CVE-2024-56518

  JAVA_OPTS="\
         --add-opens java.base/java.lang=ALL-UNNAMED \
         --add-opens java.base/sun.nio.ch=ALL-UNNAMED ${JAVA_OPTS} \
         -Dcom.sun.jndi.ldap.object.trustSerialData=false \
      "

  VM_NAME="$(${JAVA} -XshowSettings:properties -version 2>&1 | grep java.vm.name | cut -d "=" -f2)"
  if [[ "$VM_NAME" =~ "OpenJ9" ]]; then
    JAVA_OPTS="--add-exports jdk.management/com.ibm.lang.management.internal=ALL-UNNAMED ${JAVA_OPTS}"
  fi

  if [ -n "${LOGGING_LEVEL}" ]; then
      export JAVA_OPTS="-Dhazelcast.mc.log.level=${LOGGING_LEVEL} ${JAVA_OPTS}"
  fi
  # unset to not break spring boot config binding
  unset LOGGING_LEVEL

  JAVA_OPTS="$* ${JAVA_OPTS}"

  upgradeH2

  if [[ "$OSTYPE" =~ ^linux ]]; then
    # UseContainerSupport is a linux-only java feature
    if [ "${CONTAINER_SUPPORT:-false}" = "false" ] ;then
        echo "Container support disabled. Using manual heap sizing by specifying MIN_HEAP_SIZE, MAX_HEAP_SIZE or custom settings configured by JAVA_OPTS." 1>&2
        append_min_max_heapsize;
    else
        echo "Container support enabled. Using automatic heap sizing. JVM will use up to 80% of the memory limit of the host." 1>&2
        export JAVA_OPTS="${JAVA_OPTS} -XX:+UseContainerSupport -XX:MaxRAMPercentage=80"
    fi
  else
    append_min_max_heapsize;
  fi

  exportClasspath

  if [ -n "${MC_INIT_CMD}" ]; then
     echo "Executing command specified by MC_INIT_CMD." 1>&2
     eval "${MC_INIT_CMD}"
  fi

  if [ -n "${MC_INIT_SCRIPT}" ]; then
      echo "Loading script $MC_INIT_SCRIPT specified by MC_INIT_SCRIPT." 1>&2
      # shellcheck source=/dev/null
      source "${MC_INIT_SCRIPT}"
  fi

  if [ -n "${MC_ADMIN_USER}" ] && [ -n "${MC_ADMIN_PASSWORD}" ]; then
    echo "Creating admin user."  1>&2
    conf user create --lenient=true -n="${MC_ADMIN_USER}" -p="${MC_ADMIN_PASSWORD}" -r=admin
    if [ $? -ne 0 ]; then
      echo "Failed to create user ${MC_ADMIN_USER}." 1>&2
      exit 1
    fi
  fi

  prepare_java_opts_with_masking
  echo "+ exec $JAVA -server -Dloader.path=${MC_CLASSPATH} ${DISPLAY_OPTS[*]} -cp ${MC_RUNTIME} org.springframework.boot.loader.launch.PropertiesLauncher"

  # shellcheck disable=SC2086
  exec $JAVA \
      -server \
      -Dloader.path="${MC_CLASSPATH}" \
      ${FILTERED_JAVA_OPTS[@]} \
      -cp "${MC_RUNTIME}" \
      org.springframework.boot.loader.launch.PropertiesLauncher
}

prepare_java_opts_with_masking() {
  mask_keys=""
  mask_array=()
  java_opts_array=($JAVA_OPTS)
  FILTERED_JAVA_OPTS=()
  DISPLAY_OPTS=()

  for opt in "${java_opts_array[@]}"; do
    if [[ "${opt}" == -DmaskOpts=* ]]; then
      mask_keys="${opt#-DmaskOpts=}"
      IFS=',' read -r -a mask_array <<< "$mask_keys"
    else
      FILTERED_JAVA_OPTS+=("${opt}")
    fi
  done

  DISPLAY_OPTS=()
  for opt in "${FILTERED_JAVA_OPTS[@]}"; do
    masked=false
    for key in "${mask_array[@]}"; do
      if [[ "${opt}" == -D"${key}"=* ]]; then
        DISPLAY_OPTS+=("-D${key}=****")
        masked=true
        break
      fi
    done
    if ! $masked; then
      DISPLAY_OPTS+=("${opt}")
    fi
  done

  # Mask the license key and squeeze extra spaces
  joined_opts="${DISPLAY_OPTS[*]}"
  license_masked_opts=$(sed -E 's/(-Dhazelcast\.mc\.license=)([^ ]{10})[^ ]*([^ ]{5})/\1\2********\3/' <<< "$joined_opts" | tr -s ' ')
  IFS=' ' read -r -a DISPLAY_OPTS <<< "${license_masked_opts}"
}

function conf() {
  exportClasspath

  upgradeH2 --silent-start
  $JAVA ${JAVA_OPTS} \
       -Dloader.path="${MC_CLASSPATH}" \
       -Dloader.main=com.hazelcast.webmonitor.cli.MCConfCommandLine \
       -Dcom.sun.jndi.ldap.object.trustSerialData=false \
       -cp "${MC_RUNTIME}" \
       org.springframework.boot.loader.launch.PropertiesLauncher "$@"
}

function upgradeH2() {
  h2UpgradeJar="${SCRIPT_DIR}/../h2-upgrade-cli-5.10.0.jar"

  if [ -f "$h2UpgradeJar" ]; then
    local overwriteFlag=""
    if [ "$1" == "--overwrite" ]; then
      overwriteFlag="$1"
      shift
    fi
    $JAVA -jar ${JAVA_OPTS} -Dcom.sun.jndi.ldap.object.trustSerialData=false "$h2UpgradeJar" upgrade $overwriteFlag "$@"
  fi
}

function exportClasspath() {
  # shellcheck disable=SC2154
  export USER_LIB="${SCRIPT_DIR}/user-lib"
  if [ -n "${MC_CLASSPATH}" ]; then
      export MC_CLASSPATH="${MC_CLASSPATH//:/,},${USER_LIB}"
  else
      export MC_CLASSPATH="${USER_LIB}"
  fi
}

function stop() {
  PROCESS_MATCHER="\-cp ${MC_RUNTIME} org.springframework.boot.loader.launch.PropertiesLauncher"

  PIDS=$(ps ax | grep "$PROCESS_MATCHER" | grep -v 'grep' | awk '{print $1}' || true)

  if [ -z "$PIDS" ]; then
    echo "No Hazelcast Management Center found to stop"
    exit 1
  else
    kill -s TERM $PIDS
    echo "Stopped Hazelcast Management Centers with the following PIDs:"
    echo "$PIDS"
  fi

}

function version(){
  echo "Version: 5.10.0"
}

function usage() {
  MINOR_VERSION=$(echo "5.10.0" | cut -d '.' -f 1,2)
  SUFFIX=$(echo "5.10.0" | cut -d '-' -f 2- -s)
  if [ ! -z "$SUFFIX" ]; then
    SUFFIX="-$SUFFIX"
  fi
  DOCS_VERSION=$(echo "$MINOR_VERSION$SUFFIX" | tr '[:upper:]' '[:lower:]')
  echo "Global options are:"
  echo "  -h, --help      Show this help message and exit."
  echo "  -V, --version   Print version information and exit."
  echo "Commands"
  echo " start     <java options> Starts Hazelcast Management Center.
  More information: https://docs.hazelcast.com/management-center/$DOCS_VERSION/deploy-manage/system-properties"
  echo " stop      Stops Hazelcast Management Centers launched by hz-mc start"
  echo " conf      Tool for interacting with Hazelcast Management Center configuration"
  echo " upgrade-h2 Migrates H2 db data from previous Management Center installation and overwrites existing \${hazelcast.mc.home}/metadata directory"
}

case "$1" in
  --help|-h)
      usage
      ;;

  --version|-V)
      version
      ;;

  start)
      shift
      start "$@"
      ;;

  stop)
      stop
      ;;

  conf)
      shift
      conf "$@"
      ;;

  upgrade-h2)
      shift
      upgradeH2 --overwrite "$@"
      ;;

  *)
     usage
     exit 1
esac
