#!/bin/bash
# --------------------------
# 颜色设置
# --------------------------
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

# --------------------------
# 目录和文件设置
# --------------------------
BASE_DIR="/www/wwwroot/messages"
BACKEND_DIR="${BASE_DIR}/background"
FRONTEND_DIR="${BASE_DIR}/web"
TEMP_DIR="${BASE_DIR}/temp"
LOG_DIR="${BASE_DIR}/logs"
JAR_NAME="cross-border-commerce-1.0-SNAPSHOT.jar"
BACKUP_DIR="${BASE_DIR}/backups"
SQL_DIR="${BASE_DIR}/sql"  # SQL 文件存放目录
SQL_CHANGED=0 
# --------------------------
# 下载URL设置
# --------------------------
BACKEND_URL="http://wildrabbitfishplatform.online:8082/background/cross-border-commerce-1.0-SNAPSHOT.jar"
FRONTEND_URL="http://wildrabbitfishplatform.online:8082/web.zip"
SQL_URL="http://wildrabbitfishplatform.online:8082/sql"

# --------------------------
# 进程和端口设置
# --------------------------
MONITOR_PORT=4577
WAIT_TIME=20
MAX_RETRIES=5
RETRY_DELAY=10

# --------------------------
# 日志设置
# --------------------------
LOG_FILE="${LOG_DIR}/update_$(date +%Y%m%d_%H%M%S).log"
MAX_LOG_FILES=10

# --------------------------
# IPv4 获取函数（公网优先，失败回退本机IPv4）
# --------------------------
get_public_ipv4() {
  for cmd in \
    "curl -4 -fsS https://ipv4.icanhazip.com" \
    "curl -4 -fsS https://api.ipify.org" \
    "wget -4 -qO- https://ipv4.icanhazip.com" \
    "dig +short -4 myip.opendns.com @resolver1.opendns.com"
  do
    ip=$(eval "$cmd" 2>/dev/null | tr -d '\n\r ')
    [[ $ip =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] && { echo "$ip"; return 0; }
  done
  return 1
}

get_local_ipv4() {
  ip -4 -o addr show scope global up 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -n1
}

# --------------------------
# 初始化函数
# --------------------------
init_directories() {
    mkdir -p "${TEMP_DIR}"
    mkdir -p "${LOG_DIR}"
    mkdir -p "${BACKUP_DIR}"
    mkdir -p "${SQL_DIR}"  # 创建 SQL 文件存放目录
    touch "${LOG_FILE}"
    
    # 清理旧日志
    find "${LOG_DIR}" -name "update_*.log" -type f | sort -r | tail -n +$((MAX_LOG_FILES + 1)) | xargs rm -f
}

# --------------------------
# 日志函数
# --------------------------
log() {
    local level=$1
    local message=$2
    local timestamp
    timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    
    case $level in
        "INFO") color=${GREEN} ;;
        "WARN") color=${YELLOW} ;;
        "ERROR") color=${RED} ;;
        *) color=${NC} ;;
    esac
    
    echo -e "${color}[${timestamp}] [${level}] ${message}${NC}" | tee -a "${LOG_FILE}"
}

# --------------------------
# 网络检查函数
# --------------------------
check_network() {
    if ! ping -c 1 wildrabbitfishplatform.online &> /dev/null; then
        log "ERROR" "无法连接到更新服务器，请检查网络"
        return 1
    fi
    return 0
}

