#!/bin/bash
################################################################################
# Java应用检测模块
# 功能: 检测Java容器状态、版本、JVM内存、GC状态等
# 作者: Claude Code
# 日期: 2026-07-20
################################################################################

# 获取脚本所在目录并加载依赖
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LIB_DIR="${LIB_DIR:-/tmp/check_modules}"

# 加载配置文件和通用函数库
if [ -f "$LIB_DIR/lib/config.sh" ]; then
    source "$LIB_DIR/lib/config.sh"
else
    echo "ERROR: 配置文件不存在: $LIB_DIR/lib/config.sh"
    exit 1
fi

if [ -f "$LIB_DIR/lib/common.sh" ]; then
    source "$LIB_DIR/lib/common.sh"
else
    echo "ERROR: 通用函数库不存在: $LIB_DIR/lib/common.sh"
    exit 1
fi

# Java容器名称
JAVA_CONTAINER="${CONTAINERS[java]}"

# ==================== 辅助函数 ====================

# 在Java容器中执行命令
java_exec() {
    docker exec "$JAVA_CONTAINER" $@ 2>&1
}

# ==================== 检测函数 ====================

# 检测Java容器状态（使用模糊匹配）
check_java_container() {
    local container_status="未运行"
    local level="严重"

    if command -v docker &> /dev/null; then
        # 使用当前JAVA_CONTAINER变量值进行检测
        if docker ps --format "{{.Names}}" | grep -q "^${JAVA_CONTAINER}$"; then
            container_status="运行中"
            level="正常"
        elif docker ps --format "{{.Names}}" | grep -qi "${JAVA_CONTAINER}"; then
            container_status="运行中"
            level="正常"
        else
            # 检查容器是否存在但未运行
            if docker ps -a --format "{{.Names}}" | grep -qi "${JAVA_CONTAINER}"; then
                container_status="已停止"
                level="警告"
            fi
        fi
    fi

    output_result "JAVA_CONTAINER_STATUS" "$container_status"
    output_result "JAVA_CONTAINER_LEVEL" "$level"

    if [ "$level" != "正常" ]; then
        return 1
    fi

    return 0
}

# 检测Java版本
check_java_version() {
    local version=""

    version=$(java_exec java -version 2>&1 | grep -i "version" | head -1)

    if [ -n "$version" ]; then
        # 提取版本号 - 支持多种格式
        version=$(echo "$version" | sed -n 's/.*version "\([^"]*\)".*/\1/ip' | head -1)

        if [ -n "$version" ]; then
            output_result "JAVA_VERSION" "$version"
        fi
    fi
}

# 检测Java进程
check_java_process() {
    local java_pid=""

    java_pid=$(java_exec jps 2>/dev/null | grep -v "Jps" | head -1 | awk '{print $1}')

    if [ -n "$java_pid" ]; then
        output_result "JAVA_PID" "$java_pid"

        # 获取JVM内存信息
        local proc_info
        proc_info=$(java_exec cat /proc/$java_pid/status 2>/dev/null)

        if [ -n "$proc_info" ]; then
            local vm_size vm_rss threads
            vm_size=$(echo "$proc_info" | grep "^VmSize:" | awk '{print int($2/1024/1024)}')
            vm_rss=$(echo "$proc_info" | grep "^VmRSS:" | awk '{print int($2/1024/1024)}')
            threads=$(echo "$proc_info" | grep "^Threads:" | awk '{print $2}')

            [ -n "$vm_size" ] && output_result "JAVA_VM_SIZE" "${vm_size}MB"
            [ -n "$vm_rss" ] && output_result "JAVA_VM_RSS" "${vm_rss}MB"
            [ -n "$threads" ] && output_result "JAVA_THREADS" "$threads"
        fi
    fi
}

# 检测JVM堆内存
check_jvm_heap() {
    local heap_info=""

    # 先获取Java进程ID
    local java_pid
    java_pid=$(java_exec jps 2>/dev/null | grep -v "Jps" | head -1 | awk '{print $1}')

    if [ -z "$java_pid" ]; then
        return
    fi

    heap_info=$(java_exec jstat -gc "$java_pid" 2>/dev/null | tail -1)

    if [ -n "$heap_info" ]; then
        # 解析jstat输出
        local ec eu oc ou
        read ec eu oc ou < <(echo "$heap_info" | awk '{print $3, $4, $5, $6}')

        if [ -n "$ec" ] && [ -n "$eu" ]; then
            local heap_capacity=$((ec / 1024))
            local heap_used=$((eu / 1024))

            if [ "$heap_capacity" -gt 0 ]; then
                local heap_percent=$((heap_used * 100 / heap_capacity))

                output_result "JAVA_HEAP_CAPACITY" "${heap_capacity}MB"
                output_result "JAVA_HEAP_USED" "${heap_used}MB"
                output_result "JAVA_HEAP_PERCENT" "${heap_percent}%"

                # 判断堆内存使用率
                local level="正常"
                if [ "$heap_percent" -ge 90 ]; then
                    level="严重"
                elif [ "$heap_percent" -ge 80 ]; then
                    level="警告"
                fi

                output_result "JAVA_HEAP_LEVEL" "$level"
            fi
        fi
    fi
}

