#!/usr/bin/env bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

C_RESET="\033[0m"
C_GREEN="\033[1;32m"
C_CYAN="\033[1;36m"
C_YELLOW="\033[1;33m"
C_BLUE="\033[1;96m"
C_RED="\033[1;31m"

# 内核镜像基址（含 EL7 kernel-ml、RHEL BBRv3 三件套等）
MIRROR_KERNEL_IGZ=http://8.134.160.125/bbr3
MIRROR_KERNEL_IPAN=https://ipan.mbvpn.cn/bbr3

check_sys() {
    # Alibaba Cloud Linux 3/4（含 OpenAnolis/Agentic 变体）也带 /etc/redhat-release，
    # 必须先按 os-release 的 ID 识别 alinux/alios，否则会被误判为 centos 而走内核更换流程
    if grep -qiE '^ID="?alinux"?$|^ID="?alios"?$' /etc/os-release 2>/dev/null; then
        release="alinux"
    elif [[ -f /etc/redhat-release ]]; then
        release="centos"
    elif grep -q -E -i "debian" /etc/issue /proc/version 2>/dev/null; then
        release="debian"
    elif grep -q -E -i "ubuntu" /etc/issue /proc/version 2>/dev/null; then
        release="ubuntu"
    elif grep -q -E -i "centos|red hat|redhat" /proc/version 2>/dev/null; then
        release="centos"
    fi
}

check_version() {
    if [[ -s /etc/redhat-release ]]; then
        version=$(grep -oE "[0-9.]+" /etc/redhat-release | cut -d . -f 1)
    elif [[ "${release}" == "alinux" ]]; then
        version=$(sed -n 's/^VERSION_ID="\?\([^"]*\)"\?$/\1/p' /etc/os-release | grep -oE '^[0-9]+' | head -1)
    else
        version=$(grep -oE "[0-9.]+" /etc/issue | cut -d . -f 1)
    fi
    bit=$(uname -m)
    [[ ${bit} = "x86_64" ]] && bit="x64" || bit="x32"
}

# 检测并自动安装 curl（脚本下载/地域检测均依赖 curl，缺失会静默走兜底路径）
check_curl() {
    command -v curl >/dev/null 2>&1 && return 0
    echo -e "${C_YELLOW}[*] 未检测到 curl，正在安装...${C_RESET}"
    if [[ "${release}" == "debian" || "${release}" == "ubuntu" ]]; then
        apt-get update -y >/dev/null 2>&1 || true
        apt-get install -y curl >/dev/null 2>&1
    elif [[ "${release}" == "centos" || "${release}" == "alinux" ]]; then
        if command -v dnf >/dev/null 2>&1; then
            dnf install -y curl >/dev/null 2>&1
        elif command -v yum >/dev/null 2>&1; then
            yum install -y curl >/dev/null 2>&1
        fi
    fi
    if ! command -v curl >/dev/null 2>&1; then
        echo -e "${C_RED}[-] curl 安装失败，无法继续（脚本网络功能依赖 curl）。${C_RESET}"
        exit 1
    fi
    echo -e "${C_GREEN}[+] curl 已就绪。${C_RESET}"
}

# 检测并自动安装 tc（iproute2/iproute-tc）：启用脚本需用 tc 即时挂载网卡 qdisc，
# 缺失时 sysctl 配置仍生效，但网卡 qdisc 无法即时替换
check_tc() {
    command -v tc >/dev/null 2>&1 && return 0
    echo -e "${C_YELLOW}[*] 未检测到 tc，正在安装...${C_RESET}"
    if [[ "${release}" == "debian" || "${release}" == "ubuntu" ]]; then
        apt-get update -y >/dev/null 2>&1 || true
        apt-get install -y iproute2 >/dev/null 2>&1
    elif [[ "${release}" == "centos" || "${release}" == "alinux" ]]; then
        if command -v dnf >/dev/null 2>&1; then
            dnf install -y iproute-tc >/dev/null 2>&1
        elif command -v yum >/dev/null 2>&1; then
            yum install -y iproute-tc >/dev/null 2>&1
        fi
    fi
    if ! command -v tc >/dev/null 2>&1; then
        echo -e "${C_RED}[-] tc 安装失败，无法即时应用网卡 qdisc（sysctl 配置仍会生效）。${C_RESET}"
        return 1
    fi
    echo -e "${C_GREEN}[+] tc 已就绪。${C_RESET}"
}

# 生成简洁系统名（如 Debian 12 / Ubuntu 24.04 / CentOS 7 / Alibaba Cloud Linux 3）
get_sys_name() {
    local id="" vid="" pretty=""
    [[ -f /etc/os-release ]] && {
        id=$(sed -n 's/^ID="\?\([^"]*\)"\?$/\1/p' /etc/os-release)
        vid=$(sed -n 's/^VERSION_ID="\?\([^"]*\)"\?$/\1/p' /etc/os-release)
        pretty=$(sed -n 's/^PRETTY_NAME="\?\([^"]*\)"\?$/\1/p' /etc/os-release)
    }
    case "${id:-${release}}" in
        debian)   sys_name="Debian ${vid%%.*}" ;;
        ubuntu)   sys_name="Ubuntu ${vid%%.*}" ;;
        centos)   sys_name="CentOS ${vid%%.*}" ;;
        alinux)   sys_name="Alibaba Cloud Linux ${vid%%.*}" ;;
        *)        sys_name="${pretty:-${release} ${version}}" ;;
    esac
}

