#!/usr/bin/env bash
# =============================================================================
#  restic_backup.sh — 文件增量加密备份脚本（暂停容器后直接备份）
#  依赖：restic ≥ 0.14.0（自动安装）
#  文档参考：https://restic.readthedocs.io/en/stable/
# =============================================================================
#
# 【定时任务】复制以下行到 crontab（sudo crontab -e）：
#
#   每周日凌晨 3:00 执行
#   0 3 * * 0 /bin/bash /opt/scripts/restic_backup.sh
#
#   如需每天执行：
#   0 3 * * * /bin/bash /opt/scripts/restic_backup.sh
#
# =============================================================================
set -euo pipefail

# ─────────────────────────────────────────────────────────────────────────────
# 【用户自定义区域】— 仅需修改此区域
# ─────────────────────────────────────────────────────────────────────────────

# --- Restic 加密密码（必填）---
export RESTIC_PASSWORD="your-strong-restic-password"

# --- S3 存储后端（选择一种，注释另一种）---
#
# ① AWS 原生 S3 — 格式：s3:s3.<region>.amazonaws.com/<bucket>/<path>
RESTIC_REPOSITORY="s3:s3.ap-east-1.amazonaws.com/your-bucket-name/restic"
export AWS_ACCESS_KEY_ID="YOUR_AWS_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="YOUR_AWS_SECRET_KEY"
#
# ② S3 兼容服务（Cloudflare R2 / MinIO / Wasabi 等）
# RESTIC_REPOSITORY="s3:https://your-endpoint.example.com/your-bucket/restic"
# export AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY"
# export AWS_SECRET_ACCESS_KEY="YOUR_SECRET_KEY"

export RESTIC_REPOSITORY

# --- 要备份的目录列表 ---
BACKUP_PATHS=(
    "/opt/pod"
    # "/home/user/app"
)

# --- 从备份中排除的路径（支持 glob）---
EXCLUDE_PATTERNS=(
    "/opt/pod/**/logs"
    "/opt/pod/**/cache"
    "/opt/pod/**/node_modules"
    "*.tmp"
    "*.pid"
    "*.lock"
)

# --- 云端快照保留策略 ---
KEEP_LAST=2
KEEP_WEEKLY=4
KEEP_MONTHLY=2

# --- 备份标签 ---
BACKUP_TAG="weekly-backup"

# ─────────────────────────────────────────────────────────────────────────────
# 【以下内容请勿修改】
# ─────────────────────────────────────────────────────────────────────────────

TIMESTAMP=$(date '+%Y-%m-%d_%H-%M-%S')

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOG_DIR="${SCRIPT_DIR}/logs"
mkdir -p "$LOG_DIR"
LOG_FILE="${LOG_DIR}/restic_${TIMESTAMP}.log"

# 只保留最近 30 个日志文件
find "$LOG_DIR" -name 'restic_*.log' -type f | sort | head -n -30 | xargs -r rm -f

log()  { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO]  $*" | tee -a "$LOG_FILE"; }
warn() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [WARN]  $*" | tee -a "$LOG_FILE"; }
err()  {
    local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*"
    echo "$msg" >> "$LOG_FILE"
    echo "$msg" >&2
}
die()  { err "$*"; exit 1; }

