#!/usr/bin/env bash
# pemeliharaan.sh — pemeliharaan berkala: periksa, bersihkan, laporkan.
#
# Membaca snapshot data sistem (df.txt, paket.txt, services.txt) dari
# folder data, memeriksa tiga hal — pemakaian disk, paket update
# tertunda, service gagal — lalu membersihkan file cache, log terkompresi
# (*.gz), dan file tmp usang HANYA di dalam folder whitelist yang
# tercantum di berkas konfigurasi. Mode default DRY-RUN: tidak ada
# file yang dihapus sampai --apply diberikan, dan mode tidak bisa
# diubah dari konfigurasi. Hasil akhirnya adalah laporan terstruktur
# yang ditulis ke stdout sekaligus berkas laporan.
#
# Pemakaian:
#   bash pemeliharaan.sh [--data-dir DIR] [--config FILE] [--apply]
#
# Variabel lingkungan (berguna untuk pengujian):
#   CONF      berkas konfigurasi yang dibaca (default: pemeliharaan.conf)
#   SOURCE_DIR  folder data; kalah oleh --data-dir (default: data)
#   HARI_INI  tanggal acuan umur file, format YYYY-MM-DD (default: hari ini)
#
# Kode keluar:
#   0 sukses                        1 konfigurasi tidak valid
#   2 berkas snapshot tidak ada     3 folder whitelist tidak ada

set -u

CONF="${CONF:-pemeliharaan.conf}"
DATA_DIR="${SOURCE_DIR:-data}"
HARI_INI="${HARI_INI:-$(date +%F)}"
APPLY=0

# --- 1. Baca argumen -----------------------------------------------------
while [ $# -gt 0 ]; do
    case "$1" in
        --apply) APPLY=1; shift ;;
        --data-dir|--config)
            if [ $# -lt 2 ]; then
                printf 'pemeliharaan.sh: %s butuh nilai\n' "$1" >&2
                exit 1
            fi
            if [ "$1" = "--data-dir" ]; then
                DATA_DIR="$2"
            else
                CONF="$2"
            fi
            shift 2
            ;;
        *)
            printf 'pemeliharaan.sh: argumen tidak dikenal: %s\n' "$1" >&2
            exit 1
            ;;
    esac
done

# --- 2. Baca dan validasi konfigurasi ------------------------------------
if [ ! -f "$CONF" ]; then
    printf 'pemeliharaan.sh: berkas konfigurasi %s tidak ditemukan\n' "$CONF" >&2
    exit 1
fi

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

if [ -z "${CACHE_DIRS:-}" ] && [ -z "${LOG_DIRS:-}" ] && [ -z "${TMP_DIRS:-}" ]; then
    printf 'pemeliharaan.sh: whitelist kosong (CACHE_DIRS/LOG_DIRS/TMP_DIRS) di %s\n' "$CONF" >&2
    exit 1
fi

valid_umur() {
    case $1 in
        ''|*[!0-9]*|0) return 1 ;;
    esac
    return 0
}

valid_umur "${CACHE_MAX_DAYS:-}" || {
    printf 'pemeliharaan.sh: CACHE_MAX_DAYS harus angka 1 atau lebih\n' >&2; exit 1; }
valid_umur "${LOG_MAX_DAYS:-}" || {
    printf 'pemeliharaan.sh: LOG_MAX_DAYS harus angka 1 atau lebih\n' >&2; exit 1; }
valid_umur "${TMP_MAX_DAYS:-}" || {
    printf 'pemeliharaan.sh: TMP_MAX_DAYS harus angka 1 atau lebih\n' >&2; exit 1; }

case ${DISK_WARN_PCT:-} in
    ''|*[!0-9]*|0)
        printf 'pemeliharaan.sh: DISK_WARN_PCT harus angka 1-99\n' >&2
        exit 1
        ;;
esac
if [ "$DISK_WARN_PCT" -gt 99 ]; then
    printf 'pemeliharaan.sh: DISK_WARN_PCT harus angka 1-99\n' >&2
    exit 1
fi

# Mode sengaja di set SETELAH konfigurasi dibaca: walau berkas
# konfigurasi berisi "MODE=apply", nilainya tetap ditimpa di sini.
# Hanya argumen --apply yang boleh menyalakan pembersihan sungguhan.
MODE="dry-run"
if [ "$APPLY" -eq 1 ]; then
    MODE="apply"
fi

