#!/usr/bin/env bash
# run-tests.sh — test suite untuk paket lingkungan-development-linux.
#
# Suite menyiapkan folder kerja terisolasi (mktemp), menyalin skrip
# dari scripts/ dan dotfile dari config/, lalu menjalankan rangkaian
# pengujian dengan assertion sederhana:
#   - setup.sh --check melaporkan tanpa mengubah apa pun,
#   - setup.sh idempoten: dua eksekusi, keadaan akhir identik,
#   - dotfiles terpasang dengan isi identik + hook .bashrc sekali saja,
#   - backup .bak menyimpan versi lama sebelum ditimpa,
#   - tool inti hilang dilaporkan dengan keluar 3 (PATH tiruan),
#   - proyek-baru.sh membuat struktur + repo git dengan commit awal,
#   - proyek hasil scaffold bisa dibangun dan dijalankan (make run),
#   - penolakan: nama terpakai, tanpa nama, jenis tak dikenal.
#
# Jalankan dari mana saja: bash tests/run-tests.sh

set -u

SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
PROJ_DIR=$(dirname "$SCRIPT_DIR")

PASS=0
FAIL=0

# assert_eq <harapan> <nyata> <pesan>: bandingkan dua nilai.
assert_eq() {
    if [ "$1" = "$2" ]; then
        PASS=$((PASS + 1))
        printf 'OK    %s\n' "$3"
    else
        FAIL=$((FAIL + 1))
        printf 'GAGAL %s: harapan %s, nyata %s\n' "$3" "$1" "$2"
    fi
}

# --- folder kerja terisolasi + salin paket ----------------------------------
WORK=$(mktemp -d)
mkdir -p "$WORK/paket/scripts" "$WORK/paket/config"
cp "$PROJ_DIR/scripts/setup.sh" "$PROJ_DIR/scripts/proyek-baru.sh" \
    "$WORK/paket/scripts/"
cp -R "$PROJ_DIR/config/." "$WORK/paket/config/"
SETUP="$WORK/paket/scripts/setup.sh"
BARU="$WORK/paket/scripts/proyek-baru.sh"

RUMAH="$WORK/rumah"
mkdir -p "$RUMAH"

# potret: checksum seluruh berkas di $RUMAH — bahan uji idempotensi.
potret() {
    (cd "$RUMAH" && find . -type f -print | sort | xargs sha256sum)
}

# --- uji 1: --check melaporkan tanpa mengubah ------------------------------
DEV_HOME="$RUMAH" bash "$SETUP" --check > "$WORK/cek1.txt"
assert_eq 0 "$?" "setup --check keluar 0"
grep -q '^mode --check' "$WORK/cek1.txt"
assert_eq 0 "$?" "mode --check diumumkan di laporan"
grep -q '^\[OK\]     gcc' "$WORK/cek1.txt"
assert_eq 0 "$?" "gcc dilaporkan OK beserta versinya"
n_baru=$(grep -c '^\[BARU\]' "$WORK/cek1.txt")
assert_eq 4 "$n_baru" "--check melaporkan 4 item akan dipasang"
n_berkas=$(find "$RUMAH" -type f | wc -l)
assert_eq 0 "$n_berkas" "--check tidak membuat berkas apa pun"

# --- uji 2: pemasangan pertama ----------------------------------------------
DEV_HOME="$RUMAH" bash "$SETUP" > "$WORK/pasang1.txt"
assert_eq 0 "$?" "setup pertama keluar 0"
[ -f "$RUMAH/.gitconfig" ]
assert_eq 0 "$?" "gitconfig terpasang"
cmp -s "$WORK/paket/config/.gitconfig" "$RUMAH/.gitconfig"
assert_eq 0 "$?" "isi gitconfig identik dengan template"
[ -f "$RUMAH/.editorconfig" ]
assert_eq 0 "$?" "editorconfig terpasang"
[ -f "$RUMAH/.bashrc-dev" ]
assert_eq 0 "$?" "bashrc-dev terpasang"
grep -qF '. "$HOME/.bashrc-dev"' "$RUMAH/.bashrc"
assert_eq 0 "$?" "hook .bashrc memuat bashrc-dev"
n=$(grep -c '^\[PASANG\]' "$WORK/pasang1.txt")
assert_eq 4 "$n" "empat item terpasang pada eksekusi pertama"

# --- uji 3: idempoten — eksekusi ulang tidak mengubah keadaan -----------------
potret > "$WORK/state1.txt"
DEV_HOME="$RUMAH" bash "$SETUP" > "$WORK/pasang2.txt"
assert_eq 0 "$?" "setup kedua keluar 0"
potret > "$WORK/state2.txt"
diff "$WORK/state1.txt" "$WORK/state2.txt" > /dev/null 2>&1
assert_eq 0 "$?" "keadaan identik setelah eksekusi kedua"
n=$(grep -c 'sudah terpasang' "$WORK/pasang2.txt")
assert_eq 3 "$n" "eksekusi kedua melaporkan 3 dotfile sudah terpasang"
grep -q 'sudah memuat .bashrc-dev' "$WORK/pasang2.txt"
assert_eq 0 "$?" "hook .bashrc tidak ditulis dua kali"

# --- uji 4: --check pada keadaan terpasang tetap tidak mengubah ----------------
potret > "$WORK/state3.txt"
DEV_HOME="$RUMAH" bash "$SETUP" --check > "$WORK/cek2.txt"
assert_eq 0 "$?" "setup --check (sudah terpasang) keluar 0"
potret > "$WORK/state4.txt"
diff "$WORK/state3.txt" "$WORK/state4.txt" > /dev/null 2>&1
assert_eq 0 "$?" "--check tidak mengubah keadaan terpasang"
n=$(grep -c 'sudah terpasang' "$WORK/cek2.txt")
assert_eq 3 "$n" "--check melaporkan 3 dotfile sudah terpasang"