check_l2tp_support() {
    # 权威探测放最前：modprobe -n 直接探测当前运行内核（内建 y/模块 m/没有）
    # 避免 /boot/config-$(uname -r) 属于其他内核时的假阳性误判
    if modprobe -n l2tp_ppp 2>/dev/null; then
        echo "支持L2TP"
        return
    fi

    local kv=$(uname -r)
    if ls /lib/modules/${kv}/kernel/net/l2tp/l2tp_ppp.ko* >/dev/null 2>&1; then
        echo "支持L2TP"
        return
    fi

    local cfg=""
    if [[ -f /proc/config.gz ]]; then
        cfg=$(zcat /proc/config.gz 2>/dev/null)
    elif [[ -f /boot/config-${kv} ]]; then
        cfg=$(cat /boot/config-${kv})
    fi
    if echo "${cfg}" | grep -qE "^CONFIG_PPP(=y|=m)" \
        && echo "${cfg}" | grep -qE "^CONFIG_PPPOL2TP(=y|=m)"; then
        echo "支持L2TP"
        return
    fi

    echo "不支持L2TP"
}

# 内核是否已具备加速所需能力（BBR + FQ 类队列），具备则无需升级
# 用 modprobe -n 干跑探测：模块存在（.ko 或内建）即视为可用，不实际加载
# FQ_PIE 优先，缺时退级 fq_codel（3.x 老内核起就有）也算满足；BBR 缺失才判定需要升级
kernel_sufficient() {
    if ! modprobe -n tcp_bbr 2>/dev/null; then
        return 1
    fi
    if ! modprobe -n sch_fq_pie 2>/dev/null && ! modprobe -n sch_fq_codel 2>/dev/null; then
        return 1
    fi
    return 0
}

# 内核镜像中可用的最新内核版本：
#   EL 系 → kernel-ml-x.y.z rpm 版本；Debian/Ubuntu → 镜像 .version 文件（joeyblog 版本）
# 探测失败返回空
mirror_kernel_ml_version() {
    if [[ -z "${KML_MIRROR_VERSION:-}" ]]; then
        [[ -z "${KERNEL_MIRROR:-}" ]] && select_kernel_mirror >&2
        if [[ "${release}" == "centos" ]]; then
            local f=""
            f=$(curl -s --connect-timeout 8 -m 15 "${KERNEL_MIRROR}/" 2>/dev/null | grep -oE 'kernel-ml-[0-9][^"<]*\.rpm' | grep -E "\.el${version}\." | grep -vE 'devel|doc|headers|tools' | sort -uV | tail -1)
            KML_MIRROR_VERSION=$(echo "${f}" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
        else
            KML_MIRROR_VERSION=$(curl -s --connect-timeout 8 -m 15 "${KERNEL_MIRROR}/.version" 2>/dev/null | tr -d ' \r\n')
        fi
    fi
    echo "${KML_MIRROR_VERSION}"
}

# 当前内核是否落后于镜像（模块能力满足但版本过老/未装 joeyblog 也算落后，应升级）
kernel_outdated() {
    # Alibaba Cloud Linux 3/4 不提供内核更换（仅调优），永远不算落后
    [[ "${release}" == "alinux" ]] && return 1
    local cur="" mir=""
    mir=$(mirror_kernel_ml_version)
    [[ -z "${mir}" ]] && return 1
    if [[ "${release}" == "centos" ]]; then
        cur=$(uname -r | grep -oE '^[0-9]+\.[0-9]+(\.[0-9]+)?')
        [[ -z "${cur}" ]] && return 1
    else
        # Debian/Ubuntu：看是否已装 joeyblog 内核及其版本
        cur=$(dpkg -l 2>/dev/null | awk '/^ii/ && $2 ~ /^linux-image-/ && $2 ~ /joeyblog/ {print $2}' | sed 's/^linux-image-//; s/-joeyblog-bbrv3$//' | sort -V | tail -1)
        [[ -z "${cur}" ]] && return 0
    fi
    if [[ "${cur}" != "${mir}" && "$(printf '%s\n%s\n' "${mir}" "${cur}" | sort -V | head -1)" == "${cur}" ]]; then
        return 0
    fi
    return 1
}

# 从内核镜像离线安装 joeyblog BBRv3（仅 Debian/Ubuntu），失败返回 1
install_kernel_deb_mirror() {
    local arch="" base="" tmp=""
    case "$(uname -m)" in
        x86_64|amd64) arch="x86_64" ;;
        aarch64|arm64) arch="arm64" ;;
        *) return 1 ;;
    esac
    [[ -z "${KERNEL_MIRROR:-}" ]] && select_kernel_mirror
    base="${KERNEL_MIRROR}"
    tmp=$(mktemp -d /tmp/joeyblog.XXXXXX)
    local p=""
    for p in headers image libc-dev; do
        echo -e "${C_YELLOW}[*] 下载 ${base}/debian-${arch}-linux-${p}.deb ...${C_RESET}"
        if ! curl -fL --progress-bar --connect-timeout 10 -m 600 -o "${tmp}/linux-${p}.deb" "${base}/debian-${arch}-linux-${p}.deb"; then
            rm -rf "${tmp}"
            return 1
        fi
    done
    if ! dpkg -i "${tmp}"/linux-*.deb >/dev/null 2>&1; then
        echo -e "${C_RED}安装包损坏或 dpkg 失败，已中止，旧内核完好保留。${C_RESET}"
        rm -rf "${tmp}"
        return 1
    fi
    rm -rf "${tmp}"
    return 0
}

