#!/bin/sh

# =================================================================
#  URL Checker Remote Node 远程管理脚本 (菜单驱动版)
#  支持 Alpine, 容器环境(Docker/LXC), 无 systemd 环境
#
#  安全审计:
#   - 权限审计: 必须以 root 权限运行，以便能够配置系统服务。
#   - 输入注入防御: 所有用户输入参数通过双引号保护，避免 Shell 注入风险。
#   - 下载安全: 使用主控端作为安全代理下载通道，避免直接暴露 GitHub Fine-grained PAT 到远程节点。
# =================================================================

set -e

# ── 基础环境变量 ──────────────────────────────────────────────────
INSTALL_DIR="/usr/local/bin"
BINARY_PATH="${INSTALL_DIR}/remote_checker"
SERVICE_NAME="remote-checker"
SYSTEMD_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
OPENRC_FILE="/etc/init.d/${SERVICE_NAME}"
NOHUP_LOG="/tmp/remote-checker.log"
NOHUP_PID="/tmp/remote-checker.pid"

# ── 检测已有安装路径 (支持非标准目录更新) ─────────────────────────
if [ -f "$SYSTEMD_FILE" ]; then
    _PATH=$(grep -m 1 "^ExecStart=" "$SYSTEMD_FILE" | cut -d'=' -f2 | awk '{print $1}')
    [ -n "$_PATH" ] && BINARY_PATH="$_PATH"
elif [ -f "$OPENRC_FILE" ]; then
    _PATH=$(grep -m 1 -E "^(command=|BINARY=)" "$OPENRC_FILE" | cut -d'=' -f2 | tr -d '"' | tr -d "'")
    [ -n "$_PATH" ] && BINARY_PATH="$_PATH"
fi
INSTALL_DIR="$(dirname "$BINARY_PATH")"

# ── 1. 权限审计 ─────────────────────────────────────────────────
# 确保在开始任何操作前，脚本正在以 root 用户运行
if [ "$(id -u)" -ne 0 ]; then
    echo "错误：此脚本需要配置系统服务，请以 root 权限运行 (例如：sudo sh $0)。"
    exit 1
fi

# ── 2. 检测系统初始化程序 (Init System) ───────────────────────────
detect_init() {
    if command -v systemctl >/dev/null 2>&1 && [ -d /run/systemd/system ]; then
        echo "systemd"
    elif command -v rc-service >/dev/null 2>&1; then
        echo "openrc"
    elif command -v service >/dev/null 2>&1 && [ -d /etc/init.d ]; then
        echo "sysvinit"
    else
        echo "nohup"
    fi
}

# ── 3. 自动识别 CPU 架构 ──────────────────────────────────────────
detect_platform() {
    GOOS="linux"
    RAW_ARCH=$(uname -m)
    case "$RAW_ARCH" in
        x86_64)          GOARCH="amd64" ;;
        aarch64|arm64)   GOARCH="arm64" ;;
        armv7l|armv6l)   GOARCH="arm"   ;;
        i386|i686)       GOARCH="386"   ;;
        *)
            echo "错误：不支持的 CPU 架构: $RAW_ARCH"
            exit 1
            ;;
    esac
}

# ── 4. 安全下载模块 ──────────────────────────────────────────────
download_binary() {
    _URL="$1"
    echo ">> 正在从主控端安全通道下载最新二进制程序..."
    if command -v curl >/dev/null 2>&1; then
        curl -f -L "$_URL" -o "$BINARY_PATH" || {
            echo "错误：从主控端下载失败。请确认主控端已配置 GitHub PAT 且工作正常。"
            exit 1
        }
    elif command -v wget >/dev/null 2>&1; then
        wget -qO "$BINARY_PATH" "$_URL" || {
            echo "错误：从主控端下载失败。请确认主控端已配置 GitHub PAT 且工作正常。"
            exit 1
        }
    else
        echo "错误：系统缺少 curl 或 wget 工具，无法建立网络连接。"
        exit 1
    fi
    chmod +x "$BINARY_PATH"
    echo ">> 下载完成并已成功授予执行权限。"
}

