#!/usr/bin/env bash
# diff_collect.sh — Helper module for collecting git changes and untracked files (B-7)
# Single source of truth for collecting review diffs.

MAM_DIFF_UNAVAILABLE_MARKER="!!! CHANGE SET UNAVAILABLE !!!"
MAM_DIFF_TRUNCATED_MARKER="!!! DIFF TRUNCATED !!!"
MAM_DIFF_NESTED_MARKER="!!! NOT EXPANDED IN THIS DIFF !!!"

MAM_DIFF_MAX_BYTES="${MAM_DIFF_MAX_BYTES:-200000}"
MAM_DIFF_MAX_LINES="${MAM_DIFF_MAX_LINES:-4000}"

_mam_nested_dir_diff() {
  local d="$1" outer_top="$2" inner_top=""
  inner_top=$(git -C "$d" rev-parse --show-toplevel 2>/dev/null || printf '')

  if [ -n "$inner_top" ] && [ "$inner_top" != "$outer_top" ]; then
    printf '%s %s\n' "$MAM_DIFF_NESTED_MARKER" "$d"
    printf 'Untracked NESTED GIT REPOSITORY. Its contents are listed through its own\n'
    printf 'git, so its .gitignore applies; the outer diff cannot describe it.\n\n'
    git -C "$d" diff 2>/dev/null || true
    git -C "$d" ls-files -o --exclude-standard -z 2>/dev/null | while IFS= read -r -d '' s; do
      [ -n "$s" ] || continue
      git -C "$d" diff --no-index -- /dev/null "$s" 2>/dev/null || true
    done
    printf '\n'
    return 0
  fi

  printf '%s %s (untracked directory)\n' "$MAM_DIFF_NESTED_MARKER" "$d"
  git ls-files -o --exclude-standard -z -- "$d" 2>/dev/null | while IFS= read -r -d '' s; do
    [ -n "$s" ] || continue
    [ -f "$s" ] && [ ! -L "$s" ] || continue
    git diff --no-index -- /dev/null "$s" 2>/dev/null || true
  done
}

_mam_untracked_diff() {
  local outer_top
  outer_top=$(git rev-parse --show-toplevel 2>/dev/null || printf '%s' "$PWD")
  git ls-files -o --exclude-standard -z 2>/dev/null | while IFS= read -r -d '' f; do
    [ -n "$f" ] || continue
    if [ -L "$f" ]; then
      printf '%s %s -> %s (symlink, not expanded)\n' \
        "$MAM_DIFF_NESTED_MARKER" "$f" "$(readlink "$f" 2>/dev/null)"
    elif [ -d "$f" ]; then
      _mam_nested_dir_diff "$f" "$outer_top"
    elif [ -f "$f" ]; then
      git diff --no-index -- /dev/null "$f" 2>/dev/null || true
    else
      printf '%s %s (unreadable entry, not shown)\n' "$MAM_DIFF_NESTED_MARKER" "$f"
    fi
  done
}

mam_collect_changes_diff() {
  local repo_root="${1:-}"
  local base_commit="${2:-}"

  if [ -z "$repo_root" ]; then
    printf '%s\nreason: repo_root parameter is required\n' "$MAM_DIFF_UNAVAILABLE_MARKER"
    return 2
  fi

  if [ ! -d "$repo_root" ]; then
    printf '%s\nreason: repo_root directory %s does not exist\n' "$MAM_DIFF_UNAVAILABLE_MARKER" "$repo_root"
    return 2
  fi

  local raw_diff="" rc=0
  raw_diff=$(
    cd -P "$repo_root" 2>/dev/null || exit 3
    git rev-parse --git-dir >/dev/null 2>&1 || exit 4
    if [ -n "$base_commit" ] && git cat-file -e "${base_commit}^{commit}" 2>/dev/null; then
      git diff "$base_commit" || exit 5
    else
      git diff || exit 5
    fi
    _mam_untracked_diff
  ) || rc=$?

  if [ "$rc" -ne 0 ]; then
    local reason="not a git repository or git command failed (exit code $rc)"
    if [ "$rc" -eq 3 ]; then
      reason="cannot cd into repo_root $repo_root"
    elif [ "$rc" -eq 4 ]; then
      reason="not a git repository: $repo_root"
    elif [ "$rc" -eq 5 ]; then
      reason="git diff command failed"
    fi
    printf '%s\nreason: %s\n' "$MAM_DIFF_UNAVAILABLE_MARKER" "$reason"
    return 2
  fi

  if [ -z "$(printf '%s' "$raw_diff" | tr -d '[:space:]')" ]; then
    printf '(no changes since base commit)\n'
    return 0
  fi

  local byte_cnt line_cnt
  byte_cnt=$(printf '%s' "$raw_diff" | wc -c | tr -d '[:space:]')
  line_cnt=$(printf '%s' "$raw_diff" | wc -l | tr -d '[:space:]')

  if [ "$byte_cnt" -gt "$MAM_DIFF_MAX_BYTES" ] || [ "$line_cnt" -gt "$MAM_DIFF_MAX_LINES" ]; then
    local stat_summary=""
    stat_summary=$(
      cd -P "$repo_root" 2>/dev/null || exit 0
      if [ -n "$base_commit" ] && git cat-file -e "${base_commit}^{commit}" 2>/dev/null; then
        git diff "$base_commit" --stat 2>/dev/null || true
      else
        git diff --stat 2>/dev/null || true
      fi
      printf '\nUntracked files:\n'
      git ls-files -o --exclude-standard 2>/dev/null || true
    )

    printf '%s\n' "$MAM_DIFF_TRUNCATED_MARKER"
    printf 'The change set is %s bytes / %s lines, over the review limit (%s bytes / %s lines).\n' \
      "$byte_cnt" "$line_cnt" "$MAM_DIFF_MAX_BYTES" "$MAM_DIFF_MAX_LINES"
    printf 'Only the file-level summary is shown below. You have NOT been shown the\n'
    printf 'full change set -- inspect the working tree directly before voting.\n\n'
    printf '%s\n\n' "$stat_summary"
    printf '%s\n' "$MAM_DIFF_TRUNCATED_MARKER"
    return 0
  fi

  printf '%s\n' "$raw_diff"
  return 0
}

if [ "${BASH_SOURCE[0]}" = "$0" ]; then
  mam_collect_changes_diff "$@"
fi
