#!/bin/bash
# ============================================================
# Docker 一键安装脚本
# 适用平台: Ubuntu 22.04 (ARM64 / aarch64 / x86_64)
# 说明: 通过 Ubuntu 官方 ports 源安装 docker.io
#       不修改 eth0 网络配置, 不需要重启
# ============================================================

set -e

# ---------- 颜色输出 ----------
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

info()    { echo -e "${BLUE}[INFO]${NC}  $*"; }
success() { echo -e "${GREEN}[OK]${NC}    $*"; }
warn()    { echo -e "${YELLOW}[WARN]${NC}  $*"; }
error()   { echo -e "${RED}[ERROR]${NC} $*"; }
die()     { error "$*"; exit 1; }

echo ""
echo "============================================================"
echo "   Docker 一键安装脚本 (Ubuntu 22.04 ARM64/x86_64)"
echo "============================================================"
echo ""

# ---------- 验证函数 (必须在调用前定义) ----------
verify_docker() {
    echo ""
    info "验证 Docker 安装..."

    if ! command -v docker &>/dev/null; then
        die "docker 命令不存在, 安装可能失败"
    fi
    DOCKER_VER=$(docker --version)
    success "Docker 版本: $DOCKER_VER"

    if pgrep -x dockerd &>/dev/null; then
        success "dockerd 进程运行中 ✓"
    else
        warn "dockerd 进程未检测到, Docker 可能未正常启动"
    fi

    if [ -S /var/run/docker.sock ]; then
        success "Docker socket 存在: /var/run/docker.sock ✓"
    else
        warn "Docker socket 不存在, 服务可能未启动"
    fi

    if docker info &>/dev/null; then
        success "docker info 执行成功 ✓"
    else
        warn "docker info 失败, 请检查 Docker 服务状态"
        info "可尝试: systemctl status docker 或 journalctl -u docker"
    fi

    echo ""
    info "验证核心命令可用性..."
    for cmd in "docker pull --help" "docker push --help" "docker commit --help" \
               "docker export --help" "docker load --help" "docker images" \
               "docker ps"; do
        if $cmd &>/dev/null; then
            success "  $cmd ✓"
        else
            warn "  $cmd 执行异常"
        fi
    done
}

# ---------- 1. 检查架构 ----------
info "检查系统架构..."
ARCH=$(uname -m)
if [ "$ARCH" = "aarch64" ]; then
    success "架构: aarch64 (ARM64) ✓"
elif [ "$ARCH" = "x86_64" ]; then
    success "架构: x86_64 ✓"
else
    warn "当前架构为 $ARCH, 本脚本针对 aarch64/x86_64 优化, 继续执行但可能出现问题"
fi

# ---------- 2. 检查是否 root ----------
info "检查运行权限..."
if [ "$(id -u)" -ne 0 ]; then
    die "请使用 root 权限运行本脚本: sudo bash $0"
fi
success "当前为 root 用户 ✓"

# ---------- 3. 保护 eth0 ----------
info "检查 eth0 网络接口..."
if ! ip link show eth0 &>/dev/null; then
    warn "eth0 接口不存在, 跳过保护检查"
else
    ETH0_STATE=$(ip link show eth0 | grep -o 'state [A-Z]*' | awk '{print $2}')
    ETH0_IP=$(ip addr show eth0 | grep 'inet ' | awk '{print $2}' | head -1)
    success "eth0 状态: $ETH0_STATE, IP: ${ETH0_IP:-未分配}"
    info "注意: 本脚本不会修改 eth0 的任何配置"
fi

# ---------- 4. 检查 Docker 是否已安装 ----------
info "检查 Docker 是否已安装..."
if command -v docker &>/dev/null; then
    DOCKER_VER=$(docker --version 2>/dev/null)
    warn "Docker 已安装: $DOCKER_VER"
    read -r -p "是否重新安装/覆盖? [y/N] " REINSTALL
    if [[ ! "$REINSTALL" =~ ^[Yy]$ ]]; then
        info "跳过安装, 直接验证现有 Docker..."
        verify_docker
        exit 0
    fi