# --- 3. Validasi snapshot data --------------------------------------------
for f in df.txt paket.txt services.txt; do
    if [ ! -f "$DATA_DIR/$f" ]; then
        printf 'pemeliharaan.sh: snapshot %s/%s tidak ditemukan\n' "$DATA_DIR" "$f" >&2
        exit 2
    fi
done

# --- 4. Pemeriksaan -------------------------------------------------------
# Temuan pemeriksaan terkumpul di sini, lalu menjadi bagian laporan.
# Disk: kolom ke-5 df adalah Use%; "+0" di awk membuang karakter '%'
# sehingga "89%" terbaca sebagai angka 89.
DISK_BARIS=$(awk -v ambang="$DISK_WARN_PCT" '
    NR > 1 {
        pct = $5 + 0
        if (pct >= ambang)
            printf "   %-10s %3d%%  WASPADA\n", $6, pct
        else
            printf "   %-10s %3d%%  OK\n", $6, pct
    }' "$DATA_DIR/df.txt")

DISK_MELEBIHI=$(awk -v ambang="$DISK_WARN_PCT" '
    NR > 1 && ($5 + 0) >= ambang {
        printf "%s%s", pisah, $6
        pisah = ", "
    }' "$DATA_DIR/df.txt")
DISK_N=$(awk -v ambang="$DISK_WARN_PCT" '
    NR > 1 && ($5 + 0) >= ambang { n++ } END { print n + 0 }' "$DATA_DIR/df.txt")

# Paket tertunda: output simulasi apt-get -s upgrade menandai tiap paket
# dengan satu baris "Inst <nama> [versi lama] (versi baru)".
PAKET_DAFTAR=$(awk '/^Inst / {
        printf "%s%s", pisah, $2
        pisah = ", "
    }' "$DATA_DIR/paket.txt")
PAKET_N=$(awk '/^Inst / { n++ } END { print n + 0 }' "$DATA_DIR/paket.txt")

# Service gagal: snapshot berformat "unit substate" per baris.
SVC_DAFTAR=$(awk '$2 == "failed" {
        printf "%s%s", pisah, $1
        pisah = ", "
    }' "$DATA_DIR/services.txt")
SVC_N=$(awk '$2 == "failed" { n++ } END { print n + 0 }' "$DATA_DIR/services.txt")

# --- 5. Pembersihan (hanya di dalam whitelist) -----------------------------
# Hasil pembersihan terkumpul di BAG_BERSIH; penghitung JUMLAH_FILE dan
# HEMAT_BYTES dipakai seksi HEMAT RUANG di laporan.
JUMLAH_FILE=0
HEMAT_BYTES=0
BAG_BERSIH=""

# bersihkan <judul> <dirs> <umur-hari> <pola-nama>:
# kumpulkan file di folder whitelist yang lebih tua dari batas umur,
# catat ukurannya untuk laporan, dan hapus hanya bila mode apply.
# -type f menjaga folder tetap utuh; find hanya berangkat dari folder
# whitelist, jadi file di luar daftar tidak pernah tersentuh.
bersihkan() {
    judul="$1"
    dirs="$2"
    umur="$3"
    pola="${4:-*}"
    batas=$(date -d "$HARI_INI -$umur days" +%F)
    if [ "$pola" = "*" ]; then
        label="file"
    else
        label="file $pola"
    fi
    terpakai=0

    if [ -n "$BAG_BERSIH" ]; then
        BAG_BERSIH+=$'\n'
    fi
    BAG_BERSIH+="$judul — $label lebih tua dari $batas (whitelist: $dirs)"$'\n'

    # shellcheck disable=SC2086  # whitelist memang dipisah spasi
    for dir in $dirs; do
        if [ ! -d "$dir" ]; then
            printf 'pemeliharaan.sh: folder whitelist %s tidak ditemukan\n' "$dir" >&2
            exit 3
        fi
        # Process substitution < <(...) menjaga penghitung tetap hidup
        # di shell utama; pipeline biasa akan menjalankan while di
        # subshell sehingga JUMLAH_FILE hilang.
        while IFS= read -r f; do
            [ -n "$f" ] || continue
            ukuran=$(stat -c %s "$f")
            JUMLAH_FILE=$((JUMLAH_FILE + 1))
            HEMAT_BYTES=$((HEMAT_BYTES + ukuran))
            terpakai=$((terpakai + 1))
            if [ "$MODE" = "apply" ]; then
                rm -f -- "$f"
                BAG_BERSIH+="   [terhapus] $f (${ukuran} byte)"$'\n'
            else
                BAG_BERSIH+="   [dry-run] $f (${ukuran} byte)"$'\n'
            fi
        done < <(find "$dir" -type f -name "$pola" ! -newermt "$batas" | sort)
    done

    if [ "$terpakai" -eq 0 ]; then
        BAG_BERSIH+="   (tidak ada kandidat)"$'\n'
    fi
}

if [ -n "${CACHE_DIRS:-}" ]; then
    bersihkan "cache" "$CACHE_DIRS" "$CACHE_MAX_DAYS"
fi
if [ -n "${LOG_DIRS:-}" ]; then
    bersihkan "log" "$LOG_DIRS" "$LOG_MAX_DAYS" "*.gz"
fi
if [ -n "${TMP_DIRS:-}" ]; then
    bersihkan "tmp" "$TMP_DIRS" "$TMP_MAX_DAYS"
fi

# --- 6. Susun laporan -----------------------------------------------------
if [ "$MODE" = "apply" ]; then
    LABEL_MODE="APPLY (pembersihan benar-benar dijalankan)"
    LABEL_HEMAT="benar-benar dibebaskan dari disk"
else
    LABEL_MODE="DRY-RUN (tidak ada file yang dihapus)"
    LABEL_HEMAT="potensi hemat; jalankan --apply untuk benar-benar menghapus"
fi

LAPORAN=""
tambah() {
    LAPORAN+="$1"
    LAPORAN+=$'\n'
}

tambah "================================================================"
tambah "LAPORAN PEMELIHARAAN SISTEM"
tambah "Tanggal acuan : $HARI_INI"
tambah "Mode          : $LABEL_MODE"
tambah "================================================================"
tambah ""
tambah "PEMERIKSAAN"
tambah "----------------------------------------------------------------"
tambah "1. Pemakaian disk (ambang $DISK_WARN_PCT%)"
tambah "$DISK_BARIS"
tambah "2. Paket update tertunda"
if [ "$PAKET_N" -gt 0 ]; then
    tambah "   $PAKET_N paket siap di-upgrade: $PAKET_DAFTAR"
else
    tambah "   tidak ada paket tertunda"
fi
tambah "3. Service gagal"
if [ "$SVC_N" -gt 0 ]; then
    tambah "   $SVC_N service failed: $SVC_DAFTAR"
else
    tambah "   semua service berjalan normal"
fi
tambah ""
tambah "PEMBERSIHAN"
tambah "----------------------------------------------------------------"
tambah "${BAG_BERSIH%$'\n'}"
tambah ""
tambah "HEMAT RUANG"
tambah "----------------------------------------------------------------"
tambah "Total: $JUMLAH_FILE file, $HEMAT_BYTES byte ($LABEL_HEMAT)"
tambah ""
tambah "REKOMENDASI"
tambah "----------------------------------------------------------------"
nomor=1
if [ "$DISK_N" -gt 0 ]; then
    tambah "$nomor. Pemakaian disk melewati ambang $DISK_WARN_PCT% di $DISK_MELEBIHI:"
    tambah "   cari penyumbang terbesar dengan du -h, lalu bersihkan atau pindahkan."
    nomor=$((nomor + 1))
fi
if [ "$PAKET_N" -gt 0 ]; then
    tambah "$nomor. Ada $PAKET_N paket update tertunda:"
    tambah "   jalankan apt upgrade pada jadwal maintenance berikutnya."
    nomor=$((nomor + 1))
fi
if [ "$SVC_N" -gt 0 ]; then
    tambah "$nomor. Ada $SVC_N service gagal ($SVC_DAFTAR):"
    tambah "   periksa journalctl -u <unit> untuk penyebabnya, lalu restart bila aman."
    nomor=$((nomor + 1))
fi
if [ "$MODE" = "dry-run" ] && [ "$JUMLAH_FILE" -gt 0 ]; then
    tambah "$nomor. DRY-RUN menemukan $JUMLAH_FILE file ($HEMAT_BYTES byte) layak dibersihkan:"
    tambah "   periksa daftar di bagian PEMBERSIHAN, lalu jalankan ulang dengan --apply."
    nomor=$((nomor + 1))
fi
if [ "$nomor" -eq 1 ]; then
    tambah "1. Tidak ada temuan: sistem dalam kondisi baik. Ulangi pemeliharaan sesuai jadwal."
fi

# --- 7. Tulis laporan ke stdout dan berkas ---------------------------------
REPORT_FILE="${REPORT_FILE:-laporan-pemeliharaan.txt}"
mkdir -p "$(dirname "$REPORT_FILE")"
printf '%s' "$LAPORAN" | tee "$REPORT_FILE"
exit 0