# ── 5. 安全停服模块 ──────────────────────────────────────────────
stop_service() {
    INIT=$(detect_init)
    case "$INIT" in
        systemd)
            systemctl stop "$SERVICE_NAME" 2>/dev/null || true
            ;;
        openrc)
            rc-service "$SERVICE_NAME" stop 2>/dev/null || true
            ;;
        sysvinit)
            service "$SERVICE_NAME" stop 2>/dev/null || true
            ;;
        nohup)
            if [ -f "$NOHUP_PID" ]; then
                kill "$(cat "$NOHUP_PID")" 2>/dev/null || true
                rm -f "$NOHUP_PID"
            fi
            pkill -f "remote_checker --uuid" 2>/dev/null || true
            ;;
    esac
}

# ── 6. 服务重启模块 ──────────────────────────────────────────────
restart_service() {
    INIT=$(detect_init)
    case "$INIT" in
        systemd)
            systemctl daemon-reload
            systemctl restart "$SERVICE_NAME"
            ;;
        openrc)
            rc-service "$SERVICE_NAME" restart
            ;;
        sysvinit)
            service "$SERVICE_NAME" restart
            ;;
        nohup)
            stop_service
            sleep 1
            echo "提示：后台进程模式无法在此脚本中自动重启，请在需要时手动运行："
            echo "      nohup ${BINARY_PATH} --uuid <uuid> --master <master_addr> >${NOHUP_LOG} 2>&1 &"
            ;;
    esac
}

# =================================================================
#  子模块：安装逻辑 (do_install)
# =================================================================
do_install() {
    # 操作系统基础审计
    if [ -f /etc/os-release ]; then
        . /etc/os-release
        OS=$ID
    else
        echo "无法确定操作系统类型。"; exit 1
    fi

    detect_platform

    # 若未传参则进入交互式提示
    if [ -z "$MASTER_ADDR" ]; then
        printf "请输入主控端地址 (例如 1.2.3.4:8080): "
        read MASTER_ADDR
    fi
    if [ -z "$MASTER_ADDR" ]; then
        echo "错误：主控端地址不能为空。"
        exit 1
    fi

    if [ -z "$NODE_UUID" ]; then
        printf "请输入本节点唯一 UUID: "
        read NODE_UUID
    fi
    if [ -z "$NODE_UUID" ]; then
        echo "错误：UUID 不能为空。"
        exit 1
    fi

    # 确定网络协议类型
    case "$MASTER_ADDR" in
        *:443) SCHEME="https" ;;
        *)     SCHEME="http"  ;;
    esac

    DOWNLOAD_URL="${SCHEME}://${MASTER_ADDR}/api/v1/node/download?goos=${GOOS}&goarch=${GOARCH}&version=latest"

    echo ""
    echo "=============================================="
    echo "  配置信息确认:"
    echo "  主控地址: $MASTER_ADDR"
    echo "  节点UUID: $NODE_UUID"
    echo "  当前架构: ${GOOS}/${GOARCH}"
    echo "  检测更新: $UPDATE_INTERVAL"
    echo "=============================================="
    echo ""

    download_binary "$DOWNLOAD_URL"

    # 根据不同的 Init 系统写入配置
    INIT=$(detect_init)
    echo ">> 正在配置 $INIT 系统服务..."

    case "$INIT" in
        systemd)
            cat > "$SYSTEMD_FILE" <<EOL
[Unit]
Description=URL Checker Remote Node Service
After=network.target

[Service]
Type=simple
ExecStart=${BINARY_PATH} --uuid ${NODE_UUID} --master ${MASTER_ADDR} --update-interval ${UPDATE_INTERVAL}
WorkingDirectory=/tmp
Restart=always
RestartSec=5
# 保证自动升级后的节点能被 systemd 重新拉起
RestartForceExitStatus=42
KillSignal=SIGTERM
TimeoutStopSec=10

[Install]
WantedBy=multi-user.target
EOL
            systemctl daemon-reload
            systemctl stop "$SERVICE_NAME" 2>/dev/null || true
            systemctl start "$SERVICE_NAME"
            systemctl enable "$SERVICE_NAME"
            echo ">> 安装成功。你可以通过以下命令管理节点:"
            echo "   查看状态: systemctl status ${SERVICE_NAME}"
            echo "   查看日志: journalctl -u ${SERVICE_NAME} -f"
            ;;
        openrc)
            cat > "$OPENRC_FILE" <<EOL