fi

# ---------- 5. 检查必要命令 ----------
info "检查依赖命令..."
MISSING_CMDS=()
for cmd in apt curl wget gpg; do
    if ! command -v "$cmd" &>/dev/null; then
        MISSING_CMDS+=("$cmd")
    fi
done

if [ ${#MISSING_CMDS[@]} -gt 0 ]; then
    warn "以下命令缺失: ${MISSING_CMDS[*]}, 尝试安装..."
    apt-get update -qq && apt-get install -y "${MISSING_CMDS[@]}" \
        || die "无法安装缺失命令: ${MISSING_CMDS[*]}, 请手动安装后重试"
fi
success "依赖命令检查完毕 ✓"

# ---------- 6. 检查网络连通性 ----------
info "检查网络连通性..."
NETWORK_OK=false

if curl -s --connect-timeout 8 http://ports.ubuntu.com/ &>/dev/null; then
    success "可访问 Ubuntu ports 源 (ports.ubuntu.com) ✓"
    NETWORK_OK=true
    APT_SOURCE="ubuntu_ports"
elif curl -s --connect-timeout 8 https://mirrors.tuna.tsinghua.edu.cn/ &>/dev/null; then
    success "可访问清华大学镜像源 ✓"
    NETWORK_OK=true
    APT_SOURCE="tsinghua"
elif curl -s --connect-timeout 8 https://mirrors.ustc.edu.cn/ &>/dev/null; then
    success "可访问中科大镜像源 ✓"
    NETWORK_OK=true
    APT_SOURCE="ustc"
else
    error "无法访问任何已知软件源!"
    echo ""
    echo "  请检查以下内容:"
    echo "  1. 网络是否正常: ping 114.114.114.114"
    echo "  2. DNS 是否正常: nslookup ports.ubuntu.com"
    echo "  3. 防火墙是否拦截出站流量"
    echo ""
    die "网络不可用, 无法继续安装"
fi

# ---------- 7. 更新 apt 缓存 ----------
info "更新 apt 软件包缓存..."
if ! apt-get update -qq 2>&1; then
    warn "apt update 出现警告, 尝试忽略错误继续..."
    apt-get update --allow-releaseinfo-change -qq 2>&1 || true
fi
success "apt 缓存更新完毕 ✓"

# ---------- 8. 安装依赖包 ----------
info "安装必要依赖包..."
DEPS=(ca-certificates curl gnupg lsb-release apt-transport-https)
MISSING_DEPS=()
for pkg in "${DEPS[@]}"; do
    if ! dpkg -l "$pkg" 2>/dev/null | grep -q '^ii'; then
        MISSING_DEPS+=("$pkg")
    fi
done

if [ ${#MISSING_DEPS[@]} -gt 0 ]; then
    info "安装缺失依赖: ${MISSING_DEPS[*]}"
    apt-get install -y "${MISSING_DEPS[@]}" \
        || die "依赖包安装失败, 请检查网络或 apt 源配置"
fi
success "依赖包就绪 ✓"

# ---------- 9. 安装 Docker ----------
info "开始安装 Docker (docker.io)..."

apt-get install -y \
    docker.io \
    docker-buildx \
    docker-compose-v2 \
    || die "Docker 安装失败! 请检查网络连接和 apt 源配置"

success "Docker 安装完毕 ✓"

# ---------- 10. 修复 iptables 后端 ----------
# 部分嵌入式/定制内核 (如 RT 内核) 不支持 nf_tables,
# 需切换到 iptables-legacy, 否则 dockerd 启动时报:
# "iptables (nf_tables): Could not fetch rule set generation id: Invalid argument"
info "检查 iptables 后端兼容性..."

# 若 iptables-legacy 不存在则先安装
if ! command -v iptables-legacy &>/dev/null && ! [ -f /usr/sbin/iptables-legacy ]; then
    info "iptables-legacy 不存在, 尝试安装 iptables..."
    apt-get install -y iptables 2>/dev/null || true
fi

if update-alternatives --list iptables 2>/dev/null | grep -q legacy; then
    update-alternatives --set iptables  /usr/sbin/iptables-legacy  2>/dev/null || true
    update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy 2>/dev/null || true
    success "已切换到 iptables-legacy 后端 ✓"
else
    warn "未找到 iptables-legacy 备选项, 若 Docker 启动失败请手动执行:"
    warn "  apt-get install -y iptables && update-alternatives --set iptables /usr/sbin/iptables-legacy"
fi

# ---------- 11. 配置 Docker 镜像加速 ----------
info "配置 Docker 镜像加速源..."
mkdir -p /etc/docker

# 检查内核是否有 iptable_raw 模块
# 部分嵌入式/定制内核 (如 RT 内核) 没有编译 iptable_raw,
# Docker 28.x 默认会用 raw 表做 DIRECT ACCESS FILTERING,
# 若模块不存在则 docker run 报错:
#   "can't initialize iptables table `raw': Table does not exist"
# 解决方案: 在 daemon.json 加 "allow-direct-routing": true 跳过该规则
ALLOW_DIRECT_ROUTING="false"
if ! modprobe iptable_raw 2>/dev/null && ! lsmod | grep -q '^iptable_raw'; then
    warn "内核缺少 iptable_raw 模块, 将启用 allow-direct-routing 绕过 raw 表限制"
    ALLOW_DIRECT_ROUTING="true"
else
    success "iptable_raw 模块可用 ✓"
fi

cat > /etc/docker/daemon.json <<EOF
{
    "registry-mirrors": [
        "https://docker.m.daocloud.io"
    ],
    "dns": ["114.114.114.114", "223.5.5.5"],
    "allow-direct-routing": ${ALLOW_DIRECT_ROUTING},
    "log-driver": "json-file",
    "log-opts": {
        "max-size": "10m",
        "max-file": "3"
    }
}
EOF
success "镜像加速配置完毕 ✓"

# ---------- 12. 启动并设置开机自启 ----------
info "启动 Docker 服务..."

# 注意: 嵌入式板子上 systemd 可能处于 degraded 状态 (非关键服务失败),
# 不能用 is-system-running 判断, 改为直接检查 systemctl 命令是否可用
# 以及 docker.service 单元文件是否存在
SYSTEMD_OK=false
if command -v systemctl &>/dev/null && systemctl cat docker.service &>/dev/null 2>&1; then
    SYSTEMD_OK=true
fi

if [ "$SYSTEMD_OK" = true ]; then
    systemctl enable docker 2>/dev/null || warn "无法设置 docker 开机自启"
    systemctl restart docker 2>/dev/null || warn "systemctl restart docker 失败, 尝试直接启动"
    sleep 3
    if systemctl is-active --quiet docker; then
        success "Docker 服务已通过 systemd 启动 ✓"
    else
        warn "systemd 启动失败, 查看日志: journalctl -u docker -n 20"
        warn "尝试直接启动 dockerd..."
        pkill -x dockerd 2>/dev/null || true
        sleep 1
        dockerd --host=unix:///var/run/docker.sock >> /var/log/dockerd.log 2>&1 &
        sleep 4
        if pgrep -x dockerd &>/dev/null; then
            success "dockerd 直接启动成功 ✓"
        else
            error "dockerd 启动失败, 请查看日志: /var/log/dockerd.log"
        fi
    fi
else
    warn "systemd 不可用, 直接启动 dockerd..."
    if ! pgrep -x dockerd &>/dev/null; then
        dockerd --host=unix:///var/run/docker.sock >> /var/log/dockerd.log 2>&1 &
        sleep 4
    fi
    if pgrep -x dockerd &>/dev/null; then
        success "dockerd 直接启动成功 ✓"
    else
        error "dockerd 启动失败, 请查看日志: /var/log/dockerd.log"
    fi
fi

# ---------- 13. 修复 DNS 解析 (内网DNS无法解析公网域名时的兜底方案) ----------
info "检查 Docker Hub 及镜像源 DNS 解析..."

DNS_FIXED=false
fix_dns_with_hosts() {
    local dns_server="$1"
    python3 -c "
import socket, sys

def dns_query(server, domain):
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.settimeout(3)
        tid = b'\xab\xcd'
        flags = b'\x01\x00'
        qdcount = b'\x00\x01'
        header = tid + flags + qdcount + b'\x00\x00\x00\x00\x00\x00'
        qname = b''
        for part in domain.split('.'):
            qname += bytes([len(part)]) + part.encode()
        qname += b'\x00'
        question = qname + b'\x00\x01\x00\x01'
        s.sendto(header + question, (server, 53))
        data, _ = s.recvfrom(512)
        ips = []
        i = 12
        while data[i] != 0: i += data[i] + 1
        i += 5
        while i < len(data) - 10:
            if data[i] >= 0xc0: i += 2
            else:
                while i < len(data) and data[i] != 0: i += data[i] + 1
                i += 1
            rtype = (data[i]<<8)|data[i+1]
            rdlen = (data[i+8]<<8)|data[i+9]
            i += 10
            if rtype == 1 and rdlen == 4:
                ips.append('.'.join(str(b) for b in data[i:i+4]))
            i += rdlen
        return ips
    except: return []
    finally: s.close()

server = sys.argv[1]
domains = [
    'docker.m.daocloud.io',
    'registry-1.docker.io',
    'auth.docker.io',
]
results = []
for d in domains:
    ips = dns_query(server, d)
    if ips:
        results.append(f'{ips[0]}  {d}')
if results:
    print('\n'.join(results))
    sys.exit(0)
else:
    sys.exit(1)
" "$dns_server" 2>/dev/null
}

HOSTS_BLOCK=""
for dns in 114.114.114.114 223.5.5.5; do
    HOSTS_BLOCK=$(fix_dns_with_hosts "$dns" 2>/dev/null) && DNS_FIXED=true && break
done

if [ "$DNS_FIXED" = true ]; then
    sed -i '/# Docker 镜像源/,/^$/d' /etc/hosts 2>/dev/null || true
    printf "\n# Docker 镜像源 (由 install_docker.sh 写入, 绕过内网DNS)\n%s\n" "$HOSTS_BLOCK" >> /etc/hosts
    success "DNS hosts 修复完毕 ✓"
else
    warn "公共 DNS 不可达, 跳过 hosts 修复 (pull 时可能遇到解析失败)"
fi

# ---------- 14. 验证安装 ----------
verify_docker

# ---------- 15. 完成提示 ----------
echo ""
echo "============================================================"
success "Docker 安装完成!"
echo "============================================================"
echo ""
echo "  常用命令速查:"
echo "    docker pull <镜像>             # 拉取镜像"
echo "    docker push <镜像>             # 推送镜像"
echo "    docker commit <容器> <镜像>    # 提交容器为镜像"
echo "    docker export <容器> -o x.tar  # 导出容器"
echo "    docker load -i <文件.tar>      # 加载镜像"
echo "    docker images                  # 查看本地镜像"
echo "    docker ps -a                   # 查看所有容器"
echo ""
echo "  注意事项:"
echo "    - eth0 网络配置未做任何修改"
echo "    - 本次安装无需重启系统"
echo "    - 镜像加速配置: /etc/docker/daemon.json"
echo "    - Docker 日志: journalctl -u docker 或 /var/log/dockerd.log"
echo ""
