#!/usr/bin/env bash
set -euo pipefail

# Recursively scans a folder (including hidden files/folders), but only processes
# human-readable text files. It:
#   1) Lists files containing OLD value
#   2) Asks for confirmation
#   3) Creates .backup copies (AFTER confirmation)
#   4) Replaces OLD -> NEW in those files
#
# Usage:
#   ./replace_hash_text_only.sh /path/to/folder

OLD='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
NEW='yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'

if [[ $# -ne 1 ]]; then
  echo "Usage: $0 /path/to/folder"
  exit 1
fi

ROOT="$1"
if [[ ! -d "$ROOT" ]]; then
  echo "Error: '$ROOT' is not a directory."
  exit 1
fi

for cmd in find grep perl file cp; do
  command -v "$cmd" >/dev/null 2>&1 || {
    echo "Error: required command not found: $cmd"
    exit 1
  }
done

is_human_readable_text() {
  # file --mime output example:
  #   text/plain; charset=utf-8
  #   application/octet-stream; charset=binary
  local mime
  mime="$(file --mime -b -- "$1" 2>/dev/null || true)"
  [[ -n "$mime" ]] || return 1
  [[ "$mime" == *"charset=binary"* ]] && return 1
  return 0
}

declare -a MATCHED_FILES=()
declare -a MATCH_COUNTS=()

files_seen=0
text_files_scanned=0
non_text_skipped=0

while IFS= read -r -d '' file; do
  ((files_seen+=1))

  if ! is_human_readable_text "$file"; then
    ((non_text_skipped+=1))
    continue
  fi

  ((text_files_scanned+=1))

  if LC_ALL=C grep -qF -- "$OLD" "$file" 2>/dev/null; then
    count="$(LC_ALL=C grep -oF -- "$OLD" "$file" | wc -l | tr -d ' ')"
    MATCHED_FILES+=("$file")
    MATCH_COUNTS+=("$count")
  fi
done < <(find "$ROOT" -type f -print0)

matched_total="${#MATCHED_FILES[@]}"

echo "Scanned files (all types): $files_seen"
echo "Text files scanned:         $text_files_scanned"
echo "Non-text files skipped:     $non_text_skipped"
echo "Files with matches:         $matched_total"

if (( matched_total == 0 )); then
  echo "No matches found in text files. Nothing to do."
  exit 0
fi

echo
echo "Potential changes:"
for i in "${!MATCHED_FILES[@]}"; do
  idx=$((i + 1))
  printf "  %4d) %s  (occurrences: %s)\n" \
    "$idx" "${MATCHED_FILES[$i]}" "${MATCH_COUNTS[$i]}"
done

echo
read -r -p "Proceed? This will create .backup files, then apply replacements. [y/N]: " confirm
case "$confirm" in
  y|Y|yes|YES) ;;
  *)
    echo "Canceled. No backups created. No files modified."
    exit 0
    ;;
esac

# Safety check: do not overwrite existing backups
for file in "${MATCHED_FILES[@]}"; do
  if [[ -e "${file}.backup" ]]; then
    echo "Error: backup already exists: ${file}.backup"
    echo "Aborting without making changes."
    exit 1
  fi
done

echo
echo "Creating backups..."
for file in "${MATCHED_FILES[@]}"; do
  cp -p -- "$file" "${file}.backup"
  echo "  [backup] ${file}.backup"
done

echo
echo "Applying replacements..."
updated=0
total_replacements=0

for i in "${!MATCHED_FILES[@]}"; do
  file="${MATCHED_FILES[$i]}"
  count="${MATCH_COUNTS[$i]}"

  OLD="$OLD" NEW="$NEW" perl -i -pe 's/\Q$ENV{OLD}\E/$ENV{NEW}/g' "$file"
  ((updated+=1))
  ((total_replacements+=count))
  echo "  [updated] $file"
done

echo
echo "Done."
echo "Updated files:       $updated"
echo "Total replacements:  $total_replacements"
echo "Backups created as:  <file>.backup"