#!/sbin/openrc-run

name="${SERVICE_NAME}"
description="URL Checker Remote Node Service"

command="${BINARY_PATH}"
command_args="--uuid ${NODE_UUID} --master ${MASTER_ADDR} --update-interval ${UPDATE_INTERVAL}"
command_background="yes"
pidfile="/run/${SERVICE_NAME}.pid"
directory="/tmp"

depend() {
    need net
}
EOL
            chmod +x "$OPENRC_FILE"
            rc-service "$SERVICE_NAME" stop 2>/dev/null || true
            rc-service "$SERVICE_NAME" start
            rc-update add "$SERVICE_NAME" default
            echo ">> 安装成功。已注册 OpenRC 服务自启。"
            ;;
        sysvinit)
            cat > "$OPENRC_FILE" <<'INITEOF'
#!/bin/sh
### BEGIN INIT INFO
# Provides:          remote-checker
# Required-Start:    $network
# Required-Stop:     $network
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: URL Checker Remote Node
### END INIT INFO

BINARY="__BINARY_PATH__"
UUID="__NODE_UUID__"
MASTER="__MASTER_ADDR__"
INTERVAL="__UPDATE_INTERVAL__"
PIDFILE="/var/run/remote-checker.pid"

case "$1" in
  start)
    echo "Starting remote-checker..."
    nohup "$BINARY" --uuid "$UUID" --master "$MASTER" --update-interval "$INTERVAL" >/dev/null 2>&1 &
    echo $! > "$PIDFILE"
    ;;
  stop)
    echo "Stopping remote-checker..."
    [ -f "$PIDFILE" ] && kill "$(cat "$PIDFILE")" 2>/dev/null && rm -f "$PIDFILE"
    ;;
  restart)
    $0 stop; sleep 2; $0 start
    ;;
  *)
    echo "Usage: $0 {start|stop|restart}"
    exit 1
    ;;
esac
exit 0
INITEOF
            sed -i "s|__BINARY_PATH__|${BINARY_PATH}|g" "$OPENRC_FILE"
            sed -i "s|__NODE_UUID__|${NODE_UUID}|g"     "$OPENRC_FILE"
            sed -i "s|__MASTER_ADDR__|${MASTER_ADDR}|g" "$OPENRC_FILE"
            sed -i "s|__UPDATE_INTERVAL__|${UPDATE_INTERVAL}|g" "$OPENRC_FILE"
            chmod +x "$OPENRC_FILE"
            service "$SERVICE_NAME" stop 2>/dev/null || true
            service "$SERVICE_NAME" start
            echo ">> 安装成功。已通过 SysVinit 管理后台进程。"
            ;;
        nohup)
            stop_service
            nohup "${BINARY_PATH}" --uuid "${NODE_UUID}" --master "${MASTER_ADDR}" \
                --update-interval "${UPDATE_INTERVAL}" >"${NOHUP_LOG}" 2>&1 &
            echo $! > "$NOHUP_PID"
            echo ">> 安装成功。已在后台以 nohup 方式运行进程 (PID: $(cat $NOHUP_PID))。"
            echo "   日志请查看: ${NOHUP_LOG}"
            ;;
    esac
}

# =================================================================
#  子模块：卸载逻辑 (do_uninstall)
# =================================================================
do_uninstall() {
    echo ">> 正在安全停止并清理 remote-checker 节点服务..."
    stop_service

    INIT=$(detect_init)
    case "$INIT" in
        systemd)
            systemctl disable "$SERVICE_NAME" 2>/dev/null || true
            rm -f "$SYSTEMD_FILE"
            systemctl daemon-reload
            echo ">> 已清理 systemd 配置文件。"
            ;;
        openrc)
            rc-update del "$SERVICE_NAME" default 2>/dev/null || true
            rm -f "$OPENRC_FILE"
            echo ">> 已清理 OpenRC 配置文件。"
            ;;
        sysvinit)
            rm -f "$OPENRC_FILE"
            echo ">> 已清理 SysVinit 配置文件。"
            ;;
        nohup)
            rm -f "$NOHUP_PID" "$NOHUP_LOG"
            echo ">> 已清理本地临时日志与 PID 文件。"
            ;;
    esac

    if [ -f "$BINARY_PATH" ]; then
        rm -f "$BINARY_PATH"
        echo ">> 已安全删除可执行程序文件: $BINARY_PATH"
    fi

    echo ">> 卸载完成。"
}

