#!/bin/sh

set -eu

SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
ROOT_DIR=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)
LOG_DIR="$ROOT_DIR/logs"
CONSOLE_LOG="$LOG_DIR/backend-console.log"
PID_FILE="$LOG_DIR/backend-java.pid"
TARGET_DIR="$ROOT_DIR/ruoyi-admin/target"
JAR_NAME="ruoyi-admin.jar"
SERVER_PORT=62318
STOP_WAIT_SECONDS=30
APP_MARKER="-Dccdi.backend.root=$ROOT_DIR"
JAVA_OPTS="$APP_MARKER -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError"

timestamp() {
    date "+%Y-%m-%d %H:%M:%S"
}

log_info() {
    printf '[%s] %s\n' "$(timestamp)" "$1"
}

log_error() {
    printf '[%s] %s\n' "$(timestamp)" "$1" >&2
}

usage() {
    cat <<'EOF'
用法: ./bin/restart_java_backend.sh [start|stop|restart|status]

默认动作:
  restart  重新构建后端并重启，随后持续输出运行日志
EOF
}

ensure_command() {
    if ! command -v "$1" >/dev/null 2>&1; then
        log_error "缺少命令: $1"
        exit 1
    fi
}

is_managed_backend_pid() {
    pid="$1"
    if [ -z "${pid:-}" ] || ! kill -0 "$pid" 2>/dev/null; then
        return 1
    fi

    args=$(ps -o args= -p "$pid" 2>/dev/null || true)
    if [ -z "${args:-}" ]; then
        return 1
    fi

    case "$args" in
        *"$APP_MARKER"*"$JAR_NAME"*|*"$JAR_NAME"*"$APP_MARKER"*)
            return 0
            ;;
    esac

    if [ -f "$PID_FILE" ]; then
        file_pid=$(cat "$PID_FILE" 2>/dev/null || true)
        if [ "${file_pid:-}" = "$pid" ]; then
            case "$args" in
                *"java"*"-jar"*"$JAR_NAME"*)
                    return 0
                    ;;
            esac
        fi
    fi

    return 1
}

collect_pids() {
    all_pids=""

    if [ -f "$PID_FILE" ]; then
        file_pid=$(cat "$PID_FILE" 2>/dev/null || true)
        if [ -n "${file_pid:-}" ] && is_managed_backend_pid "$file_pid"; then
            all_pids="$all_pids $file_pid"
        fi
    fi

    marker_pids=$(pgrep -f -- "$APP_MARKER" 2>/dev/null || true)
    if [ -n "${marker_pids:-}" ]; then
        for pid in $marker_pids; do
            if is_managed_backend_pid "$pid"; then
                all_pids="$all_pids $pid"
            fi
        done
    fi

    port_pids=$(lsof -tiTCP:"$SERVER_PORT" -sTCP:LISTEN 2>/dev/null || true)
    if [ -n "${port_pids:-}" ]; then
        for pid in $port_pids; do
            if is_managed_backend_pid "$pid"; then
                all_pids="$all_pids $pid"
            fi
        done
    fi

    unique_pids=""
    for pid in $all_pids; do
        case " $unique_pids " in
            *" $pid "*) ;;
            *)
                unique_pids="$unique_pids $pid"
                ;;
        esac
    done

    printf '%s\n' "$(echo "$unique_pids" | xargs 2>/dev/null || true)"
}

build_backend() {
    log_info "开始构建后端: mvn -pl ruoyi-admin -am clean package -DskipTests"
    (
        cd "$ROOT_DIR"
        mvn -pl ruoyi-admin -am clean package -DskipTests
    )
}

stop_backend() {
    pids=$(collect_pids)

    if [ -z "${pids:-}" ]; then
        log_info "未发现运行中的后端进程"
        rm -f "$PID_FILE"
        return 0
    fi

    log_info "准备停止后端进程: $pids"
    for pid in $pids; do
        kill -TERM "$pid" 2>/dev/null || true
    done

    remaining_pids="$pids"
    elapsed=0
    while [ -n "${remaining_pids:-}" ] && [ "$elapsed" -lt "$STOP_WAIT_SECONDS" ]; do
        sleep 1
        elapsed=$((elapsed + 1))
        remaining_pids=""
        for pid in $pids; do
            if kill -0 "$pid" 2>/dev/null; then
                remaining_pids="$remaining_pids $pid"
            fi
        done
        remaining_pids=$(echo "$remaining_pids" | xargs 2>/dev/null || true)
    done

    if [ -n "${remaining_pids:-}" ]; then
        log_info "仍有进程未退出，执行强制停止: $remaining_pids"
        for pid in $remaining_pids; do
            kill -KILL "$pid" 2>/dev/null || true
        done
    fi

    rm -f "$PID_FILE"
    log_info "后端停止完成"
}

start_backend() {
    mkdir -p "$LOG_DIR"
    touch "$CONSOLE_LOG"

    printf '\n===== %s restart =====\n' "$(timestamp)" >> "$CONSOLE_LOG"

    log_info "开始启动后端，控制台日志输出到: $CONSOLE_LOG"
    if [ ! -f "$TARGET_DIR/$JAR_NAME" ]; then
        log_error "未找到打包产物: $TARGET_DIR/$JAR_NAME"
        exit 1
    fi

    (
        cd "$TARGET_DIR"
        nohup java $JAVA_OPTS -jar "$JAR_NAME" >> "$CONSOLE_LOG" 2>&1 &
        echo $! > "$PID_FILE"
    )

    sleep 3

    starter_pid=$(cat "$PID_FILE" 2>/dev/null || true)
    if [ -z "${starter_pid:-}" ] || ! kill -0 "$starter_pid" 2>/dev/null; then
        log_error "启动命令未保持运行，请检查日志: $CONSOLE_LOG"
        exit 1
    fi

    log_info "启动命令已提交，PID: $starter_pid"
}

status_backend() {
    pids=$(collect_pids)
    if [ -n "${pids:-}" ]; then
        log_info "后端正在运行，进程: $pids"
        return 0
    fi

    port_pids=$(lsof -tiTCP:"$SERVER_PORT" -sTCP:LISTEN 2>/dev/null || true)
    if [ -n "${port_pids:-}" ]; then
        log_info "未发现脚本托管的后端进程，但端口 $SERVER_PORT 被其他进程占用: $port_pids"
    else
        log_info "后端未运行"
    fi
}

follow_logs() {
    mkdir -p "$LOG_DIR"
    touch "$CONSOLE_LOG"
    log_info "持续输出日志中，按 Ctrl+C 仅退出日志查看"
    tail -n 200 -F "$CONSOLE_LOG"
}

start_action() {
    running_pids=$(collect_pids)
    if [ -n "${running_pids:-}" ]; then
        log_error "检测到已有后端进程在运行: ${running_pids}，请先执行 stop 或 restart"
        exit 1
    fi

    build_backend
    start_backend
    follow_logs
}

restart_action() {
    build_backend
    stop_backend
    start_backend
    follow_logs
}

main() {
    ensure_command mvn
    ensure_command lsof
    ensure_command pgrep
    ensure_command ps
    ensure_command tail

    action="${1:-restart}"
    case "$action" in
        start)
            start_action
            ;;
        stop)
            stop_backend
            ;;
        restart)
            restart_action
            ;;
        status)
            status_backend
            ;;
        -h|--help|help)
            usage
            ;;
        *)
            usage
            exit 1
            ;;
    esac
}

main "$@"
