#!/bin/bash
# 适用于 JDK 17
# ./start-jvm.sh {start|stop|restart|status} {merchant|mini}
# ./start-jvm.sh status all

APP_HOME=$(cd "$(dirname "$0")" && pwd) || exit 1
cd "$APP_HOME" || exit 1

declare -A SERVICE_JARS=(
    [merchant]="merchant-1.0.jar"
    [mini]="mini-program-1.0.jar"
)

COMMAND=${1:-}
SERVICE_NAME=${2:-}

usage() {
    echo "Usage: $0 {start|stop|restart|status} {merchant|mini}"
    echo "       $0 status all"
}

case "$COMMAND" in
    start|stop|restart|status) ;;
    *) usage; exit 1 ;;
esac

case "$SERVICE_NAME" in
    merchant|mini) ;;
    all)
        [ "$COMMAND" = "status" ] || {
            echo "ERROR: all 仅支持 status"
            exit 1
        }
        ;;
    *) usage; exit 1 ;;
esac

# 仅匹配 java 进程中 -jar 后面的准确 JAR 文件名。
get_pids() {
    local jar=${SERVICE_JARS[$1]}

    ps -eo pid=,comm=,args= | awk -v jar="$jar" '
        $2 == "java" {
            for (i = 3; i < NF; i++) {
                if ($i == "-jar") {
                    n = split($(i+1), parts, "/")
                    if (parts[n] == jar) print $1
                    break
                }
            }
        }
    '
}

build_opts() {
    local svc=$1

    JVM_OPTS=(
        -Xms512m
        -Xmx1536m
        -XX:+UseG1GC
        -XX:MaxGCPauseMillis=200
        -XX:+HeapDumpOnOutOfMemoryError
        "-XX:HeapDumpPath=${APP_HOME}/logs/heapdump/${svc}/"
        "-Xlog:gc*,safepoint:file=${APP_HOME}/logs/gc/gc-${svc}.log:time,uptime,level,tags:filecount=5,filesize=20M"
        -Dfile.encoding=UTF-8
        -Duser.timezone=Asia/Shanghai
    )
}

# 重启前检查文件及 JVM 参数，检查失败不会停止旧服务。
preflight() {
    local svc=$1

    if [ ! -f "$APP_HOME/${SERVICE_JARS[$svc]}" ]; then
        echo "ERROR: 找不到 $APP_HOME/${SERVICE_JARS[$svc]}"
        return 1
    fi

    mkdir -p \
        "$APP_HOME/logs/gc" \
        "$APP_HOME/logs/heapdump/$svc" || return 1

    build_opts "$svc"
    java "${JVM_OPTS[@]}" -version || return 1
}

start() {
    local svc=$1 pid
    local -a pids=()

    mapfile -t pids < <(get_pids "$svc")

    if [ "${#pids[@]}" -gt 0 ]; then
        echo "=== $svc 已运行，PID：${pids[*]}"
        [ "${#pids[@]}" -eq 1 ]
        return $?
    fi

    if [ "${2:-}" != "checked" ]; then
        preflight "$svc" || return 1
    fi

    build_opts "$svc"

    echo "=== Starting $svc"
    echo "=== JVM: -Xms512m -Xmx1536m + G1"
    echo "=== 控制台输出丢弃，请通过项目自身日志检查业务状态"

    nohup java "${JVM_OPTS[@]}" \
        -jar "$APP_HOME/${SERVICE_JARS[$svc]}" \
        </dev/null >/dev/null 2>&1 9>&- &

    pid=$!
    sleep 3

    if get_pids "$svc" | grep -qx "$pid"; then
        echo "=== $svc 进程已启动，PID=$pid"
        echo "=== 进程存在不代表业务就绪，请检查接口和业务日志"
    else
        echo "ERROR: $svc 启动失败"
        echo "控制台输出未保存，请检查项目自身日志。"
        echo "必要时在维护窗口前台启动查看错误。"
        return 1
    fi
}

stop() {
    local svc=$1 pid i
    local -a pids=()

    mapfile -t pids < <(get_pids "$svc")

    if [ "${#pids[@]}" -eq 0 ]; then
        echo "=== $svc 未运行"
        return 0
    fi

    if [ "${#pids[@]}" -ne 1 ]; then
        echo "ERROR: $svc 存在多个进程：${pids[*]}"
        echo "请人工确认，不自动停止。"
        return 1
    fi

    pid=${pids[0]}
    echo "=== Stopping $svc，PID=$pid，最多等待 60 秒"
    kill -TERM "$pid" || return 1

    for ((i=0; i<60; i++)); do
        if ! get_pids "$svc" | grep -qx "$pid"; then
            echo "=== $svc 已停止"
            return 0
        fi
        sleep 1
    done

    echo "ERROR: $svc 尚未退出，不自动强杀，已中止后续启动"
    return 1
}

status() {
    local svc=$1 pid
    local -a pids=()

    mapfile -t pids < <(get_pids "$svc")

    if [ "${#pids[@]}" -eq 0 ]; then
        echo "=== $svc 未运行"
        return 1
    fi

    for pid in "${pids[@]}"; do
        echo "=== $svc 正在运行，PID=$pid"
        ps -ww -p "$pid" -o pid,ppid,%cpu,%mem,rss,etime,args

        if command -v jstat >/dev/null 2>&1; then
            echo "=== JVM GC ==="
            jstat -gcutil "$pid" || echo "无法读取 jstat"
        fi
    done

    [ "${#pids[@]}" -eq 1 ]
}

restart() {
    preflight "$1" || return 1
    stop "$1" || return 1
    start "$1" checked
}

# 防止通过本脚本并发执行启停操作。
if [ "$COMMAND" != "status" ]; then
    command -v flock >/dev/null 2>&1 || {
        echo "ERROR: 缺少 flock 命令"
        exit 1
    }

    exec 9>"$APP_HOME/.start-jvm.lock" || exit 1
    flock -n 9 || {
        echo "ERROR: 另一个启停操作正在执行"
        exit 1
    }
fi

if [ "$SERVICE_NAME" = "all" ]; then
    result=0
    for svc in merchant mini; do
        status "$svc" || result=1
    done
    exit "$result"
fi

"$COMMAND" "$SERVICE_NAME"
