#!/usr/bin/env sh
set -eu

JAR_PATH="${EASYFLOW_JAR_PATH:-/app/artifacts/easyflow.jar}"
CONFIG_PATH="${EASYFLOW_CONFIG_PATH:-file:/app/application.yml}"
LOG_FILE="${EASYFLOW_LOG_FILE:-/app/logs/app.log}"
RESTART_GRACE_SECONDS="${EASYFLOW_JAR_RESTART_GRACE_SECONDS:-30}"
ENTRYPOINT_PRIVS_DROPPED="${EASYFLOW_ENTRYPOINT_PRIVS_DROPPED:-0}"

JAVA_PID=""
WATCHER_PID=""
STOP_REQUESTED=0
RESTART_REASON_FILE="/tmp/easyflow-restart-reason"

log() {
  printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
}

ensure_runtime_permissions() {
  for path in /app/logs /app/data; do
    mkdir -p "$path"
    chown -R easyflow:easyflow "$path"
  done
}

drop_privileges_if_needed() {
  if [ "$(id -u)" -ne 0 ] || [ "$ENTRYPOINT_PRIVS_DROPPED" = "1" ]; then
    return
  fi

  ensure_runtime_permissions
  export EASYFLOW_ENTRYPOINT_PRIVS_DROPPED=1
  exec setpriv --reuid=easyflow --regid=easyflow --init-groups "$0" "$@"
}

ensure_prerequisites() {
  if ! command -v inotifywait >/dev/null 2>&1; then
    log "ERROR: inotifywait not found"
    exit 1
  fi

  if [ ! -f "$JAR_PATH" ]; then
    log "ERROR: easyflow jar not found: $JAR_PATH"
    exit 1
  fi

  mkdir -p "$(dirname "$LOG_FILE")"
}

compute_jar_digest() {
  sha256sum "$JAR_PATH" | awk '{print $1}'
}

stop_watcher() {
  if [ -n "$WATCHER_PID" ] && kill -0 "$WATCHER_PID" 2>/dev/null; then
    kill "$WATCHER_PID" 2>/dev/null || true
    wait "$WATCHER_PID" 2>/dev/null || true
  fi
  WATCHER_PID=""
}

stop_java_process() {
  if [ -z "$JAVA_PID" ] || ! kill -0 "$JAVA_PID" 2>/dev/null; then
    return
  fi

  kill -TERM "$JAVA_PID" 2>/dev/null || true

  remaining="$RESTART_GRACE_SECONDS"
  while kill -0 "$JAVA_PID" 2>/dev/null; do
    if [ "$remaining" -le 0 ]; then
      log "java process did not stop within ${RESTART_GRACE_SECONDS}s, forcing shutdown"
      kill -KILL "$JAVA_PID" 2>/dev/null || true
      break
    fi
    sleep 1
    remaining=$((remaining - 1))
  done
}

handle_shutdown() {
  STOP_REQUESTED=1
  printf '%s' "shutdown" >"$RESTART_REASON_FILE"
  stop_watcher
  stop_java_process
}

trap 'handle_shutdown' TERM INT

start_java() {
  log "starting jar: $JAR_PATH"
  # JAVA_OPTS needs shell word splitting to preserve user-provided JVM args.
  # shellcheck disable=SC2086
  java ${JAVA_OPTS:-} -Djava.security.egd=file:/dev/./urandom -jar "$JAR_PATH" \
    --spring.config.location="$CONFIG_PATH" \
    --logging.file.name="$LOG_FILE" &
  JAVA_PID=$!
}

start_watcher() {
  jar_digest="$1"
  jar_dir="$(dirname "$JAR_PATH")"
  jar_name="$(basename "$JAR_PATH")"

  (
    while true; do
      event_line="$(inotifywait --quiet --event close_write,moved_to --format '%e %f' "$jar_dir")" || exit 0
      event_name="${event_line%% *}"
      event_file="${event_line#* }"

      if [ "$event_file" != "$jar_name" ]; then
        continue
      fi

      if [ ! -f "$JAR_PATH" ]; then
        continue
      fi

      new_digest="$(compute_jar_digest)"
      if [ "$new_digest" = "$jar_digest" ]; then
        continue
      fi

      log "detected jar update (${event_name}), restarting java process"
      printf '%s' "jar_changed" >"$RESTART_REASON_FILE"
      stop_java_process
      exit 0
    done
  ) &

  WATCHER_PID=$!
}

main() {
  drop_privileges_if_needed "$@"
  ensure_prerequisites

  current_digest="$(compute_jar_digest)"

  while true; do
    rm -f "$RESTART_REASON_FILE"
    start_java
    start_watcher "$current_digest"

    set +e
    wait "$JAVA_PID"
    java_status=$?
    set -e

    stop_watcher
    JAVA_PID=""

    restart_reason=""
    if [ -f "$RESTART_REASON_FILE" ]; then
      restart_reason="$(cat "$RESTART_REASON_FILE")"
      rm -f "$RESTART_REASON_FILE"
    fi

    if [ "$STOP_REQUESTED" -eq 1 ] || [ "$restart_reason" = "shutdown" ]; then
      exit 0
    fi

    if [ "$restart_reason" = "jar_changed" ]; then
      ensure_prerequisites
      current_digest="$(compute_jar_digest)"
      log "restarting java with updated jar"
      continue
    fi

    log "java process exited unexpectedly with code $java_status"
    exit "$java_status"
  done
}

main "$@"
