#!/usr/bin/env bash
# -------------------------------------------------------------
# Debian12 系统深度优化脚本（带 Docker 镜像大小清理）
# -------------------------------------------------------------
set -euo pipefail
IFS=$'\n\t'

# ------------------- 全局只读变量 -------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOG_FILE="${SCRIPT_DIR}/system_optimize_$(date +%F_%H%M%S).log"
SYSCTL_CONF="/etc/sysctl.d/99-debian12-opt.conf"
DOCKER_CONF="/etc/docker/daemon.json"

# Docker data‑root（若未安装 Docker 则使用默认路径）
DOCKER_DATA_ROOT="$(docker info --format '{{.DockerRootDir}}' 2>/dev/null || echo '/var/lib/docker')"

# ------------------- 日志函数 -------------------
log(){
    local ts="$(date '+%Y-%m-%d %H:%M:%S')"
    echo "[$ts] $*" | tee -a "${LOG_FILE}"
}

# ------------------- 交互帮助 -------------------
prompt_yes_no(){
    local prompt="$1"
    read -rp "${prompt} (y/N) " answer
    [[ "$answer" =~ ^[Yy]$ ]]
}
prompt_docker_swap(){
    if prompt_yes_no "是否为 Docker 设置专属 swap (单位：MiB)？"; then
        read -rp "请输入专属 swap 大小（MiB），0 表示关闭： " sz
        [[ "$sz" =~ ^[0-9]+$ ]] && echo "$sz" || { log "无效输入，默认关闭 swap"; echo "0"; }
    else
        echo "0"
    fi
}
prompt_docker_data_root(){
    if prompt_yes_no "当前 Docker data‑root 为 ${DOCKER_DATA_ROOT}。是否迁移到新挂载点？"; then
        read -rp "请输入新挂载点的完整路径（如 /mnt/docker）： " new_root
        echo "$new_root"
    else
        echo ""
    fi
}

# ------------------- 1️⃣ Kernel 调优 -------------------
optimize_kernel(){
    local mem_kb=$(awk '/MemTotal:/ {print $2}' /proc/meminfo)
    local cpu_cores=$(nproc)

    # ----- 只读阈值（供后续 self_check 使用） -----
    readonly VM_SWAPPINESS=0
    readonly VM_MIN_FREE_KB=$(( mem_kb / 20 ))            # 5% 内存
    readonly WATERMARK_LOW_KBYTES=$(( mem_kb / 50 ))     # 2%
    readonly WATERMARK_HIGH_KBYTES=$(( mem_kb / 25 ))    # 4%
    readonly VFS_CACHE_PRESSURE=100

    # ----- 网络调优 -----
    local somaxconn=$(( cpu_cores * 256 ))
    local netdev_backlog=$(( cpu_cores * 256 ))

    cat > "${SYSCTL_CONF}" <<EOF
# -------------------------------------------------
# 自动生成 – ${HOSTNAME} @ $(date '+%F %T')
# MemTotal: $((mem_kb/1024)) MiB   CPU: ${cpu_cores}
# -------------------------------------------------
vm.swappiness = ${VM_SWAPPINESS}
vm.min_free_kbytes = ${VM_MIN_FREE_KB}
vm.watermark_low_kbytes = ${WATERMARK_LOW_KBYTES}
vm.watermark_high_kbytes = ${WATERMARK_HIGH_KBYTES}
vm.vfs_cache_pressure = ${VFS_CACHE_PRESSURE}
vm.dirty_background_ratio = 10
vm.dirty_ratio = 20
net.core.somaxconn = ${somaxconn}
net.core.netdev_max_backlog = ${netdev_backlog}
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 60
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 16384 16777216
fs.file-max = 2097152
EOF

    sysctl --system >/dev/null
    log "✅ Kernel 参数已写入 ${SYSCTL_CONF} 并已生效"
    log "🔍 关键参数：
   vm.swappiness          = ${VM_SWAPPINESS}
   vm.min_free_kbytes     = ${VM_MIN_FREE_KB} KiB
   vm.watermark_low_kbytes= ${WATERMARK_LOW_KBYTES} KiB
   vm.watermark_high_kbytes= ${WATERMARK_HIGH_KBYTES} KiB
   vm.vfs_cache_pressure  = ${VFS_CACHE_PRESSURE}
   net.core.somaxconn     = ${somaxconn}"
}