# ── 1. 安装依赖 ───────────────────────────────────────────────────────────────
install_dependencies() {
    log "============================================================"
    log "阶段 1/3：检查并安装依赖工具"
    log "============================================================"

    local pkg_manager=""
    if command -v apt-get &>/dev/null; then
        pkg_manager="apt"
    elif command -v dnf &>/dev/null; then
        pkg_manager="dnf"
    elif command -v yum &>/dev/null; then
        pkg_manager="yum"
    else
        warn "无法识别包管理器，请手动安装所需工具"
    fi

    local RESTIC_VERSION="0.18.1"
    local RESTIC_BASE_URL="https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}"

    local raw_arch restic_arch
    raw_arch=$(uname -m)
    case "$raw_arch" in
        x86_64)          restic_arch="amd64" ;;
        aarch64|arm64)   restic_arch="arm64" ;;
        armv7l|armv6l)   restic_arch="arm"   ;;
        *)               die "不支持的架构: $raw_arch，请手动安装 restic $RESTIC_VERSION" ;;
    esac

    local need_install=false
    if command -v restic &>/dev/null; then
        local installed_ver major minor
        installed_ver=$(restic version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
        major=$(echo "$installed_ver" | cut -d. -f1)
        minor=$(echo "$installed_ver" | cut -d. -f2)
        if (( major == 0 && minor < 14 )); then
            warn "已安装 restic $installed_ver < 0.14.0，升级至 $RESTIC_VERSION"
            need_install=true
        else
            log "restic $installed_ver 已安装 ✓"
        fi
    else
        log "未检测到 restic，开始安装 v${RESTIC_VERSION} (${restic_arch})..."
        need_install=true
    fi

    if [[ "$need_install" == "true" ]]; then
        if ! command -v bunzip2 &>/dev/null; then
            log "安装 bzip2..."
            [[ "$pkg_manager" == "apt" ]] && apt-get install -y bzip2
            [[ "$pkg_manager" =~ yum|dnf ]] && $pkg_manager install -y bzip2
        fi

        local bz2_filename bz2_file
        bz2_filename="restic_${RESTIC_VERSION}_linux_${restic_arch}.bz2"
        bz2_file="/tmp/${bz2_filename}"

        log "下载 ${bz2_filename}..."
        curl -fsSL --retry 3 --retry-delay 2 \
            -o "$bz2_file" \
            "${RESTIC_BASE_URL}/${bz2_filename}" \
            || die "下载失败，手动下载：${RESTIC_BASE_URL}/${bz2_filename}"

        local sha256sums_file="/tmp/restic_${RESTIC_VERSION}_SHA256SUMS"
        log "下载 SHA256SUMS 进行完整性校验..."
        if curl -fsSL --retry 3 --retry-delay 2 \
            -o "$sha256sums_file" \
            "${RESTIC_BASE_URL}/SHA256SUMS" 2>>"$LOG_FILE"; then

            local expected actual
            expected=$(grep "${bz2_filename}" "$sha256sums_file" | cut -d' ' -f1)
            if [[ -z "$expected" ]]; then
                warn "未找到 ${bz2_filename} 的校验值，跳过校验"
            else
                actual=$(sha256sum "$bz2_file" | cut -d' ' -f1)
                if [[ "$actual" != "$expected" ]]; then
                    rm -f "$bz2_file" "$sha256sums_file"
                    die "SHA256 校验失败！期望: $expected  实际: $actual"
                fi
                log "  SHA256 校验通过 ✓"
            fi
            rm -f "$sha256sums_file"
        else
            warn "SHA256SUMS 下载失败，跳过校验"
        fi

        log "解压并安装到 /usr/local/bin/restic..."
        bunzip2 -c "$bz2_file" > /usr/local/bin/restic
        chmod +x /usr/local/bin/restic
        rm -f "$bz2_file"
        log "$(restic version | head -1) 安装完成 ✓"
    fi

    log "依赖检查完毕"
}

# ── 2. Restic 备份 ────────────────────────────────────────────────────────────
run_restic_backup() {
    log "============================================================"
    log "阶段 2/3：Restic 增量加密备份"
    log "============================================================"

    if ! restic snapshots &>/dev/null 2>&1; then
        log "仓库不存在，初始化（repository version 2）..."
        restic init 2>&1 | tee -a "$LOG_FILE"
    else
        log "仓库已存在，跳过初始化"
    fi

    local exclude_args=()
    for pattern in "${EXCLUDE_PATTERNS[@]}"; do
        exclude_args+=("--exclude=$pattern")
    done

    log "备份路径: ${BACKUP_PATHS[*]}"
    log "压缩: --compression max（zstd 最高级）"

    if restic backup \
        --compression max \
        --tag "$BACKUP_TAG" \
        --tag "ts-$TIMESTAMP" \
        --one-file-system \
        "${exclude_args[@]}" \
        "${BACKUP_PATHS[@]}" \
        2>&1 | tee -a "$LOG_FILE"; then
        log "✓ 备份上传成功"
    else
        die "备份失败，查看日志：$LOG_FILE"
    fi
}

# ── 3. 云端快照清理 ───────────────────────────────────────────────────────────
prune_old_snapshots() {
    log "============================================================"
    log "阶段 3/3：云端快照清理（forget --prune）"
    log "============================================================"

    restic forget \
        --tag "$BACKUP_TAG" \
        --keep-last    "$KEEP_LAST" \
        --keep-weekly  "$KEEP_WEEKLY" \
        --keep-monthly "$KEEP_MONTHLY" \
        --prune \
        2>&1 | tee -a "$LOG_FILE"

    log "✓ 清理完成，当前 [$BACKUP_TAG] 快照："
    restic snapshots --tag "$BACKUP_TAG" 2>&1 | tee -a "$LOG_FILE"
}

# ── 主流程 ────────────────────────────────────────────────────────────────────
main() {
    [[ $EUID -ne 0 ]] && die "请以 root 运行：sudo ./restic_backup.sh"

    log "████████████████████████████████████████████████████████████"
    log "  备份开始 — $TIMESTAMP"
    log "  仓库: $RESTIC_REPOSITORY"
    log "████████████████████████████████████████████████████████████"

    install_dependencies
    run_restic_backup
    prune_old_snapshots

    log "████████████████████████████████████████████████████████████"
    log "  全部完成 ✓"
    log "████████████████████████████████████████████████████████████"
}

main "$@"