# 检测GC状态
check_gc_status() {
    local gc_logs=""

    gc_logs=$(docker logs --tail 500 "$JAVA_CONTAINER" 2>&1 | grep -iE "GC|heap" | tail -20)

    if [ -n "$gc_logs" ]; then
        # 统计Full GC次数
        local full_gc_count
        full_gc_count=$(echo "$gc_logs" | grep -c "Full GC" || echo "0")

        # 统计Young GC次数
        local young_gc_count
        young_gc_count=$(echo "$gc_logs" | grep -cE "GC \(|GC \(Allocation Failure" || echo "0")

        output_result "JAVA_FULL_GC" "$full_gc_count"
        output_result "JAVA_YOUNG_GC" "$young_gc_count"

        # 判断GC状态
        local level="正常"
        if [ "$full_gc_count" -gt 5 ]; then
            level="严重"
        elif [ "$full_gc_count" -gt 2 ]; then
            level="警告"
        fi

        output_result "JAVA_GC_LEVEL" "$level"
    fi
}

# 检测Java线程数
check_java_threads() {
    local thread_count=0

    # 获取第一个Java进程的线程数
    local java_pid
    java_pid=$(java_exec jps 2>/dev/null | grep -v "Jps" | awk '{print $1}' | head -1)

    if [ -n "$java_pid" ]; then
        thread_count=$(docker exec "$JAVA_CONTAINER" cat /proc/$java_pid/status 2>/dev/null | grep "^Threads:" | awk '{print $2}')

        if [ -n "$thread_count" ]; then
            output_result "JAVA_THREAD_COUNT" "$thread_count"

            # 判断线程数
            local level="正常"
            if [ "$thread_count" -ge 3000 ]; then
                level="严重"
            elif [ "$thread_count" -ge 1000 ]; then
                level="警告"
            fi

            output_result "JAVA_THREAD_LEVEL" "$level"
        fi
    fi
}

# 检测Java应用日志
check_java_logs() {
    local log_errors=0

    log_errors=$(docker logs --tail 1000 "$JAVA_CONTAINER" 2>&1 | grep -ciE "ERROR|Exception" || echo "0")
    log_errors=$(echo "$log_errors" | tail -1)

    output_result "JAVA_LOG_ERRORS" "$log_errors"

    if [ "$log_errors" -gt 50 ]; then
        output_result "JAVA_LOG_LEVEL" "警告"
    else
        output_result "JAVA_LOG_LEVEL" "正常"
    fi
}

# 检测Spring Boot Actuator端点
check_springboot_actuator() {
    # 检测可用命令（curl或wget）
    local http_cmd=""
    if docker exec "$JAVA_CONTAINER" which curl >/dev/null 2>&1; then
        http_cmd="curl -s -m 2 --max-time 2"
    elif docker exec "$JAVA_CONTAINER" which wget >/dev/null 2>&1; then
        http_cmd="wget -q -O - -T 2 --timeout=2"
    else
        output_result "SPRINGBOOT_ACTUATOR" "检测工具不可用"
        return
    fi

    # 常见的Actuator端点路径和端口
    local actuator_ports=("8080" "8999")
    local actuator_paths=("/actuator/health" "/health" "/actuator" "/")
    local actuator_found="false"
    local health_status="未检测"

    for port in "${actuator_ports[@]}"; do
        for path in "${actuator_paths[@]}"; do
            # 尝试访问端点（超时2秒）
            local response
            response=$(docker exec "$JAVA_CONTAINER" $http_cmd "http://localhost:${port}${path}" 2>/dev/null | head -c 500)

            if [ -n "$response" ]; then
                if echo "$response" | grep -qiE '"status"|"UP"|"DOWN"|"HEALTH"'; then
                    actuator_found="true"

                    # 解析健康状态
                    if echo "$response" | grep -qi '"status"[[:space:]]*:[[:space:]]*"UP"'; then
                        health_status="UP"
                    elif echo "$response" | grep -qi '"status"[[:space:]]*:[[:space:]]*"DOWN"'; then
                        health_status="DOWN"
                    else
                        health_status="UNKNOWN"
                    fi

                    break 2
                elif echo "$response" | grep -qiE "403|401|禁止|拒绝|unauthorized"; then
                    actuator_found="true"
                    health_status="需要认证"
                    break 2
                fi
            fi
        done
        if [ "$actuator_found" = "true" ]; then
            break
        fi
    done

    if [ "$actuator_found" = "true" ]; then
        output_result "SPRINGBOOT_ACTUATOR" "已启用"
        output_result "SPRINGBOOT_HEALTH_STATUS" "$health_status"

        # 判断健康状态等级
        local level="正常"
        if [ "$health_status" = "DOWN" ]; then
            level="严重"
        elif [ "$health_status" = "UNKNOWN" ]; then
            level="警告"
        fi

        output_result "SPRINGBOOT_HEALTH_LEVEL" "$level"
    else
        output_result "SPRINGBOOT_ACTUATOR" "未启用或不可访问"
    fi
}