# ------------------- 2️⃣ Docker 配置 -------------------
configure_docker(){
    if [[ -f "${DOCKER_CONF}" ]]; then
        cp -a "${DOCKER_CONF}" "${DOCKER_CONF}.bak"
        log "已备份 Docker 配置 → ${DOCKER_CONF}.bak"
        daemon_json=$(cat "${DOCKER_CONF}")
    else
        daemon_json="{}"
        log "未检测到 ${DOCKER_CONF}，使用空 JSON"
    fi

    # ----- Docker 独立 swap -----
    local dswap
    dswap=$(prompt_docker_swap)
    if (( dswap > 0 )); then
        command -v jq >/dev/null || { apt-get update && apt-get install -y jq; }
        daemon_json=$(echo "$daemon_json" |
            jq --arg sz "$dswap" '.swap = ($sz|tonumber)')
        log "已在 Docker daemon 中开启 swap=${dswap}MiB"
    fi

    # ----- data‑root 迁移 -----
    local new_root
    new_root=$(prompt_docker_data_root)
    if [[ -n "$new_root" ]]; then
        mkdir -p "$new_root"
        log "停止 Docker 服务进行迁移"
        systemctl stop docker

        rsync -aH --info=progress2 "${DOCKER_DATA_ROOT}/" "${new_root}/"

        daemon_json=$(echo "$daemon_json" |
            jq --arg dr "$new_root" '."data-root" = $dr')
        log "Docker data‑root 已迁移至 ${new_root}"

        systemctl start docker
    fi

    # ----- 写回 daemon.json 并重启 Docker -----
    echo "$daemon_json" > "${DOCKER_CONF}"
    systemctl restart docker
    log "Docker 配置已写回 ${DOCKER_CONF} 并已重启"
}

# ------------------- 3️⃣ Docker 镜像清理 -------------------
# ① 方案 A：只做普通 prune（删除所有悬空/未被容器引用的镜像）
docker_cleanup_prune(){
    log "执行普通的 docker image prune（删除未被容器使用的镜像）……"
    docker image prune -a -f | tee -a "${LOG_FILE}"
}

# ② 方案 B：按大小删除（> 2 GiB）——纯 Bash + awk 实现
docker_cleanup_by_size(){
    local threshold=$((2 * 1024 * 1024 * 1024))   # 2 GiB，单位：字节
    log "正在查找并删除大于 2 GiB 的镜像（可能会稍慢）……"

    # 列出 <image-id> <size>，size 可能是 KiB、MiB、GiB 等
    docker images --format "{{.ID}} {{.Size}}" |
    awk -v limit="${threshold}" '
        function to_bytes(str){
            # 统一把各种单位转成字节
            # 支持 B,KB,KiB,MB,MiB,GB,GiB
            if (str ~ /[0-9.]+[Gg][iI]?[Bb]?/) { unit=1024*1024*1024 }
            else if (str ~ /[0-9.]+[Mm][iI]?[Bb]?/) { unit=1024*1024 }
            else if (str ~ /[0-9.]+[Kk][iI]?[Bb]?/) { unit=1024 }
            else { unit=1 }
            gsub(/[[:alpha:]]/,"",str)   # 去掉所有字母，只剩数字
            return str*unit
        }
        {
            size_bytes = to_bytes($2)
            if (size_bytes > limit) {
                print $1   # 只输出镜像 ID
            }
        }
    ' | while IFS= read -r img_id; do
        if [[ -n "$img_id" ]]; then
            log "删除镜像 $img_id（大于 2 GiB）"
            docker rmi -f "$img_id" || log "⚠️ 删除 $img_id 失败，可能被容器占用"
        fi
    done
    log "按大小清理结束"
}

# ------------------- 4️⃣ Docker 容器 / 卷 / 网络清理 -------------------
docker_cleanup_containers_and_volumes(){
    log "清理停止的容器、未使用的卷、网络…"
    docker container prune -f | tee -a "${LOG_FILE}"
    docker volume   prune -f | tee -a "${LOG_FILE}"
    docker network  prune -f | tee -a "${LOG_FILE}"
}

# ------------------- 5️⃣ 自检报告 -------------------
self_check(){
    log "=== 自检报告 ==="
    log "系统内存 (KiB)          : $(awk '/MemTotal:/ {print $2}' /proc/meminfo)"
    log "CPU 核心数              : $(nproc)"
    log "vm.min_free_kbytes      : ${VM_MIN_FREE_KB:-未定义}"
    log "Docker data‑root         : ${DOCKER_DATA_ROOT}"
    log "Docker 服务状态        : $(systemctl is-active docker)"
    log "已写入的 sysctl 参数："
    grep -E 'vm\.|net\.core' "${SYSCTL_CONF}" | tee -a "${LOG_FILE}"
    log "=== 自检结束 ==="
}

# ------------------- 主入口 -------------------
main(){
    log "=== 系统优化脚本启动 ==="
    optimize_kernel
    configure_docker

    # -------------------------------------------------
    # 下面两个变量决定使用哪种镜像清理方式
    #   PRUNE_ONLY   : true  → 只做普通 prune（方案 A）
    #   SIZE_CLEANUP : true  → 按大小清理（方案 B）
    # 只需要把其中一个设为 true，另一个设为 false 即可
    # -------------------------------------------------
    PRUNE_ONLY=true
    SIZE_CLEANUP=false

    if $PRUNE_ONLY; then
        docker_cleanup_prune
    fi

    if $SIZE_CLEANUP; then
        docker_cleanup_by_size
    fi

    docker_cleanup_containers_and_volumes
    self_check
    log "=== 所有任务完成，完整日志位于 ${LOG_FILE} ==="
}
main "$@"
