#!/usr/bin/env bash
# 在线热备：不停服备份 SQLite（backup API 保证一致性）+ files/ 原始文件目录。
# 用法：./backup.sh [备份根目录]（默认 ../backups）
# 保留策略：日备保留 30 天，每月 1 号的首份备份额外保留 12 个月（monthly/）。
# 建议宿主机 cron：15 2 * * * /path/to/deploy/backup.sh >> /path/to/deploy/backup.log 2>&1
# 官方环境建议至少将 monthly/ 同步一份到异机/异盘。
set -euo pipefail
ROOT="$(cd "$(dirname "$0")" && pwd)"
DEST="${1:-$ROOT/../backups}"
CONTAINER="${CONTAINER:-caiwuzongzhang-app}"
STAMP="$(date +%Y%m%d-%H%M%S)"
DAY_DIR="$DEST/daily"
MONTH_DIR="$DEST/monthly"
mkdir -p "$DAY_DIR" "$MONTH_DIR"

# 1) SQLite 在线热备（容器内 python sqlite3 backup API，主库可继续写入）
#    备份文件写进数据卷 /app/data（宿主机 deploy/../data 可直接读取），避免
#    read_only 容器 + tmpfs 下 docker cp 取不到文件的问题。
docker exec -i "$CONTAINER" python - <<'PY'
import os, sqlite3
src = sqlite3.connect(os.environ.get("APP_DB_PATH", "/app/data/app.db"))
dst = sqlite3.connect("/app/data/.backup-tmp.db")
src.backup(dst)
dst.close(); src.close()
print("hot-backup ok")
PY
mv "$ROOT/../data/.backup-tmp.db" "$DAY_DIR/app.db.$STAMP"

# 2) 原始文件目录打包（银行原始证据，只读复制，绝不改动）
tar -C "$ROOT/.." -czf "$DAY_DIR/files.$STAMP.tar.gz" data/files

# 3) 完整性自检：PRAGMA integrity_check + 关键计数
docker run --rm -v "$DAY_DIR/app.db.$STAMP:/check/app.db:ro" python:3.12-slim-bookworm \
    python -c "
import sqlite3
c = sqlite3.connect('/check/app.db')
print('integrity:', c.execute('PRAGMA integrity_check').fetchone()[0])
for t in ('source_rows', 'transfer_match_decisions', 'period_close_runs', 'audit_log'):
    try:
        print(t, c.execute(f'SELECT COUNT(*) FROM {t}').fetchone()[0])
    except sqlite3.OperationalError:
        print(t, 'n/a')
"

# 4) 保留策略：日备 >30 天删除；每月 1 号留档 monthly/
find "$DAY_DIR" -name 'app.db.*' -mtime +30 -delete
find "$DAY_DIR" -name 'files.*.tar.gz' -mtime +30 -delete
if [ "$(date +%d)" = "01" ]; then
    cp "$DAY_DIR/app.db.$STAMP" "$MONTH_DIR/app.db.$STAMP"
    cp "$DAY_DIR/files.$STAMP.tar.gz" "$MONTH_DIR/files.$STAMP.tar.gz"
    find "$MONTH_DIR" -mtime +365 -delete
fi
echo "backup complete: $DAY_DIR/app.db.$STAMP"
