#!/usr/bin/env bash
# backup.sh — backup otomatis untuk pengguna desktop dan server kecil.
#
# Membaca daftar folder sumber dari berkas konfigurasi (backup.conf),
# lalu pada setiap eksekusi: membuat arsip tar.gz beserta checksum
# SHA-256, memperbarui mirror rsync, merotasi run lama, dan mencatat
# log. Berjalan sebagai user biasa — tidak butuh root.
#
# Variabel lingkungan (berguna untuk pengujian):
#   CONF   berkas konfigurasi yang dibaca (default: backup.conf)
#   STAMP  identitas run (default: tanggal hari ini, format YYYYMMDD)
#
# Kode keluar:
#   0 sukses                        1 konfigurasi tidak valid
#   2 folder sumber tidak ada       3 arsip gagal dibuat/dibaca
#   4 mirror rsync gagal            5 backup lain sedang berjalan

set -u

CONF="${CONF:-backup.conf}"
STAMP="${STAMP:-$(date +%Y%m%d)}"

# log(): tulis satu baris ke stdout sekaligus ke berkas log.
log() {
    printf '[%s] %s\n' "$STAMP" "$1" | tee -a "$LOG_FILE"
}

# die(): catat kegagalan lewat log, lalu keluar dengan kode tertentu.
die() {
    log "GAGAL: $1 (kode $2)"
    exit "$2"
}

# --- 1. Baca dan validasi konfigurasi ---------------------------------
# Pesan tahap ini keluar lewat stderr: letak berkas log sendiri baru
# diketahui setelah konfigurasi terbaca, jadi log belum bisa dipakai.
if [ ! -f "$CONF" ]; then
    printf 'backup.sh: berkas konfigurasi %s tidak ditemukan\n' "$CONF" >&2
    exit 1
fi

# shellcheck source=/dev/null
. "$CONF"

if [ -z "${BACKUP_DIRS:-}" ]; then
    printf 'backup.sh: BACKUP_DIRS belum diisi di %s\n' "$CONF" >&2
    exit 1
fi
if [ -z "${BACKUP_DEST:-}" ]; then
    printf 'backup.sh: BACKUP_DEST belum diisi di %s\n' "$CONF" >&2
    exit 1
fi
case ${KEEP_ARCHIVES:-} in
    ''|*[!0-9]*|0)
        printf 'backup.sh: KEEP_ARCHIVES harus angka 1 atau lebih\n' >&2
        exit 1
        ;;
esac

# --- 2. Siapkan tujuan, log, dan kunci anti-dobel ----------------------
mkdir -p "$BACKUP_DEST" || exit 1
LOG_FILE="${LOG_FILE:-$BACKUP_DEST/backup.log}"

# flock -n: bila kunci sibuk, ada backup lain yang masih berjalan.
exec 9>>"$BACKUP_DEST/.backup.lock"
if ! flock -n 9; then
    die "backup lain sedang berjalan; kunci .backup.lock sibuk" 5
fi

log "mulai backup: sumber = $BACKUP_DIRS"

# --- 3. Periksa semua folder sumber tersedia ---------------------------
hilang=""
# shellcheck disable=SC2086  # daftar sumber memang dipisah spasi
for src in $BACKUP_DIRS; do
    [ -d "$src" ] || hilang="$hilang $src"
done
if [ -n "$hilang" ]; then
    die "folder sumber tidak ditemukan:$hilang" 2
fi

# --- 4. Arsip tar.gz + checksum SHA-256 per folder sumber --------------
RUNDIR="$BACKUP_DEST/backup-$STAMP"
mkdir -p "$RUNDIR"

# shellcheck disable=SC2086
for src in $BACKUP_DIRS; do
    nama=$(basename "$src")
    arsip="$RUNDIR/$nama.tar.gz"
    # -C + basename: jalur di dalam arsip dimulai dari nama folder
    # sumber, bukan seluruh jalur relatifnya; --sort=name membuat
    # daftar isi arsip stabil dan terurut.
    if ! tar --sort=name -C "$(dirname "$src")" -czf "$arsip" "$nama"; then
        die "tar gagal membungkus $src" 3
    fi
    if ! tar -tzf "$arsip" > /dev/null; then
        die "arsip $arsip tidak terbaca" 3
    fi
    # Checksum disimpan di samping arsip agar restore bisa diverifikasi.
    ( cd "$RUNDIR" && sha256sum "$nama.tar.gz" > "$nama.tar.gz.sha256" )
    log "arsip: $src -> $arsip"
done

# --- 5. Mirror rsync: kondisi terkini tanpa kompresi -------------------
mkdir -p "$BACKUP_DEST/mirror"
# shellcheck disable=SC2086
for src in $BACKUP_DIRS; do
    rsync -a --delete "$src" "$BACKUP_DEST/mirror/" ||
        die "rsync gagal untuk $src" 4
done
log "mirror diperbarui: $BACKUP_DEST/mirror"

# --- 6. Rotasi: simpan hanya KEEP_ARCHIVES run terbaru ------------------
ls -1dt "$BACKUP_DEST"/backup-* 2> /dev/null |
    tail -n +"$((KEEP_ARCHIVES + 1))" |
    xargs -r rm -rf
log "rotasi: maksimal $KEEP_ARCHIVES run tersimpan"

log "selesai tanpa error"
exit 0