# --------------------------
# 下载函数
# --------------------------
download_file() {
    local url=$1
    local dest=$2
    local description=$3
    local retries=0
    
    log "INFO" "开始下载 ${description}..."
    
    while [ $retries -lt $MAX_RETRIES ]; do
        if wget --tries=3 --timeout=30 -q "${url}" -O "${dest}"; then
            log "INFO" "${description} 下载成功"
            return 0
        else
            retries=$((retries + 1))
            log "WARN" "${description} 下载失败 (尝试 ${retries}/${MAX_RETRIES})"
            sleep $RETRY_DELAY
        fi
    done
    
    log "ERROR" "${description} 下载失败，达到最大重试次数"
    return 1
}
# --------------------------
# 下载SQL脚本函数（清空后再下载；无则跳过）
# --------------------------
download_sql_scripts() {
    log "INFO" "开始下载SQL脚本到: ${SQL_DIR}"
    mkdir -p "${SQL_DIR}" "${TEMP_DIR}"

    # 如果目录非空，先整体备份再清空（备份放 TEMP_DIR，避免 tar 自读自写告警）
    if [ "$(ls -A "${SQL_DIR}" 2>/dev/null)" ]; then
        local ts
        ts="$(date +%Y%m%d_%H%M%S)"
        local bk="${TEMP_DIR}/sql_backup_${ts}.tar.gz"
        log "INFO" "SQL 目录非空，先备份再清空..."
        tar -czf "${bk}" -C "${SQL_DIR}" . 2>/dev/null || true
        [ -f "${bk}" ] && log "INFO" "已备份到: ${bk}"
        rm -rf "${SQL_DIR:?}/"*
    fi

    # 方式一：镜像整个目录（需要服务端允许目录索引）
    if wget -q -r -np -nH -nd -A "*.sql" -P "${SQL_DIR}" "${SQL_URL}/"; then
        if ls "${SQL_DIR}"/*.sql >/dev/null 2>&1; then
            local n
            n=$(ls -1 ${SQL_DIR}/*.sql | wc -l)
            log "INFO" "已镜像下载 SQL 脚本: ${n} 个"
            SQL_CHANGED=1
            return 0
        fi
    else
        log "WARN" "目录镜像失败或未开启索引，尝试兜底下载..."
    fi

    # 方式二：兜底按常见文件名尝试
    local any=0
    for f in update.sql latest.sql schema.sql init.sql; do
        if wget -q -O "${SQL_DIR}/${f}" "${SQL_URL}/${f}"; then
            log "INFO" "已下载: ${f}"
            any=1
        else
            rm -f "${SQL_DIR}/${f}" 2>/dev/null || true
        fi
    done

    if [ "${any}" -eq 1 ]; then
        SQL_CHANGED=1
        return 0
    else
        SQL_CHANGED=0
        log "WARN" "未能从 ${SQL_URL} 获取到任何 .sql，本次将跳过 Flyway 迁移"
        return 0   # 注意：返回成功，交由主流程决定是否迁移
    fi
}



# --------------------------
# 备份函数
# --------------------------
backup_files() {
    local timestamp
    timestamp=$(date +%Y%m%d_%H%M%S)
    
    # 备份后端
    if [ -f "${BACKEND_DIR}/${JAR_NAME}" ]; then
        log "INFO" "备份后端文件..."
        cp "${BACKEND_DIR}/${JAR_NAME}" "${BACKUP_DIR}/${JAR_NAME}_${timestamp}"
    fi
    
    # 备份前端
    if [ -d "${FRONTEND_DIR}" ]; then
        log "INFO" "备份前端目录..."
        tar -czf "${BACKUP_DIR}/web_${timestamp}.tar.gz" -C "${FRONTEND_DIR}/.." "web"
    fi
}

# --------------------------
# 进程管理函数
# --------------------------
get_java_pid() {
    lsof -t -i :${MONITOR_PORT} -s TCP:LISTEN 2>/dev/null
}

stop_java_process() {
    local pid
    pid=$(get_java_pid)
    
    if [ -n "${pid}" ]; then
        log "INFO" "停止运行中的Java进程 (PID: ${pid})..."
        kill -9 "${pid}"
        sleep 2
        
        if get_java_pid; then
            log "ERROR" "无法停止Java进程"
            return 1
        else
            log "INFO" "Java进程已停止"
            return 0
        fi
    else
        log "INFO" "没有找到运行中的Java进程"
        return 0
    fi
}

check_java_process() {
    local pid
    pid=$(get_java_pid)
    
    if [ -n "${pid}" ]; then
        log "INFO" "Java进程运行正常 (PID: ${pid})"
        return 0
    else
        log "WARN" "没有找到运行中的Java进程"
        return 1
    fi
}

# --------------------------
# 文件验证函数
# --------------------------
validate_file() {
    local file=$1
    local type=$2
    
    if [ ! -e "${file}" ]; then
        log "ERROR" "${type}文件不存在: ${file}"
        return 1
    fi
    
    case $type in
        "[jar]")
            if ! jar -tf "${file}" &> /dev/null; then
                log "ERROR" "JAR文件损坏: ${file}"
                return 1
            fi
            ;;
        "zip")
            if ! unzip -tq "${file}" &> /dev/null; then
                log "ERROR" "ZIP文件损坏: ${file}"
                return 1
            fi
            ;;
        "sql")
            if ! cat "${file}" &> /dev/null; then
                log "ERROR" "SQL脚本不可读取: ${file}"
                return 1
            fi
            ;;
    esac
    
    return 0
}

# --------------------------
# 执行 Flyway migrate（仅当有 SQL 脚本时）
# --------------------------
run_flyway_migrate() {
    if [ "${SQL_CHANGED}" != "1" ] || ! ls "${SQL_DIR}"/*.sql >/dev/null 2>&1; then
        log "INFO" "未发现 SQL 脚本，跳过 Flyway 迁移"
        return 0
    fi
    log "INFO" "开始执行 Flyway 数据库迁移..."
    if ! command -v flyway >/dev/null 2>&1; then
        log "ERROR" "flyway 未安装或未在 PATH 中"
        return 1
    fi
    if ! flyway migrate; then
        log "ERROR" "Flyway migrate 执行失败"
        return 1
    fi
    log "INFO" "Flyway 数据库迁移成功"
    return 0
}


# --------------------------
# 更新函数
# --------------------------
update_backend() {
    log "INFO" "开始更新后端..."
    
    download_file "${BACKEND_URL}" "${TEMP_DIR}/${JAR_NAME}" "后端JAR包" || return 1
    validate_file "${TEMP_DIR}/${JAR_NAME}" "jar" || return 1
    backup_files
    log "INFO" "替换后端文件..."
    mv -f "${TEMP_DIR}/${JAR_NAME}" "${BACKEND_DIR}/${JAR_NAME}"
    stop_java_process || return 1
    return 0
}

update_frontend() {
    log "INFO" "开始更新前端..."
    download_file "${FRONTEND_URL}" "${TEMP_DIR}/web.zip" "前端ZIP包" || return 1
    validate_file "${TEMP_DIR}/web.zip" "zip" || return 1
    log "INFO" "解压前端文件..."
    unzip -qo "${TEMP_DIR}/web.zip" -d "${TEMP_DIR}" || {
        log "ERROR" "前端文件解压失败"; return 1; }
    log "INFO" "替换前端目录..."
    rm -rf "${FRONTEND_DIR}"
    mv -f "${TEMP_DIR}/web" "${FRONTEND_DIR}"
    return 0
}

# --------------------------
# 清理函数
# --------------------------
cleanup() {
    log "INFO" "清理临时文件..."
    rm -rf "${TEMP_DIR}"/*
    find "${BACKUP_DIR}" -name "*.tar.gz" | sort -r | tail -n +6 | xargs rm -f
    find "${BACKUP_DIR}" -name "*.jar_*" | sort -r | tail -n +6 | xargs rm -f
}

# --------------------------
# 通知函数（改为IPv4稳健获取）
# --------------------------
send_notification() {
    local status=$1
    local public_ip
    public_ip="$(get_public_ipv4 || true)"
    if [[ -z "$public_ip" ]]; then
      public_ip="$(get_local_ipv4 || echo "unknown")"
    fi

    local notify_url="http://wildrabbitfishplatform.online:3451/api/authorization/update-version-content-by-ip?ip=${public_ip}"
    log "INFO" "发送更新通知 (IP: ${public_ip}, 状态: ${status})..."
    curl -X POST "${notify_url}" \
        -H "Content-Type: application/json" \
        -d "{\"ip\":\"${public_ip}\",\"status\":\"${status}\"}" \
        || log "WARN" "通知发送失败"
}

# --------------------------
# 主函数
# --------------------------
main() {
    log "INFO" "========== 开始系统更新 =========="
    init_directories
    
    if ! check_network; then
        send_notification "failed"; exit 1
    fi
    
    # 1) 先镜像/下载 SQL，再立刻迁移数据库
    if ! download_sql_scripts; then
        log "ERROR" "SQL脚本下载失败"; send_notification "failed"; exit 1
    fi

    if ! run_flyway_migrate; then
        log "ERROR" "Flyway 数据库迁移失败"; send_notification "failed"; exit 1
    fi
    
    # 2) 再更新前端与后端
    if ! update_frontend; then
        log "ERROR" "前端更新失败"; send_notification "failed"; exit 1
    fi
    
    if ! update_backend; then
        log "ERROR" "后端更新失败"; send_notification "failed"; exit 1
    fi
    
    log "INFO" "等待Java进程启动 (${WAIT_TIME}秒)..."
    sleep ${WAIT_TIME}
    
    if ! check_java_process; then
        log "ERROR" "Java进程未启动，尝试回滚..."
        local latest_backup
        latest_backup=$(ls -t ${BACKUP_DIR}/*.jar_* 2>/dev/null | head -1)
        if [ -n "${latest_backup}" ]; then
            log "INFO" "恢复后端备份: ${latest_backup}"
            cp -f "${latest_backup}" "${BACKEND_DIR}/${JAR_NAME}"
            log "INFO" "再次等待Java进程启动..."
            sleep ${WAIT_TIME}
            if check_java_process; then
                log "INFO" "回滚成功，系统已恢复"
                send_notification "rollback"
                cleanup
                exit 1
            else
                log "ERROR" "回滚失败，请手动检查"
                send_notification "failed"
                exit 1
            fi
        else
            log "ERROR" "找不到可用的备份文件"
            send_notification "failed"
            exit 1
        fi
    fi
    
    log "INFO" "系统更新完成"
    send_notification "success"
    cleanup
    log "INFO" "========== 更新完成 =========="
    exit 0
}

# 执行主函数
main