# 按地域选择软件源：国内→阿里云镜像（ECS 上还自动走内网免流量），海外→保持官方源
# 支持 Debian/Ubuntu（含 deb822 的 .sources）与 CentOS 7 base 源；改动前自动备份为 *.bak.opencode
select_mirror() {
    local country=""
    country=$(curl -s4 --connect-timeout 3 -m 5 https://ipinfo.io/country 2>/dev/null | tr -d ' \r\n' || true)
    if [[ "${country}" != "CN" && -n "${country}" ]]; then
        MIRROR_REGION="official"
        echo -e "${C_GREEN}[+] 海外地域 (${country})，保持官方软件源${C_RESET}"
        return
    fi
    [[ -z "${country}" ]] && echo -e "${C_YELLOW}[-] 地域检测失败，默认使用国内阿里云镜像${C_RESET}"
    MIRROR_REGION="aliyun"
    echo -e "${C_GREEN}[+] 国内地域，使用阿里云镜像源 mirrors.aliyun.com${C_RESET}"

    if [[ "${release}" == "debian" || "${release}" == "ubuntu" ]]; then
        local f=""
        local changed=0
        local sedrule='s|deb.debian.org|mirrors.aliyun.com|g; s|archive.ubuntu.com|mirrors.aliyun.com|g; s|security.ubuntu.com|mirrors.aliyun.com|g; s|ports.ubuntu.com|mirrors.aliyun.com|g; s|security.debian.org|mirrors.aliyun.com|g'
        for f in /etc/apt/sources.list /etc/apt/sources.list.d/*.sources; do
            [[ -f "${f}" ]] || continue
            if grep -qE "deb.debian.org|archive.ubuntu.com|security.ubuntu.com|ports.ubuntu.com|security.debian.org" "${f}"; then
                cp "${f}" "${f}.bak.opencode" 2>/dev/null
                sed -i -e "${sedrule}" "${f}" && changed=1
            fi
        done
        if [[ ${changed} -eq 1 ]]; then
            echo -e "${C_GREEN}[+] apt 源已切换为阿里云（原文件备份为 *.bak.opencode）${C_RESET}"
        else
            echo -e "${C_YELLOW}[-] 未发现官方 apt 源（可能已自定义，跳过）${C_RESET}"
        fi
    elif [[ "${release}" == "centos" && "${version}" == "7" ]]; then
        local brepo="/etc/yum.repos.d/CentOS-Base.repo"
        if [[ -f "${brepo}" ]]; then
            cp "${brepo}" "${brepo}.bak.opencode" 2>/dev/null
            sed -i -e 's|^mirrorlist=|#mirrorlist=|' \
                   -e 's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.aliyun.com/centos|' \
                   -e 's|^baseurl=http://mirror.centos.org/centos|baseurl=https://mirrors.aliyun.com/centos|' \
                   "${brepo}" && echo -e "${C_GREEN}[+] CentOS 7 base 源已切换为阿里云${C_RESET}"
        fi
    fi
}

# EL7 已于 2024-06-30 EOL，ELRepo 官方 el7 仓库已清空（不再提供 kernel-ml）。
# 优先从内核镜像（大陆→igz 8.134.160.125 / 海外港澳台→ipan.mbvpn.cn）获取最后一个 kernel-ml（6.9.x），
# 镜像不可用时回退官方归档镜像并 localinstall。
install_kernel_el7_archive() {
    local kml=""
    local base=""
    local arc="http://mirrors.coreix.net/elrepo-archive-archive"

    [[ -z "${KERNEL_MIRROR:-}" ]] && select_kernel_mirror
    echo -e "${C_YELLOW}[*] ELRepo 官方已停止提供 el7 内核，从内核镜像 ${KERNEL_MIRROR} 获取...${C_RESET}"
    base="${KERNEL_MIRROR}"
    kml=$(curl -s --connect-timeout 8 -m 20 "${base}/" 2>/dev/null | grep -oE 'kernel-ml-[0-9][^"<]*\.rpm' | grep -E '\.el7\.' | grep -vE 'devel|doc|headers|tools' | sort -uV | tail -1)
    if [[ -z "${kml}" ]]; then
        echo -e "${C_YELLOW}[-] 内核镜像未提供 el7 内核，回退官方归档镜像 ${arc}...${C_RESET}"
        base="${arc}/kernel/el7/x86_64/RPMS"
        kml=$(curl -s --connect-timeout 8 -m 20 "${base}/" 2>/dev/null | grep -oE 'kernel-ml-[0-9][^"<]*\.rpm' | grep -E '\.el7\.' | grep -vE 'devel|doc|headers|tools' | sort -uV | tail -1)
    fi
    if [[ -z "${kml}" ]]; then
        echo -e "${C_RED}内核镜像与归档镜像均不可用，无法获取 el7 内核，已中止，系统未做任何修改。${C_RESET}"
        return 1
    fi

    # 已安装的内核不低于镜像提供版本时跳过下载（EL7 末版 kernel-ml 固定为 6.9.x，
    # 重复运行不应反复下载重装同一内核，也不应安装比当前更旧的内核）
    local installed=""
    installed=$(rpm -qa 'kernel-ml-[0-9]*' 2>/dev/null | sort -V | tail -1)
    if [[ -n "${installed}" ]]; then
        if [[ "$(printf '%s\n%s\n' "${installed}" "${kml}" | sort -V | tail -1)" == "${installed}" ]]; then
            echo -e "${C_GREEN}[+] 已安装内核 ${installed} 不旧于镜像最新 ${kml}，无需重复下载安装${C_RESET}"
            return 0
        fi
    fi

    # GPG key 优先从内核镜像获取（离线可用），失败回退 elrepo 官网
    local keyfile=""
    keyfile=$(mktemp /tmp/elrepo-key.XXXXXX)
    if ! curl -sfL --connect-timeout 8 -m 15 -o "${keyfile}" "${KERNEL_MIRROR}/RPM-GPG-KEY-elrepo.org" 2>/dev/null; then
        curl -sfL --connect-timeout 8 -m 15 -o "${keyfile}" https://www.elrepo.org/RPM-GPG-KEY-elrepo.org 2>/dev/null
    fi
    [[ -s "${keyfile}" ]] && rpm --import "${keyfile}" 2>/dev/null
    rm -f "${keyfile}"

    local tmp=""
    tmp=$(mktemp /tmp/kernel-ml.XXXXXX.rpm)
    echo -e "${C_YELLOW}[*] 下载 ${kml}（约 70MB）...${C_RESET}"
    if ! curl -fL --progress-bar --connect-timeout 10 -m 600 -o "${tmp}" "${base}/${kml}"; then
        echo -e "${C_RED}内核下载失败，已中止，系统未做任何修改。${C_RESET}"
        rm -f "${tmp}"
        return 1
    fi
    if ! yum install -y "${tmp}"; then
        echo -e "${C_RED}内核安装失败，已中止，旧内核完好保留。${C_RESET}"
        rm -f "${tmp}"
        return 1
    fi
    rm -f "${tmp}"
    return 0
}

# EL8/9：优先从内核镜像获取最新 kernel-ml（新版 elrepo 将实际内核拆在 kernel-ml-core 主包中）。
# 镜像不可用返回 1，由调用方回退官方 elrepo-kernel 仓库。
install_kernel_el_mirror() {
    local elv="${1:-${version}}"
    local pkgmgr="${2:-dnf}"
    local base="" kml="" kml_core="" tmp="" keyfile=""
    local p=""

    [[ -z "${KERNEL_MIRROR:-}" ]] && select_kernel_mirror
    base="${KERNEL_MIRROR}"
    kml=$(curl -s --connect-timeout 8 -m 20 "${base}/" 2>/dev/null | grep -oE 'kernel-ml-[0-9][^"<]*\.rpm' | grep -E "\.el${elv}\." | grep -vE 'devel|doc|headers|tools' | sort -uV | tail -1)
    [[ -z "${kml}" ]] && return 1
    kml_core="kernel-ml-core-${kml#kernel-ml-}"

    # 已安装的内核不低于镜像提供版本时跳过下载（避免重复下载/降级安装）
    local installed=""
    installed=$(rpm -qa 'kernel-ml-[0-9]*' 2>/dev/null | sort -V | tail -1)
    if [[ -n "${installed}" ]]; then
        if [[ "$(printf '%s\n%s\n' "${installed}" "${kml}" | sort -V | tail -1)" == "${installed}" ]]; then
            echo -e "${C_GREEN}[+] 已安装内核 ${installed} 不旧于镜像最新 ${kml}，无需重复下载安装${C_RESET}"
            return 0
        fi
    fi

    # GPG key 优先从内核镜像获取（离线可用），失败回退 elrepo 官网
    keyfile=$(mktemp /tmp/elrepo-key.XXXXXX)
    if ! curl -sfL --connect-timeout 8 -m 15 -o "${keyfile}" "${KERNEL_MIRROR}/RPM-GPG-KEY-elrepo.org" 2>/dev/null; then
        curl -sfL --connect-timeout 8 -m 15 -o "${keyfile}" https://www.elrepo.org/RPM-GPG-KEY-elrepo.org 2>/dev/null
    fi
    [[ -s "${keyfile}" ]] && rpm --import "${keyfile}" 2>/dev/null
    rm -f "${keyfile}"

    tmp=$(mktemp -d /tmp/kernel-ml.XXXXXX)
    for p in "${kml}" "${kml_core}"; do
        echo -e "${C_YELLOW}[*] 下载 ${p} ...${C_RESET}"
        if ! curl -fL --progress-bar --connect-timeout 10 -m 900 -o "${tmp}/${p}" "${base}/${p}"; then
            rm -rf "${tmp}"
            return 1
        fi
    done
    if ! ${pkgmgr} install -y "${tmp}/${kml}" "${tmp}/${kml_core}" >/dev/null 2>&1; then
        echo -e "${C_RED}镜像内核安装失败，回退官方仓库重试。${C_RESET}"
        rm -rf "${tmp}"
        return 1
    fi
    rm -rf "${tmp}"
    return 0
}

# 内核镜像分流：按出口 IP 归属地选择镜像，大陆(CN)→igz，海外(含港澳台)→ipan；检测失败兜底 igz
select_kernel_mirror() {
    local country=""
    country=$(curl -s4 --connect-timeout 3 -m 5 "http://ip-api.com/line/?fields=countryCode" 2>/dev/null | tr -d ' \r\n' || true)
    if [[ ! "${country}" =~ ^[A-Za-z]{2}$ ]]; then
        country=$(curl -s4 --connect-timeout 3 -m 5 https://ipinfo.io/country 2>/dev/null | tr -d ' \r\n' || true)
    fi
    if [[ "${country}" == "CN" ]]; then
        KERNEL_MIRROR="${MIRROR_KERNEL_IGZ}"
        echo -e "${C_GREEN}[+] 大陆网络(CN)，内核镜像使用 igz: ${KERNEL_MIRROR}${C_RESET}"
    elif [[ -n "${country}" ]]; then
        KERNEL_MIRROR="${MIRROR_KERNEL_IPAN}"
        echo -e "${C_GREEN}[+] 海外网络(${country})，内核镜像使用 ipan: ${KERNEL_MIRROR}${C_RESET}"
    else
        KERNEL_MIRROR="${MIRROR_KERNEL_IGZ}"
        echo -e "${C_YELLOW}[-] 网络归属检测失败，默认使用 igz 内核镜像${C_RESET}"
    fi
}

delete_kernel() {
    # 只保留新内核(NEW_KERNEL)，删除所有旧内核（含当前运行内核，重启后即生效）
    # 若 NEW_KERNEL 为空则跳过删除，避免误删全部内核
    local del_list=""

    if [[ "${release}" == "centos" ]]; then
        if [[ -z "${NEW_KERNEL}" ]]; then
            return 0
        fi
        # 只删旧的内核主包：保留新内核、所有 headers/devel/tools
        del_list=$(rpm -qa | grep -E '^(kernel|kernel-ml|kernel-lt)-[0-9]' \
            | grep -vE 'kernel-(ml|lt)-(devel|headers|tools)' \
            | grep -vF "${NEW_KERNEL}")
        if [[ -n "${del_list}" ]]; then
            echo -e "${C_YELLOW}正在清理旧内核...${C_RESET}"
            rpm -e ${del_list} --nodeps 2>/dev/null
            echo -e "${C_GREEN}旧内核清理完成。${C_RESET}"
        fi
    elif [[ "${release}" == "debian" || "${release}" == "ubuntu" ]]; then
        if [[ -z "${NEW_KERNEL}" ]]; then
            return 0
        fi
        # 只保留本次新装内核（NEW_KERNEL），其余全部 purge
        del_list=$(dpkg -l | grep '^ii' | grep -oE 'linux-image-[0-9][^ ]*' | grep -vF "${NEW_KERNEL}")
        if [[ -n "${del_list}" ]]; then
            echo -e "${C_YELLOW}正在清理旧内核...${C_RESET}"
            DEBIAN_FRONTEND=noninteractive apt-get purge -y ${del_list} >/dev/null 2>&1
            echo -e "${C_GREEN}旧内核清理完成。${C_RESET}"
        fi
    fi
}

update_grub() {
    if [[ "${release}" == "centos" ]]; then
        # 所有 EL 版本（7/8/9/AliLinux 等）都刷新引导
        if command -v grub2-mkconfig >/dev/null 2>&1; then
            grub2-mkconfig -o /boot/grub2/grub.cfg
            grub2-set-default 0 2>/dev/null
        else
            grub-mkconfig -o /boot/grub/grub.cfg 2>/dev/null || true
        fi
    else
        update-grub
    fi
    echo -e "${C_GREEN}引导更新完成。${C_RESET}"
}

install_kernel() {
    if [[ "${release}" == "alinux" ]]; then
        echo -e "${C_GREEN}Alibaba Cloud Linux 3/4 无需更换内核，直接应用 ${BBR_LABEL} + FQ 调优。${C_RESET}"
        sleep 1
        enable_bbr_algo fq "FQ (通用型/云盘型)"
        return 0
    fi
    check_version
    [[ -z "${l2tp_raw}" ]] && l2tp_raw=$(check_l2tp_support)

    if kernel_sufficient && [[ "${l2tp_raw}" == "支持L2TP" ]] && ! kernel_outdated; then
        echo -e "${C_GREEN}当前内核已支持 BBR/FQ 加速与 L2TP，无需更新。${C_RESET}"
        sleep 2
        return
    fi
    if kernel_outdated; then
        echo -e "${C_YELLOW}[*] 当前内核 (${kernel_info:-$(uname -r)}) 已落后于镜像 (${mirror_kernel_ml_version})，更新后将获得新内核...${C_RESET}"
    elif kernel_sufficient; then
        echo -e "${C_YELLOW}[*] 当前内核缺少 L2TP 支持（如 Debian cloud 精简内核），更新后将获得 L2TP 能力...${C_RESET}"
    else
        echo -e "${C_YELLOW}[*] 当前内核缺少 BBR/FQ_PIE 支持，开始安装新内核...${C_RESET}"
    fi
    select_mirror

    if [[ "${release}" == "centos" ]]; then
        # ---- EL 系列：按主版本选正确的 elrepo ----
        local repo_url=""
        local pkgmgr=""
        case "${version}" in
            7) repo_url="https://www.elrepo.org/elrepo-release-7.0-6.el7.elrepo.noarch.rpm"; pkgmgr="yum" ;;
            8) repo_url="https://www.elrepo.org/elrepo-release-8.3-1.el8.elrepo.noarch.rpm"; pkgmgr="dnf" ;;
            9) repo_url="https://www.elrepo.org/elrepo-release-9.0-1.el9.elrepo.noarch.rpm"; pkgmgr="dnf" ;;
            *) echo -e "${C_RED}不支持的 EL 版本 (${version})，已中止，系统未做任何修改。${C_RESET}"; return 1 ;;
        esac

        if ! rpm -q elrepo-release >/dev/null 2>&1; then
            rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org 2>/dev/null
            if ! ${pkgmgr} install -y "${repo_url}"; then
                echo -e "${C_RED}elrepo 安装失败，已中止，系统未做任何修改。${C_RESET}"
                return 1
            fi
        fi

        if [[ "${version}" == "7" ]]; then
            # EL7 已于 2024-06-30 EOL：官方/elrepo 仓库已清空且不可靠（可能只回显已装的旧包）。
            # 直接走内核镜像（ipan/igz）安装最终版 kernel-ml，镜像不可用再回退官方归档。
            if ! install_kernel_el7_archive; then
                return 1
            fi
        elif install_kernel_el_mirror "${version}" "${pkgmgr}"; then
            echo -e "${C_GREEN}[+] kernel-ml (el${version}) 镜像安装完成。${C_RESET}"
        else
            echo -e "${C_YELLOW}[-] 内核镜像不可用，回退 elrepo-kernel 仓库安装...${C_RESET}"
            if ! ${pkgmgr} --enablerepo=elrepo-kernel install -y kernel-ml; then
                echo -e "${C_RED}内核安装失败，已中止，旧内核完好保留。${C_RESET}"
                return 1
            fi
        fi
        # rpm -q 输出顺序不可靠，必须按版本排序取最高版本（同 Debian 差集思路）
        local top_kml=""
        top_kml=$(rpm -qa 'kernel-ml-[0-9]*' | sort -V | tail -1)
        [ -n "${top_kml}" ] && NEW_KERNEL=$(rpm -q --qf "%{VERSION}-%{RELEASE}" "${top_kml}")
        [ -z "${NEW_KERNEL}" ] && NEW_KERNEL=$(rpm -qa | grep '^kernel-ml-[0-9]' | sort -V | tail -1)

    elif [[ "${release}" == "debian" || "${release}" == "ubuntu" ]]; then
        # 优先从内核镜像安装 joeyblog BBRv3（x86_64/arm64），镜像不可用时回退官方 apt 内核
        local before_list=""
        before_list=$(dpkg -l | grep '^ii' | grep -oE 'linux-image-[0-9][^ ]*' | sort -u)
        if install_kernel_deb_mirror; then
            echo -e "${C_GREEN}[+] joeyblog BBRv3 内核安装完成。${C_RESET}"
        else
            echo -e "${C_YELLOW}[-] 内核镜像不可用，回退系统官方源安装内核...${C_RESET}"
            apt update -y || { echo -e "${C_RED}apt update 失败，已中止，系统未做任何修改。${C_RESET}"; return 1; }

            local pkg_list=""
            if [[ "${release}" == "debian" ]]; then
                pkg_list="linux-image-amd64 linux-headers-amd64"
            else
                # Ubuntu 没有 linux-image-amd64 包：按可用包探测（老版本用 HWE 才能拿到 5.6+ 内核）
                local codename=""
                [ -f /etc/os-release ] && codename=$(grep -E '^UBUNTU_CODENAME' /etc/os-release | cut -d= -f2)
                for p in "linux-image-generic-hwe-${codename}" linux-image-virtual linux-image-generic; do
                    if apt-cache show "${p}" >/dev/null 2>&1; then
                        pkg_list="${p}"
                        break
                    fi
                done
                if [[ -z "${pkg_list}" ]]; then
                    echo -e "${C_RED}未找到可用的 Ubuntu 内核包，已中止，系统未做任何修改。${C_RESET}"
                    return 1
                fi
            fi

            if ! apt install -y ${pkg_list}; then
                echo -e "${C_RED}内核安装失败，已中止，旧内核完好保留。${C_RESET}"
                return 1
            fi
        fi
        # 差集定位"本次新装的内核"
        # （同版本号的 amd64 与 cloud-amd64 共存时 sort -V 无法区分新旧，必须用差集）
        NEW_KERNEL=$(comm -13 <(echo "${before_list}") <(dpkg -l | grep '^ii' | grep -oE 'linux-image-[0-9][^ ]*' | sort -u) | head -1)
        [ -z "${NEW_KERNEL}" ] && NEW_KERNEL=$(dpkg -l | grep '^ii' | grep -oE 'linux-image-[0-9][^ ]*' | sort -V | tail -1)
    fi

    delete_kernel
    update_grub

    # 重启后默认启用 ${BBR_LABEL} + FQ
    sed -i '/net.core.default_qdisc/d;/net.ipv4.tcp_congestion_control/d' /etc/sysctl.conf
    cat >> /etc/sysctl.conf <<EOF
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
EOF
    echo -e "${C_GREEN}[+] 已写入 sysctl，重启后将默认启用 ${BBR_LABEL} + FQ${C_RESET}"

    echo -e "${C_YELLOW}新内核 ${NEW_KERNEL} 安装并更新引导完成。${C_RESET}"
    read -p "$(echo -e "${C_BLUE}是否立即重启生效？[y/N]: ${C_RESET}")" reboot_choice
    if [[ "${reboot_choice}" =~ ^[yY]$ ]]; then
        echo -e "${C_YELLOW}3 秒后重启...${C_RESET}"
        sleep 3
        reboot
    else
        echo -e "${C_GREEN}已跳过重启，请稍后手动重启以启用新内核。${C_RESET}"
        sleep 2
    fi
}

has_congestion() {
    grep -qw "$1" /proc/sys/net/ipv4/tcp_available_congestion_control
}

enable_vpn_algo() {
    remove_all_config
    check_tc || true
    local qdisc_sel="fq_pie"
    if ! modprobe sch_fq_pie 2>/dev/null; then
        if modprobe sch_fq_codel 2>/dev/null; then
            qdisc_sel="fq_codel"
            echo -e "${C_YELLOW}[-] 内核无 FQ_PIE 支持，已降级使用 fq_codel${plain}"
        else
            echo -e "${C_RED}FQ 队列模块均不可用，启用 VPN 算法失败。${C_RESET}"
            return 1
        fi
    fi
    if ! has_congestion cubic; then
        modprobe tcp_cubic 2>/dev/null
        if ! has_congestion cubic; then
            echo -e "${C_RED}CUBIC 不可用，启用 VPN 算法失败。${C_RESET}"
            return 1
        fi
    fi
    cat >> /etc/sysctl.conf <<EOF
net.core.default_qdisc=${qdisc_sel}
net.ipv4.tcp_congestion_control=cubic
net.ipv4.ip_forward=1
net.ipv4.tcp_mtu_probing=1
EOF
    sysctl -p &>/dev/null
    local fail=0
    for iface in $(ls /sys/class/net | grep -v '^lo$'); do
        tc qdisc replace dev ${iface} root ${qdisc_sel} 2>/dev/null || fail=1
    done
    if [[ "$(sysctl -n net.ipv4.tcp_congestion_control)" == "cubic" && \
          "$(sysctl -n net.core.default_qdisc)" == "${qdisc_sel}" && \
          ${fail} -eq 0 ]]; then
        echo -e "${C_GREEN}VPN算法 (CUBIC + ${qdisc_sel^^}) 启用成功。${C_RESET}"
    else
        echo -e "${C_RED}配置未完全生效，请检查网卡与模块后重试。${C_RESET}"
    fi
}

# BBR3 通用启用：附带与选项2/3 一致的系统性优化（清旧配置、IP转发、MTU探测、tc 即时生效）
enable_bbr_algo() {
    local qdisc_sel="$1"
    local qdisc_label="$2"
    local qdisc_param="${3:-}"
    remove_all_config
    check_tc || true
    if ! modprobe "sch_${qdisc_sel}" 2>/dev/null; then
        echo -e "${C_RED}${qdisc_label} 队列模块不可用，启用失败。${C_RESET}"
        return 1
    fi
    if ! has_congestion bbr; then
        modprobe tcp_bbr 2>/dev/null
        if ! has_congestion bbr; then
            echo -e "${C_RED}${BBR_LABEL} 不可用，启用 ${qdisc_label} 失败。${C_RESET}"
            return 1
        fi
    fi
    cat >> /etc/sysctl.conf <<EOF
net.core.default_qdisc=${qdisc_sel}
net.ipv4.tcp_congestion_control=bbr
net.ipv4.ip_forward=1
net.ipv4.tcp_mtu_probing=1
EOF
    sysctl -p &>/dev/null
    local fail=0
    for iface in $(ls /sys/class/net | grep -v '^lo$'); do
        if [[ -n "${qdisc_param}" ]]; then
            tc qdisc replace dev ${iface} root ${qdisc_sel} ${qdisc_param} 2>/dev/null || fail=1
        else
            tc qdisc replace dev ${iface} root ${qdisc_sel} 2>/dev/null || fail=1
        fi
    done
    if [[ "$(sysctl -n net.ipv4.tcp_congestion_control)" == "bbr" && \
          "$(sysctl -n net.core.default_qdisc)" == "${qdisc_sel}" && \
          ${fail} -eq 0 ]]; then
        echo -e "${C_GREEN}${BBR_LABEL} + ${qdisc_label} 启用成功。${C_RESET}"
    else
        echo -e "${C_RED}配置未完全生效，请检查网卡与模块后重试。${C_RESET}"
    fi
}

remove_all_config() {
    sed -i '/net.core.default_qdisc/d' /etc/sysctl.conf
    sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.conf
    sed -i '/net.ipv4.tcp_mtu_probing/d' /etc/sysctl.conf
    sed -i '/net.ipv4.ip_forward/d' /etc/sysctl.conf
    sed -i '/fs.file-max/d' /etc/sysctl.conf
    sed -i '/fs.inotify.max_user_instances/d' /etc/sysctl.conf
    sed -i '/net.core.somaxconn/d' /etc/sysctl.conf
    sed -i '/net.core.rmem_max/d' /etc/sysctl.conf
    sed -i '/net.core.wmem_max/d' /etc/sysctl.conf
    sed -i '/net.core.wmem_default/d' /etc/sysctl.conf
    sysctl -p &>/dev/null
    echo -e "${C_GREEN}所有配置已清除。${C_RESET}"
    sleep 1
}

# 读取实际生效的网卡 qdisc 种类（default_qdisc 只决定新接口的默认值，tc 已挂载的才算数）
# 多队列网卡 root 是 mq/noqueue 包裹子队列，取出现最多的非 mq/noqueue 种类
get_active_qdisc() {
    local iface="" kinds=""
    for iface in $(ls /sys/class/net | grep -v '^lo$'); do
        kinds=$(tc qdisc show dev "${iface}" 2>/dev/null \
            | awk '$1=="qdisc" && $2!="mq" && $2!="noqueue" {print $2}')
        kinds=$(echo "${kinds}" | sort | uniq -c | sort -rn | awk '{print $2; exit}')
        [[ -n "${kinds}" ]] && { echo "${kinds}"; return; }
    done
    echo ""
}

check_status() {
    local kv=$(uname -r)
    kernel_info="${kv}"
    l2tp_raw=$(check_l2tp_support)
    if [[ "${l2tp_raw}" == "支持L2TP" ]]; then
        l2tp_info="${C_GREEN}支持L2TP${C_RESET}"
    else
        l2tp_info="${C_YELLOW}不支持L2TP${C_RESET}"
    fi

    cc=$(sysctl net.ipv4.tcp_congestion_control 2>/dev/null | awk -F'=' '{print $2}' | xargs)
    qdisc=$(sysctl net.core.default_qdisc 2>/dev/null | awk -F'=' '{print $2}' | xargs)
    active_qdisc=$(get_active_qdisc)

    if [[ "${cc}" == "cubic" ]]; then
        algo_info="CUBIC + ${qdisc^^}"
    elif [[ "${cc}" == "bbr" ]]; then
        algo_info="${BBR_LABEL} + ${qdisc^^}"
    else
        algo_info="${cc:-none} + ${qdisc:-none}"
    fi
}

main_menu() {
    clear
    check_status

    # 标记当前已生效的优化（按 拥塞控制 + 实际网卡 qdisc 组合判定）
    local m2="" m3="" m4="" m5="" m6=""
    local mark="${C_GREEN}✔ 已启用${C_RESET}"
    [[ "${cc}" == "cubic" && "${active_qdisc}" == "fq_pie" ]] && m2=" ${mark}"
    [[ "${cc}" == "bbr" && "${active_qdisc}" == "fq" ]] && m3=" ${mark}"
    [[ "${cc}" == "bbr" && "${active_qdisc}" == "fq_codel" ]] && m4=" ${mark}"
    [[ "${cc}" == "bbr" && "${active_qdisc}" == "fq_pie" ]] && m5=" ${mark}"
    [[ "${cc}" == "bbr" && "${active_qdisc}" == "cake" ]] && m6=" ${mark}"

    if kernel_sufficient; then
        if [[ "${l2tp_raw}" == "支持L2TP" ]] && ! kernel_outdated; then
            opt1_text="无需更新内核"
            opt1_color="${C_GREEN}"
        else
            opt1_text="建议更新内核"
            opt1_color="${C_YELLOW}"
        fi
    else
        opt1_text="建议更新内核"
        opt1_color="${C_YELLOW}"
    fi

    echo -e "${C_CYAN}==========================================${C_RESET}"
    echo -e "${C_GREEN}         BBR加速管理脚本${C_RESET} ${C_YELLOW}${sys_name}${C_RESET}"
    echo -e "${C_CYAN}==========================================${C_RESET}"
    echo -e " ${C_BLUE}内核: ${C_GREEN}${kernel_info}${C_RESET} ${C_YELLOW}|${C_RESET} ${l2tp_info}"
    echo -e " ${C_BLUE}算法: ${C_GREEN}${algo_info}${C_RESET}"
    echo -e "${C_CYAN}==========================================${C_RESET}"
    echo -e " ${C_GREEN}1.${C_RESET} ${opt1_color}${opt1_text}${C_RESET}"
    echo -e " ${C_GREEN}2.${C_RESET} ${C_BLUE}启用VPN算法 (CUBIC + FQ_PIE)${C_RESET}${m2}"
    echo -e " ${C_GREEN}3.${C_RESET} ${C_BLUE}启用 ${BBR_LABEL} + FQ (通用型/云盘型)${C_RESET}${m3}"
    echo -e " ${C_GREEN}4.${C_RESET} ${C_BLUE}启用 ${BBR_LABEL} + FQ_CODEL (超低延迟/流量 > 带宽)${C_RESET}${m4}"
    echo -e " ${C_GREEN}5.${C_RESET} ${C_BLUE}启用 ${BBR_LABEL} + FQ_PIE (抗抖动/低丢包线路)${C_RESET}${m5}"
    echo -e " ${C_GREEN}6.${C_RESET} ${C_BLUE}启用 ${BBR_LABEL} + CAKE (QoS整形/多流公平)${C_RESET}${m6}"
    echo -e " ${C_GREEN}7.${C_RESET} ${C_BLUE}卸载全部加速配置${C_RESET}"
    echo -e " ${C_RED}0.${C_RESET} ${C_BLUE}退出脚本${C_RESET}"
    echo -e "${C_CYAN}==========================================${C_RESET}"
    echo
    
    read -p "$(echo -e "${C_BLUE} 请输入数字 [0-7]: ${C_RESET}")" choice
    case "${choice}" in
        1) install_kernel ;;
        2) enable_vpn_algo; read -p "按回车键继续..." ;;
        3) enable_bbr_algo fq "FQ (通用型/云盘型)"; read -p "按回车键继续..." ;;
        4) enable_bbr_algo fq_codel "FQ_CODEL (超低延迟/流量 > 带宽)"; read -p "按回车键继续..." ;;
        5) enable_bbr_algo fq_pie "FQ_PIE (抗抖动/低丢包线路)"; read -p "按回车键继续..." ;;
        6)
            read -p "$(echo -e "${C_BLUE}输入 CAKE 整形带宽（单位 M，如 50，回车跳过=纯公平模式）: ${C_RESET}")" cake_bw
            if [[ -n "${cake_bw}" && "${cake_bw}" =~ ^[0-9]+$ ]]; then
                enable_bbr_algo cake "CAKE (QoS整形 ${cake_bw}M)" "bandwidth ${cake_bw}Mbit"
            else
                enable_bbr_algo cake "CAKE (QoS整形/多流公平)"
            fi
            read -p "按回车键继续..." ;;
        7) remove_all_config; read -p "按回车键继续..." ;;
        0) exit 0 ;;
        *) echo -e "${C_RED}无效输入，请重新选择。${C_RESET}" && sleep 1 ;;
    esac
    main_menu
}

check_sys
[[ ! "${release}" =~ ^(debian|ubuntu|centos|alinux)$ ]] && echo "错误：不支持当前系统。" && exit 1
check_curl
check_version
get_sys_name

# BBR3 仅 Debian/Ubuntu 系（XanMod/BBRv3 内核）；其他系统为原版 BBR
if [[ "${release}" == "debian" || "${release}" == "ubuntu" ]]; then
    BBR_LABEL="BBR3"
else
    BBR_LABEL="BBR"
fi

main_menu