#!/bin/sh

# java 可执行文件路径，空则自动探测
java_path=""

# jar 包文件名（含 .jar 后缀）
jar_name=""
# jar 包所在目录
jar_path=""
# jar 进程日志输出路径
log_path=""
# java 环境变量 / 系统属性参数
env_params=""
# java JVM 运行参数
java_params=""
# 启动监控健康检查地址，未配置则跳过监控
monitor_url=""
# 单次健康检查请求超时时间（秒）
monitor_timeout=5
# 健康检查次数
monitor_count=5
# 首次健康检查前延迟时间（秒）
monitor_delay=15
# 健康检查间隔时间（秒）
monitor_interval=10
# 优雅停止等待时间（秒），<=0 则直接 kill -9
stop_wait=30
# stop 与 start 之间的等待时间（秒），最小 1
restart_wait=2
# 启动日志输出方式: file=追加写入log_path, file-overwrite=覆盖旧文件, devnull=丢弃输出
start_log_target=file

# 输出部署日志（带时间戳）
log() {
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}

# 解析命令行参数
# 位置参数: $1=jar名 $2=jar目录 $3=日志路径 $4=环境变量 $5=JVM参数
# 可选参数(--): --monitor-url 健康检查地址
#              --monitor-timeout 单次健康检查超时(秒，默认5)
#              --monitor-count 健康检查次数(默认5)
#              --monitor-delay 首次检查前延迟(秒，默认15)
#              --monitor-interval 检查间隔(秒，默认10)
#              --stop-wait 优雅停止等待(秒，默认10；<=0则直接kill -9)
#              --restart-wait stop与start之间等待(秒，默认2，最小1)
#              --start-log 启动日志输出(file|file-overwrite|devnull，默认file)
#              --java-path java可执行文件路径（未传则按 JAVA_HOME、PATH 自动探测）
parse_args() {
  # jar包名称
  jar_name=$1.jar
  # jar包所在目录
  jar_path=$2
  # jar进程对应输出日志地址
  log_path=$3
  # 环境变量
  env_params=$4
  # java 运行参数
  java_params=$5

  shift 5 2>/dev/null || true
  while [ $# -gt 0 ]; do
    case "$1" in
      --)
        shift
        ;;
      --monitor-url=*)
        monitor_url="${1#*=}"
        shift
        ;;
      --monitor-url)
        monitor_url="$2"
        shift 2
        ;;
      --monitor-timeout=*)
        monitor_timeout="${1#*=}"
        shift
        ;;
      --monitor-timeout)
        monitor_timeout="$2"
        shift 2
        ;;
      --monitor-count=*)
        monitor_count="${1#*=}"
        shift
        ;;
      --monitor-count)
        monitor_count="$2"
        shift 2
        ;;
      --monitor-delay=*)
        monitor_delay="${1#*=}"
        shift
        ;;
      --monitor-delay)
        monitor_delay="$2"
        shift 2
        ;;
      --monitor-interval=*)
        monitor_interval="${1#*=}"
        shift
        ;;
      --monitor-interval)
        monitor_interval="$2"
        shift 2
        ;;
      --stop-wait=*)
        stop_wait="${1#*=}"
        shift
        ;;
      --stop-wait)
        stop_wait="$2"
        shift 2
        ;;
      --restart-wait=*)
        restart_wait="${1#*=}"
        shift
        ;;
      --restart-wait)
        restart_wait="$2"
        shift 2
        ;;
      --start-log=*)
        start_log_target="${1#*=}"
        shift
        ;;
      --start-log)
        start_log_target="$2"
        shift 2
        ;;
      --java-path=*)
        java_path="${1#*=}"
        shift
        ;;
      --java-path)
        java_path="$2"
        shift 2
        ;;
      *)
        shift
        ;;
    esac
  done
}

# 输出已绑定 java 的版本信息（java -version 走 stderr）
log_java_version() {
  log "java 版本:"
  "$java_path" -version 2>&1 | while IFS= read -r line; do
    log "$line"
  done
}

# 解析 java 可执行路径：优先 --java-path，其次 JAVA_HOME/bin/java，最后 PATH 中的 java
resolve_java_path() {
  if [ -n "$java_path" ]; then
    if [ ! -x "$java_path" ]; then
      log "错误: java_path 不可执行: $java_path"
      exit 1
    fi
    log "使用指定 java_path: $java_path"
  elif [ -n "$JAVA_HOME" ] && [ -x "$JAVA_HOME/bin/java" ]; then
    java_path="$JAVA_HOME/bin/java"
    log "从 JAVA_HOME 探测到 java: $java_path"
  else
    detected=$(command -v java 2>/dev/null) || detected=""
    if [ -n "$detected" ] && [ -x "$detected" ]; then
      java_path=$detected
      log "从 PATH 探测到 java: $java_path"
    else
      log "错误: 未探测到 java，请通过 --java-path 指定"
      exit 1
    fi
  fi

  log_java_version
}

# 补全默认 JVM 参数（未传入 java_params 时使用）
resolve_java_params() {
  if [ -z "$java_params" ]; then
    log "java_params 未设置，使用默认设置。-Xms1g -Xmx1g -Xmn250m -Xss256k -server -XX:+HeapDumpOnOutOfMemoryError"
    java_params=' -Xms1g -Xmx1g -Xmn250m -Xss256k -server -XX:+HeapDumpOnOutOfMemoryError '
  fi
}