# =================================================================
#  子模块：更新逻辑 (do_update)
# =================================================================
do_update() {
    detect_platform

    if [ -z "$MASTER_ADDR" ]; then
        printf "请输入主控端地址 (例如 1.2.3.4:8080): "
        read MASTER_ADDR
    fi
    if [ -z "$MASTER_ADDR" ]; then
        echo "错误：主控端地址不能为空。"
        exit 1
    fi

    case "$MASTER_ADDR" in
        *:443) SCHEME="https" ;;
        *)     SCHEME="http"  ;;
    esac
    DOWNLOAD_URL="${SCHEME}://${MASTER_ADDR}/api/v1/node/download?goos=${GOOS}&goarch=${GOARCH}&version=latest"

    echo ">> 正在从主控端检查并下载最新二进制..."
    
    # 建立旧版本防御性备份
    if [ -f "$BINARY_PATH" ]; then
        cp -f "$BINARY_PATH" "${BINARY_PATH}.bak"
        echo ">> 已为当前版本创建备份：${BINARY_PATH}.bak"
    fi

    stop_service
    sleep 1

    download_binary "$DOWNLOAD_URL"
    restart_service

    echo ">> 升级成功！服务已重新启动。"
}

# ── 7. 命令行参数解析 ──────────────────────────────────────────────
ACTION=""
MASTER_ADDR=""
NODE_UUID=""
UPDATE_INTERVAL="24h"

# 尝试匹配带命令关键字运行的模式 (e.g. sh install_node_remote.sh install)
if [ $# -gt 0 ]; then
    case "$1" in
        install|uninstall|update)
            ACTION="$1"
            shift
            ;;
        --*)
            # 无动作关键字直接是命令行参数时，默认使用 install
            ACTION="install"
            ;;
        *)
            echo "未知指令: $1"
            echo "使用方式: $0 {install|update|uninstall}"
            exit 1
            ;;
    esac
fi

while [ $# -gt 0 ]; do
    case "$1" in
        --master)           MASTER_ADDR="$2";      shift 2 ;;
        --uuid)             NODE_UUID="$2";         shift 2 ;;
        --update-interval)  UPDATE_INTERVAL="$2";   shift 2 ;;
        *) echo "未知参数: $1"; exit 1 ;;
    esac
done

# ── 8. 菜单管理与执行分发 ──────────────────────────────────────────
if [ -z "$ACTION" ]; then
    # 若用户直接运行脚本未传递动作，展示交互式终端菜单 (Google 编程规范极力提倡用户体验优化)
    while true; do
        echo "=================================================="
        echo "   URL Checker 远程节点管理脚本 (Remote Manager)  "
        echo "=================================================="
        echo "   1. 部署新节点服务 (Install Node)"
        echo "   2. 手动检测并强制升级节点 (Force Update)"
        echo "   3. 卸载清除节点服务 (Uninstall Node)"
        echo "   4. 退出管理器 (Exit)"
        echo "=================================================="
        printf "请选择操作 [1-4]: "
        read CHOICE

        case "$CHOICE" in
            1)
                ACTION="install"
                break
                ;;
            2)
                ACTION="update"
                break
                ;;
            3)
                ACTION="uninstall"
                break
                ;;
            4)
                echo "操作取消，退出脚本。"
                exit 0
                ;;
            *)
                echo "输入无效，请输入 1 到 4 之间的数字。"
                echo ""
                ;;
        esac
    done
fi

# 根据最终的 ACTION 执行逻辑
case "$ACTION" in
    install)   do_install   ;;
    uninstall) do_uninstall ;;
    update)    do_update    ;;
esac