# 检测Java容器资源使用
check_java_resources() {
    # CPU使用率
    local cpu_percent
    cpu_percent=$(docker stats --no-stream --format '{{.CPUPerc}}' "$JAVA_CONTAINER" 2>/dev/null | tr -d '%')
    if [ -n "$cpu_percent" ]; then
        output_result "JAVA_CPU_USAGE" "$cpu_percent"
    fi

    # 内存使用
    local mem_usage mem_percent
    mem_usage=$(docker stats --no-stream --format '{{.MemUsage}}' "$JAVA_CONTAINER" 2>/dev/null)
    if [ -n "$mem_usage" ]; then
        output_result "JAVA_MEMORY_USAGE" "$mem_usage"
    fi

    mem_percent=$(docker stats --no-stream --format '{{.MemPerc}}' "$JAVA_CONTAINER" 2>/dev/null | tr -d '%')
    if [ -n "$mem_percent" ]; then
        output_result "JAVA_MEMORY_PERCENT" "$mem_percent"
    fi
}

# 检测容器内进程列表
check_java_process_list() {
    local process_list
    process_list=$(java_exec ps aux 2>/dev/null | grep -v "PID" | grep -v "ps aux" | awk '{print $1, $2, $3, $4, $11}' | head -20)

    if [ -n "$process_list" ]; then
        # 统计进程数
        local total_processes
        total_processes=$(echo "$process_list" | wc -l)
        output_result "JAVA_PROCESS_COUNT" "$total_processes"

        # 格式化进程列表（USER PID CPU% MEM% COMMAND）
        local formatted
        formatted=$(echo "$process_list" | tr '\n' '|' | sed 's/|$//')
        output_result "JAVA_PROCESS_LIST" "$formatted"

        # 获取Java进程详情
        local java_processes
        java_processes=$(echo "$process_list" | grep -i "java" | head -10)
        if [ -n "$java_processes" ]; then
            output_result "JAVA_PROCESSES_DETAIL" "$(echo "$java_processes" | tr '\n' '|' | sed 's/|$//')"
        fi
    fi
}

# ==================== 主检测流程 ====================
main() {
    log_info "开始Java应用检测..."

    # 在检测前先找到实际的容器名
    if command -v docker &> /dev/null; then
        # 尝试精确匹配
        local actual_container=$(docker ps --format "{{.Names}}" | grep -x "${JAVA_CONTAINER}" | head -1)

        # 如果精确匹配失败，尝试模糊匹配
        if [ -z "$actual_container" ]; then
            actual_container=$(docker ps --format "{{.Names}}" | grep -iE "java|api" | head -1)
        fi

        # 更新JAVA_CONTAINER为实际找到的容器名
        if [ -n "$actual_container" ]; then
            JAVA_CONTAINER="$actual_container"
        fi
    fi

    # 输出实际使用的容器名
    output_result "JAVA_CONTAINER" "$JAVA_CONTAINER"

    # 检查容器状态
    if ! check_java_container; then
        log_warn "Java容器未运行，跳过其他检测"
        return
    fi

    # 执行各项检测
    check_java_version
    check_java_process
    check_jvm_heap
    check_gc_status
    check_java_threads
    check_java_logs
    check_springboot_actuator
    check_java_resources
    check_java_process_list

    log_info "Java应用检测完成"
}

# 执行主函数
main