#!/usr/bin/env bash
# Regression test for dashcaddy-update.sh's backup_update_state() function.
# Run from the dashcaddy-api/scripts/ directory:
#   bash test-dashcaddy-update-backup.sh
# Exit 0 = all assertions pass, non-zero = failure.

set -euo pipefail

readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly UPDATE_SCRIPT="${SCRIPT_DIR}/dashcaddy-update.sh"

# Resolve the sibling copy if the local one is missing (the file lives at
# /root/dashcaddy/scripts/dashcaddy-update.sh AND dashcaddy-api/scripts/dashcaddy-update.sh
# and they're kept identical via `cp` during sprint work).
if [[ ! -f "$UPDATE_SCRIPT" ]]; then
  UPDATE_SCRIPT="$(cd "${SCRIPT_DIR}/../../scripts" && pwd)/dashcaddy-update.sh"
fi

if [[ ! -f "$UPDATE_SCRIPT" ]]; then
  echo "FAIL: dashcaddy-update.sh not found at $UPDATE_SCRIPT"
  exit 1
fi

# ── Test harness ──────────────────────────────────────────────────────────────
pass=0
fail=0

assert_eq() {
  local desc="$1" expected="$2" actual="$3"
  if [[ "$expected" == "$actual" ]]; then
    echo "  PASS: $desc"
    pass=$(( pass + 1 ))
  else
    echo "  FAIL: $desc — expected '$expected', got '$actual'"
    fail=$(( fail + 1 ))
  fi
}

assert_file_contains() {
  local desc="$1" file="$2" needle="$3"
  if grep -q "$needle" "$file"; then
    echo "  PASS: $desc"
    pass=$(( pass + 1 ))
  else
    echo "  FAIL: $desc — '$needle' not in $file"
    fail=$(( fail + 1 ))
  fi
}

# Extract backup_update_state() function body from the real script
EXTRACTED=$(awk '/^backup_update_state\(\) \{/,/^\}$/' "$UPDATE_SCRIPT")

if [[ -z "$EXTRACTED" ]]; then
  echo "FAIL: backup_update_state() function not found in $UPDATE_SCRIPT"
  exit 1
fi

# ── Test 1: Both files present — function copies both ─────────────────────────
echo "=== Test 1: both trigger.json.processing + result.json present ==="
TMP=$(mktemp -d)
mkdir -p "$TMP/updates"
cat > "$TMP/trigger.json.processing" << 'EOF'
{"action":"update","version":"1.14.10","fromVersion":"1.14.8","channel":"stable","commit":"abc1234","stagingDir":"/opt/dashcaddy/updates/staging/dashcaddy-api","apiSourceDir":"/opt/dashcaddy/dashcaddy-api"}
EOF
cat > "$TMP/result.json" << 'EOF'
{"success":false,"version":"1.14.8","error":"Docker build failed","timestamp":"2026-07-10T12:34:56Z"}
EOF

bash -c "
  set -euo pipefail
  TRIGGER_FILE='$TMP/trigger.json'
  RESULT_FILE='$TMP/result.json'
  TRIGGER_PROCESSING=\"\${TRIGGER_FILE}.processing\"
  UPDATE_STATE_BACKUP_PREFIX='update-state'
  log() { :; }
  $EXTRACTED
  backup_update_state '$TMP/backup/v1.14.8'
"

assert_eq "backup dir created" "1" "$(find "$TMP/backup" -mindepth 1 -maxdepth 1 -type d | wc -l)"
assert_eq "update-state subdir exists" "1" "$(test -d "$TMP/backup/v1.14.8/update-state" && echo 1 || echo 0)"
assert_eq "trigger backup exists" "1" "$(test -f "$TMP/backup/v1.14.8/update-state/trigger.json.processing" && echo 1 || echo 0)"
assert_eq "result backup exists" "1" "$(test -f "$TMP/backup/v1.14.8/update-state/result.json" && echo 1 || echo 0)"
assert_file_contains "trigger content preserved" "$TMP/backup/v1.14.8/update-state/trigger.json.processing" '"fromVersion":"1.14.8"'
assert_file_contains "result content preserved" "$TMP/backup/v1.14.8/update-state/result.json" '"Docker build failed"'
assert_eq "trigger content byte-identical" "$(wc -c < "$TMP/trigger.json.processing")" "$(wc -c < "$TMP/backup/v1.14.8/update-state/trigger.json.processing")"
assert_eq "result content byte-identical" "$(wc -c < "$TMP/result.json")" "$(wc -c < "$TMP/backup/v1.14.8/update-state/result.json")"
rm -rf "$TMP"

