#!/usr/bin/env bash
set -euo pipefail

JS7_HOME="$(cd "${0%/*}/../bin/.." && pwd)"
export JS7_HOME
. "$JS7_HOME/bin/internal/set-context.sh"
declare classpathString java  # returned by set-context.sh
declare -a standardJavaOptions

config=/var/opt/js7/agent/config
data=/var/opt/js7/agent/data
httpPort=127.0.0.1:4444
httpsPort=4443
agentOptions=()
javaOptions=(-XX:+UseStringDeduplication)
log4Async=false
terminateCommand='{ "TYPE": "ShutDown" }'

for arg in "$@"; do
  case "$arg" in
    --rmx-port=*)
      a="${arg#*=}"
      javaOptions+=(
        "-Dcom.sun.management.jmxremote"
        "-Dcom.sun.management.jmxremote.ssl=false"
        "-Dcom.sun.management.jmxremote.authenticate=false"
        "-Dcom.sun.management.jmxremote.port=$a")
      ;;
    --debug-port=*)
      a="${arg#*=}"
      javaOptions+=("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$a")
      ;;
    --java-option=*)
      a="${arg#*=}"
      javaOptions+=("$a")
      ;;
    -J*)
      a="${arg#-J}"
      javaOptions+=("$a")
      ;;
    --directory=*)
      config="${arg#*=}"/config
      data="${arg#*=}"/data
      ;;
    --config-directory=*)
      config="${arg#*=}"
      ;;
    --data-directory=*)
      data="${arg#*=}"
      ;;
    --http-port=*)
      httpPort="${arg#*=}"
      ;;
    --https-port=*)
      httpsPort="${arg#*=}"
      ;;
    --log4j-async)
      log4Async=true
      ;;
    *)
      agentOptions+=("$arg")
      ;;
  esac
done

if [ ! -d "$config" ]; then :
  echo "No such directory: --config-directory=$config"
  exit 1
fi
config="$(toSystemPath "$config")"

if [ ! -d "$data" ]; then :
  echo "No such directory: --data-directory=$data"
  exit 1
fi
data="$(toSystemPath "$data")"

logs="$data/logs"
[ -d "$logs" ] || mkdir "$logs"

stateDir="$data/state"
[ -d "$stateDir" ] || mkdir "$stateDir"

workDir="$data/work"
[ -d "$workDir" ] || mkdir "$workDir"

# Copy stdout and stderr to stdouterr.log
if [ -e "$logs/stdouterr.log" ]; then
  echo >>"$logs/stdouterr.log" \
    -e "\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
fi
exec &> >(exec tee -a "$logs"/stdouterr.log)

javaOptions+=("-Djs7.log4j.directory=$logs")  # Used in logging configuration
if [ -f "$config/log4j2.xml" ]; then :
  javaOptions+=("-Dlog4j.configurationFile=$config/log4j2.xml")
fi
if $log4Async; then :
  javaOptions+=("-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector")
  javaOptions+=("-Dlog4j2.asyncLoggerWaitStrategy=Block")  # Uses less CPU when idling than default "Timeout"
  javaOptions+=("-Djs7.log4j.immediateFlush=false")
fi

agentOptions+=("--config-directory=$config")
agentOptions+=("--data-directory=$data")
[ -z "$httpPort" ] || agentOptions+=("--http-port=$httpPort")
[ -z "$httpsPort" ] || agentOptions+=("--https-port=$httpsPort")

crashKillScript=$([ -n "$data" ] && echo "$data/kill_tasks_after_crash.sh")
log "crashKillScript=$crashKillScript"
[ -z "$crashKillScript" ] || rm -f "$crashKillScript"

while true; do
  loopJavaOptions=()

  if [[ "$JAVA_VERSION" =~ ([0-9]+).* && ${BASH_REMATCH[1]} -ge 13 ]]; then
    [ -d "$data/cache" ] || mkdir "$data/cache"
    jsaFile="$data/cache/js7-subagent-$(cat "$JS7_HOME/VERSION").jsa"
    if [[ "$JAVA_VERSION" =~ ([0-9]+).* && ${BASH_REMATCH[1]} -ge 19 ]]; then
      loopJavaOptions+=("-XX:SharedArchiveFile=$jsaFile" -XX:+AutoCreateSharedArchive)
    elif [ -f "$jsaFile" ]; then
      loopJavaOptions+=("-XX:SharedArchiveFile=$jsaFile" -Xshare:auto)
    else
      loopJavaOptions+=("-XX:ArchiveClassesAtExit=$jsaFile" -Xshare:auto)
    fi
  fi

  # JS7 will delete the maybeDuplicateStartFile after lock file as been acquired
  maybeDuplicateStartFile="$workDir/maybe-duplicate-start-$$"
  touch "$maybeDuplicateStartFile"

  execute=(
    "$java"
    "${javaOptions[@]}"
    "${loopJavaOptions[@]}"
    "${standardJavaOptions[@]}"
    -classpath "$classpathString"
    js7.agent.main.AgentMain
    "${agentOptions[@]}")

  log "${execute[@]}"
  "${execute[@]}" &

  pid=$!
  log "PID $pid"

  terminate() {
    echo "⚡️ $1"
    log "$1: ShutDown JS7 Subagent"

    if [ "$SHELL" = "/bin/bash" ]; then
      "$JS7_HOME"/bin/js7-client --data-directory="$data" "$terminateCommand" &
    else
      "$java" -Xmx50m \
        -Dlog4j.configurationFile=js7/agent/client/main/log4j2.xml \
        "${standardJavaOptions[@]}" \
        -classpath "$classpathString" \
        js7.agent.client.main.AgentClientMain \
        --data-directory="$data" \
        http://127.0.0.1:"$httpPort" \
        "$terminateCommand" \
        >/dev/null &
    fi

    wait $pid
    returnCode=$?
  }

  returnCode=""
  trap "terminate SIGTERM" SIGTERM
  trap "terminate SIGINT" SIGINT
  if wait $pid; then
    returnCode=${returnCode:-$?}
  else
    returnCode=${returnCode:-$?}
  fi
  trap - SIGTERM SIGINT

  if [[ $returnCode -eq 0 ]] || [[ $returnCode -eq 97 ]]; then
    log "JS7 Subagent has been shut down"
  else
    log "JS7 Subagent exited with exit code $returnCode"
  fi

  # Run $crashKillScript only when maybeDuplicateStartFile has been deleted (no duplicate start)
  if [ ! -e "$maybeDuplicateStartFile" ] && [ -s "$crashKillScript" ]; then :
    ps fux || true
    log "Executing crash kill script $crashKillScript:"
    cat "$crashKillScript"
    (. "$crashKillScript" || true)
    ps fux || true
  fi

  if [[ $returnCode -eq 0 ]] || [[ $returnCode -ge 97 ]] && [[ $returnCode -le 99 ]]; then
    log "Clear $workDir"
    find "$workDir" -mindepth 1 -delete ! -type d -printf "Removed %p\n"
  fi

  if [[ $returnCode -eq 97 ]] || [[ $returnCode -eq 98 ]]; then
    log "Starting again"
  else
    exit $returnCode
  fi
done