# 查找并停止 java 进程：先 SIGTERM，超时后 kill -9；stop_wait<=0 时直接 kill -9
stop() {
  pid=$(ps -ef | grep "$jar_name" | grep -v grep | awk '{print $2}')
  log "pid value is :$pid "

  if [ -z "$pid" ]; then
    return 0
  fi

  log "停止java进程,pid:$pid"

  if [ "$stop_wait" -le 0 ] 2>/dev/null; then
    log "stop_wait=${stop_wait}，直接强制终止进程"
    kill -9 $pid 2>/dev/null
    return 0
  fi

  log "发送 SIGTERM，等待 ${stop_wait}s..."
  kill $pid 2>/dev/null

  elapsed=0
  interval=1
  while [ $elapsed -lt $stop_wait ]; do
    if ! kill -0 $pid 2>/dev/null; then
      log "进程已正常退出"
      return 0
    fi
    sleep $interval
    elapsed=$((elapsed + interval))
  done

  if kill -0 $pid 2>/dev/null; then
    log "等待超时，强制终止进程"
    kill -9 $pid 2>/dev/null
  fi
}

# 启动 jar 服务（后台运行，日志输出由 start_log_target 控制）
start() {
  cd "$jar_path" || exit 1
  log "正在启动java进程......"
  log "java_path: $java_path"
  log "java_params:  $java_params "
  log "env_params:   $env_params  "
  log "jar_name & log_path: $jar_name   $log_path "
  log "start_log_target: $start_log_target"

  if [ "$start_log_target" = "devnull" ]; then
    nohup $java_path $java_params $env_params -jar "$jar_name" > /dev/null 2>&1 &
  elif [ "$start_log_target" = "file-overwrite" ]; then
    nohup $java_path $java_params $env_params -jar "$jar_name" > "$log_path" 2>&1 &
  else
    nohup $java_path $java_params $env_params -jar "$jar_name" >> "$log_path" 2>&1 &
  fi
}

# 轮询 monitor_url 检测服务是否启动完成，未配置 monitor_url 则跳过
# 返回 0 表示启动成功，返回 1 表示监控失败
wait_for_startup() {
  if [ -z "$monitor_url" ]; then
    return 0
  fi

  log "正在监控服务启动状态, url: $monitor_url, 首次延迟: ${monitor_delay}s, 次数: ${monitor_count}, 间隔: ${monitor_interval}s, 超时: ${monitor_timeout}s"

  if [ "$monitor_delay" -gt 0 ] 2>/dev/null; then
    log "等待首次检测... ${monitor_delay}s"
    sleep "${monitor_delay}s"
  fi

  attempt=1
  while [ $attempt -le $monitor_count ]; do
    log "第 ${attempt}/${monitor_count} 次检测..."
    http_code=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout "$monitor_timeout" --max-time "$monitor_timeout" "$monitor_url" 2>/dev/null)
    if [ "$http_code" = "200" ]; then
      log "服务启动完成!"
      return 0
    fi

    if [ $attempt -lt $monitor_count ]; then
      log "检测未通过(http_code=${http_code:-000})，${monitor_interval}s 后重试..."
      sleep "${monitor_interval}s"
    fi
    attempt=$((attempt + 1))
  done

  log "服务启动监控失败，已检测 ${monitor_count} 次!"
  return 1
}

# stop 与 start 之间等待，时长由 restart_wait 控制（默认2秒，最小1秒）
wait_restart() {
  wait_seconds=$restart_wait
  if [ "$wait_seconds" -lt 1 ] 2>/dev/null; then
    wait_seconds=1
  fi
  sleep "${wait_seconds}s"
}

# 主流程：解析参数 -> 探测 java -> 停止旧进程 -> 启动新进程 -> 监控启动状态
main() {
  parse_args "$@"
  resolve_java_path
  resolve_java_params
  stop
  wait_restart
  start
  wait_for_startup
}

# 调用示例:
# 基础部署（无健康检查）:
#   ./deploy_jar.sh my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params"
#
# 带健康检查部署:
#   ./deploy_jar.sh my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params" -- \
#     --monitor-url=http://127.0.0.1:9000/monitor/health
#
# 完整参数示例:
#   ./deploy_jar.sh my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params" -- \
#     --monitor-url=http://127.0.0.1:9000/monitor/health \
#     --monitor-timeout=5 \
#     --monitor-count=5 \
#     --monitor-delay=15 \
#     --monitor-interval=10 \
#     --stop-wait=10 \
#     --restart-wait=2 \
#     --start-log=file \
#     --java-path=/usr/bin/java
#
# 指定 java 路径:
#   ./deploy_jar.sh my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params" -- \
#     --java-path=/usr/lib/jvm/java-1.8.0-openjdk/bin/java
#
# 启动时覆盖旧日志文件:
#   ./deploy_jar.sh my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params" -- \
#     --start-log=file-overwrite
#
# 启动时丢弃日志输出:
#   ./deploy_jar.sh my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params" -- \
#     --start-log=devnull
#
# 立即强制停止旧进程:
#   ./deploy_jar.sh my-app /data/web/java_app /data/logs/my-app.log "$env_params" "$java_params" -- \
#     --stop-wait=0
main "$@"