# ── Test 2: Only result.json exists — function copies only that ──────────────
echo "=== Test 2: only result.json present ==="
TMP=$(mktemp -d)
mkdir -p "$TMP/updates"
echo '{"success":true,"version":"1.14.7","duration":12}' > "$TMP/result.json"

bash -c "
  set -euo pipefail
  TRIGGER_FILE='$TMP/trigger.json'
  RESULT_FILE='$TMP/result.json'
  TRIGGER_PROCESSING=\"\${TRIGGER_FILE}.processing\"
  UPDATE_STATE_BACKUP_PREFIX='update-state'
  log() { :; }
  $EXTRACTED
  backup_update_state '$TMP/backup/v1.14.7'
"

assert_eq "trigger backup absent" "0" "$(test -f "$TMP/backup/v1.14.7/update-state/trigger.json.processing" 2>/dev/null && echo 1 || echo 0)"
assert_eq "result backup exists" "1" "$(test -f "$TMP/backup/v1.14.7/update-state/result.json" && echo 1 || echo 0)"
rm -rf "$TMP"

# ── Test 3: No state files at all — function is a no-op (cleans up empty dir) ─
echo "=== Test 3: no state files present ==="
TMP=$(mktemp -d)
mkdir -p "$TMP/updates"

bash -c "
  set -euo pipefail
  TRIGGER_FILE='$TMP/trigger.json'
  RESULT_FILE='$TMP/result.json'
  TRIGGER_PROCESSING=\"\${TRIGGER_FILE}.processing\"
  UPDATE_STATE_BACKUP_PREFIX='update-state'
  log() { :; }
  $EXTRACTED
  backup_update_state '$TMP/backup/v1.14.6'
"

assert_eq "no update-state dir when nothing to back up" "0" "$(test -d "$TMP/backup/v1.14.6/update-state" 2>/dev/null && echo 1 || echo 0)"
rm -rf "$TMP"

# ── Test 4: Idempotency — running twice doesn't fail or accumulate ──────────
echo "=== Test 4: idempotency (run twice, no error, same single backup) ==="
TMP=$(mktemp -d)
mkdir -p "$TMP/updates"
echo '{"action":"update","version":"1.14.10","fromVersion":"1.14.8"}' > "$TMP/trigger.json.processing"

bash -c "
  set -euo pipefail
  TRIGGER_FILE='$TMP/trigger.json'
  RESULT_FILE='$TMP/result.json'
  TRIGGER_PROCESSING=\"\${TRIGGER_FILE}.processing\"
  UPDATE_STATE_BACKUP_PREFIX='update-state'
  log() { :; }
  $EXTRACTED
  backup_update_state '$TMP/backup/v1.14.8'
  backup_update_state '$TMP/backup/v1.14.8'
" 2>&1 | grep -E "(ERROR|FAIL)" || true

assert_eq "only one trigger backup after 2 runs" "1" "$(find "$TMP/backup/v1.14.8/update-state" -name "trigger.json.processing" 2>/dev/null | wc -l)"
rm -rf "$TMP"

# ── Test 5: main() calls backup_update_state in the right spot ──────────────
echo "=== Test 5: main() flow — backup_update_state called after backup_data_dir ==="
if grep -q 'backup_update_state "\$backup_dir"' "$UPDATE_SCRIPT"; then
  echo "  PASS: backup_update_state invoked from main()"
  pass=$(( pass + 1 ))
else
  echo "  FAIL: backup_update_state not invoked from main()"
  fail=$(( fail + 1 ))
fi

# Verify ordering: backup_update_state must come AFTER backup_data_dir in main()
DATA_LINE=$(grep -n 'backup_data_dir "\$backup_dir"' "$UPDATE_SCRIPT" | head -1 | cut -d: -f1)
STATE_LINE=$(grep -n 'backup_update_state "\$backup_dir"' "$UPDATE_SCRIPT" | head -1 | cut -d: -f1)
if (( STATE_LINE > DATA_LINE )); then
  echo "  PASS: backup_update_state (line $STATE_LINE) called AFTER backup_data_dir (line $DATA_LINE)"
  pass=$(( pass + 1 ))
else
  echo "  FAIL: backup_update_state (line $STATE_LINE) is NOT after backup_data_dir (line $DATA_LINE)"
  fail=$(( fail + 1 ))
fi

# ── Summary ──────────────────────────────────────────────────────────────────
echo
echo "═══════════════════════════════════════════"
echo "  backup_update_state() regression test"
echo "  PASS: $pass    FAIL: $fail"
echo "═══════════════════════════════════════════"

if (( fail > 0 )); then
  exit 1
fi
echo "All tests passed."