# --- uji 5: backup .bak menyimpan versi lama -----------------------------------
printf '[user]\n\tname = Budi Lama\n' > "$RUMAH/.gitconfig"
DEV_HOME="$RUMAH" bash "$SETUP" > "$WORK/pasang3.txt"
assert_eq 0 "$?" "setup ulang setelah perubahan keluar 0"
grep -q '^\[BAK\]' "$WORK/pasang3.txt"
assert_eq 0 "$?" "backup .bak dibuat sebelum menimpa"
grep -q 'name = Budi Lama' "$RUMAH/.gitconfig.bak"
assert_eq 0 "$?" "isi lama tersimpan di .gitconfig.bak"
cmp -s "$WORK/paket/config/.gitconfig" "$RUMAH/.gitconfig"
assert_eq 0 "$?" "gitconfig kembali identik dengan template"

# --- uji 6: tool inti hilang dilaporkan keluar 3 --------------------------------
# PATH tiruan tanpa gcc: skrip harus melaporkan [HILANG] lalu keluar 3.
FAKEBIN="$WORK/fakebin"
mkdir -p "$FAKEBIN"
for t in bash awk sed grep wc cmp cp dirname make git python3 curl; do
    ln -s "$(command -v "$t")" "$FAKEBIN/$t"
done
PATH="$FAKEBIN" DEV_HOME="$WORK/rumah-fake" bash "$SETUP" --check \
    > "$WORK/cek3.txt"
assert_eq 3 "$?" "tool inti hilang dilaporkan keluar 3"
grep -q '^\[HILANG\] gcc' "$WORK/cek3.txt"
assert_eq 0 "$?" "gcc hilang terlihat di laporan"

# --- uji 7: opsi salah ditolak ---------------------------------------------------
bash "$SETUP" --salah > /dev/null 2>&1
assert_eq 1 "$?" "opsi tidak dikenal keluar 1"

# --- uji 8: proyek-baru membuat struktur + repo git -------------------------------
cd "$WORK" || exit 1
GIT_AUTHOR_NAME="Budi Santoso" GIT_AUTHOR_EMAIL="budi@example.com" \
GIT_COMMITTER_NAME="Budi Santoso" GIT_COMMITTER_EMAIL="budi@example.com" \
GIT_AUTHOR_DATE="2026-01-15 09:00:00 +0000" \
GIT_COMMITTER_DATE="2026-01-15 09:00:00 +0000" \
    bash "$BARU" aplikasi-tes c > "$WORK/baru1.txt"
assert_eq 0 "$?" "proyek-baru (c) keluar 0"
for f in README.md .gitignore Makefile src/main.c; do
    [ -f "aplikasi-tes/$f" ]
    assert_eq 0 "$?" "$f dibuat"
done
[ -d aplikasi-tes/.git ]
assert_eq 0 "$?" "repo git dibuat"
cabang=$(git -C aplikasi-tes symbolic-ref --short HEAD)
assert_eq main "$cabang" "branch utama bernama main"
n_commit=$(git -C aplikasi-tes log --oneline | wc -l)
assert_eq 1 "$n_commit" "satu commit awal dibuat"
n_kotor=$(git -C aplikasi-tes status --porcelain | wc -l)
assert_eq 0 "$n_kotor" "git status bersih setelah commit awal"

# --- uji 9: proyek hasil scaffold benar-benar bisa dijalankan ----------------------
make -s -C aplikasi-tes run > "$WORK/run-c.txt"
assert_eq 0 "$?" "make run (c) keluar 0"
grep -q 'halo, dunia' "$WORK/run-c.txt"
assert_eq 0 "$?" "program c menyapa halo, dunia"
n_kotor=$(git -C aplikasi-tes status --porcelain | wc -l)
assert_eq 0 "$n_kotor" "binary hasil build diabaikan .gitignore"

GIT_AUTHOR_NAME="Budi Santoso" GIT_AUTHOR_EMAIL="budi@example.com" \
GIT_COMMITTER_NAME="Budi Santoso" GIT_COMMITTER_EMAIL="budi@example.com" \
GIT_AUTHOR_DATE="2026-01-15 09:00:00 +0000" \
GIT_COMMITTER_DATE="2026-01-15 09:00:00 +0000" \
    bash "$BARU" web-tes python > "$WORK/baru2.txt"
assert_eq 0 "$?" "proyek-baru (python) keluar 0"
[ -f web-tes/src/main.py ]
assert_eq 0 "$?" "main.py dibuat"
make -s -C web-tes run > "$WORK/run-py.txt"
assert_eq 0 "$?" "make run (python) keluar 0"
grep -q 'halo, dunia' "$WORK/run-py.txt"
assert_eq 0 "$?" "program python menyapa halo, dunia"

# --- uji 10: penolakan --------------------------------------------------------------
bash "$BARU" aplikasi-tes c > /dev/null 2>&1
assert_eq 2 "$?" "nama yang sudah dipakai ditolak keluar 2"
bash "$BARU" > /dev/null 2>&1
assert_eq 1 "$?" "tanpa nama ditolak keluar 1"
bash "$BARU" tes-zig zig > /dev/null 2>&1
assert_eq 1 "$?" "jenis tidak dikenal ditolak keluar 1"

# --- ringkasan ------------------------------------------------------------------------
if [ "$FAIL" -eq 0 ]; then
    printf 'PASSED: %d/%d test lulus\n' "$PASS" "$PASS"
else
    printf 'FAILED: %d/%d test lulus\n' "$PASS" $((PASS + FAIL))
fi
cd / && rm -rf "$WORK"
[ "$FAIL" -eq 0 ]
