#!/bin/bash
set -u

ROOTS=(
  "/home2/vlv/public_html"
  "/home2/vlv/www"
)

BASE_DIR="/home2/vlv/public_html/jdfs.lv"
STATE_FILE="$BASE_DIR/file-watch-state.tsv"
NEW_STATE_FILE="$BASE_DIR/file-watch-state.new.tsv"
MAX_SECONDS=1200
START_TS=$(date +%s)

# Mapes, kuras pilnībā izlaižam.
# uploads NAV šajā sarakstā, jo tur var būt ļaunprātīgi .php faili.
SKIP_DIRS=(
  "cache"
  "caches"
  "tmp"
  "temp"
  "logs"
  "log"
  "sessions"
  "session"
  "vendor"
  "node_modules"
  ".git"
  ".svn"
  ".idea"
  ".vscode"
  "backup"
  "backups"
  "archive"
  "archives"
  "vqcache"
)

cleanup() {
  rm -f "$NEW_STATE_FILE"
}

trap cleanup EXIT

is_timed_out() {
  local now
  now=$(date +%s)
  (( now - START_TS >= MAX_SECONDS ))
}

# Nolasa veco stāvokli asociatīvajos masīvos.
declare -A OLD_HASH
declare -A OLD_SIZE
declare -A OLD_MTIME
declare -A OLD_CTIME
declare -A SEEN

if [[ -f "$STATE_FILE" ]]; then
  while IFS=$'\t' read -r hash size mtime ctime path; do
    [[ -z "${path:-}" ]] && continue
    OLD_HASH["$path"]="$hash"
    OLD_SIZE["$path"]="$size"
    OLD_MTIME["$path"]="$mtime"
    OLD_CTIME["$path"]="$ctime"
  done < "$STATE_FILE"
fi

: > "$NEW_STATE_FILE"

# Izveido find -prune izteiksmi.
PRUNE_EXPR=()
first=1
for d in "${SKIP_DIRS[@]}"; do
  if (( first )); then
    first=0
  else
    PRUNE_EXPR+=("-o")
  fi
  PRUNE_EXPR+=("-name" "$d")
done

scan_root() {
  local root="$1"

  [[ -d "$root" ]] || return 0

  while IFS= read -r -d '' path; do
    if is_timed_out; then
      echo "KĻŪDA: skenēšana pārsniedza ${MAX_SECONDS} sekundes. Stāvoklis netika pārrakstīts." >&2
      exit 2
    fi

    # stat vienā izsaukumā: size, mtime, ctime
    stat_out=$(stat -c '%s|%Y|%Z' -- "$path" 2>/dev/null) || continue

    size=${stat_out%%|*}
    rest=${stat_out#*|}
    mtime=${rest%%|*}
    ctime=${rest##*|}

    hash=""

    if [[ -n "${OLD_HASH[$path]+x}" \
       && "${OLD_SIZE[$path]}" == "$size" \
       && "${OLD_MTIME[$path]}" == "$mtime" \
       && "${OLD_CTIME[$path]}" == "$ctime" ]]; then
      hash="${OLD_HASH[$path]}"
    else
      hash=$(sha256sum -- "$path" 2>/dev/null | awk '{print $1}') || continue
      [[ -n "$hash" ]] || continue
    fi

    SEEN["$path"]=1

    printf '%s\t%s\t%s\t%s\t%s\n' \
      "$hash" "$size" "$mtime" "$ctime" "$path" >> "$NEW_STATE_FILE"

    if [[ -z "${OLD_HASH[$path]+x}" ]]; then
      echo "JAUNS AIZDOMĪGS FAILS: $path"
    elif [[ "${OLD_HASH[$path]}" != "$hash" ]]; then
      echo "MAINĪTS AIZDOMĪGS FAILS: $path"
    fi

  done < <(
    find "$root" \
      \( -type d \( "${PRUNE_EXPR[@]}" \) -prune \) -o \
      -type f \
      \( -iname '*.php' \
         -o -iname '*.phtml' \
         -o -iname '*.phar' \
         -o -iname '*.sh' \
         -o -name '.htaccess' \
         -o -name '.user.ini' \
      \) \
      -print0 2>/dev/null
  )
}

# Ja abi rooti ir dažādi, skenē abus.
# Ja tomēr norāda uz vienu vietu, otro izlaiž.
declare -A REAL_ROOTS

for root in "${ROOTS[@]}"; do
  real=$(readlink -f "$root" 2>/dev/null || true)
  [[ -n "$real" && -d "$real" ]] || continue

  if [[ -z "${REAL_ROOTS[$real]+x}" ]]; then
    REAL_ROOTS["$real"]=1
    scan_root "$real"
  fi
done

# Dzēsto failu noteikšana.
if [[ -f "$STATE_FILE" ]]; then
  for path in "${!OLD_HASH[@]}"; do
    if [[ -z "${SEEN[$path]+x}" ]]; then
      echo "IZDZĒSTS AIZDOMĪGS FAILS: $path"
    fi
  done
fi

# Atomiska stāvokļa faila nomaiņa tikai pēc veiksmīgas pilnas skenēšanas.
mv -f "$NEW_STATE_FILE" "$STATE_FILE"
trap - EXIT

exit 0
