# nomarchy-what-changed — plain-language "what changed last rebuild".
# Wraps nvd into a short summary (for toasts / menu) and optional full
# report. In-checkout state is not involved; this only reads Nix profiles.
#
# usage:
#   nomarchy-what-changed              # system + home (last two gens each)
#   nomarchy-what-changed system|home  # one layer
#   nomarchy-what-changed --summary …  # one line per layer (toast body)
#   nomarchy-what-changed --diff PATH1 PATH2
#   nomarchy-what-changed --summary --diff PATH1 PATH2
#
# NOMARCHY_NVD overrides the nvd binary (tests inject a fixture).

set -euo pipefail

nvd_bin="${NOMARCHY_NVD:-nvd}"
summary_only=0
mode="" # empty | system | home | diff
diff_a="" diff_b=""

usage() {
  echo "usage: nomarchy-what-changed [--summary] [system|home]" >&2
  echo "       nomarchy-what-changed [--summary] --diff <before> <after>" >&2
  exit 64
}

while [ $# -gt 0 ]; do
  case "$1" in
    --summary) summary_only=1; shift ;;
    --diff)
      mode="diff"
      shift
      [ $# -ge 2 ] || usage
      diff_a=$1; diff_b=$2; shift 2
      ;;
    system|home) mode="$1"; shift ;;
    -h|--help) usage ;;
    *) usage ;;
  esac
done

# One-line plain-language summary of an nvd --color never report.
# Prefer package row counts ([A.]/[R.]/[C.]); fall back to closure path delta.
summarize_report() {
  local label=$1 report=$2
  local added removed changed
  if printf '%s\n' "$report" | grep -q 'No version or selection state changes'; then
    printf '%s: no package changes\n' "$label"
    return
  fi
  # Count package rows (nvd tags: A added, R removed, C version/selection change).
  added=$(printf '%s\n' "$report" | grep -c '\[A\.' || true)
  removed=$(printf '%s\n' "$report" | grep -c '\[R\.' || true)
  changed=$(printf '%s\n' "$report" | grep -c '\[C\.' || true)
  # Strip trailing newline noise from grep -c on empty (already || true → 0).
  added=${added:-0}; removed=${removed:-0}; changed=${changed:-0}

  if [ "$added" -eq 0 ] && [ "$removed" -eq 0 ] && [ "$changed" -eq 0 ]; then
    # Closure-only deltas (sources, etc. without package names).
    local paths_a paths_r
    paths_a=$(printf '%s\n' "$report" \
      | sed -n 's/.*(\([0-9]*\) paths added,.*/\1/p' | head -1)
    paths_r=$(printf '%s\n' "$report" \
      | sed -n 's/.*paths added, \([0-9]*\) paths removed.*/\1/p' | head -1)
    paths_a=${paths_a:-0}; paths_r=${paths_r:-0}
    if [ "$paths_a" -eq 0 ] && [ "$paths_r" -eq 0 ]; then
      printf '%s: no package changes\n' "$label"
    else
      printf '%s: %s paths added, %s removed\n' "$label" "$paths_a" "$paths_r"
    fi
    return
  fi

  local parts=()
  [ "$added" -gt 0 ]   && parts+=("$added added")
  [ "$removed" -gt 0 ] && parts+=("$removed removed")
  [ "$changed" -gt 0 ] && parts+=("$changed updated")
  local joined
  joined=$(printf '%s, ' "${parts[@]}")
  joined=${joined%, }
  printf '%s: %s\n' "$label" "$joined"
}

run_diff() {
  local before=$1 after=$2 label=$3
  if [ ! -e "$before" ] || [ ! -e "$after" ]; then
    printf '%s: no previous generation to compare\n' "$label"
    return 0
  fi
  if [ "$(readlink -f "$before" 2>/dev/null || echo "$before")" \
     = "$(readlink -f "$after" 2>/dev/null || echo "$after")" ]; then
    printf '%s: no package changes\n' "$label"
    return 0
  fi
  local report
  report=$("$nvd_bin" --color never diff "$before" "$after" 2>/dev/null) || {
    printf '%s: could not diff (is nvd installed?)\n' "$label"
    return 0
  }
  if [ "$summary_only" -eq 1 ]; then
    summarize_report "$label" "$report"
  else
    printf '── %s ──\n' "$label"
    printf '%s\n' "$report"
    echo
    summarize_report "$label" "$report"
  fi
}

# Resolve last two links for a profile stem (…/system or …/home-manager).
last_two() {
  local stem=$1
  # shellcheck disable=SC2012
  ls -d "${stem}"-[0-9]*-link 2>/dev/null | sort -V | tail -n 2
}

system_pair() {
  last_two /nix/var/nix/profiles/system
}

home_pair() {
  local d
  d="${XDG_STATE_HOME:-$HOME/.local/state}/nix/profiles"
  if ls -d "$d"/home-manager-[0-9]*-link >/dev/null 2>&1; then
    last_two "$d/home-manager"
    return
  fi
  d="/nix/var/nix/profiles/per-user/${USER:-$(id -un)}"
  if ls -d "$d"/home-manager-[0-9]*-link >/dev/null 2>&1; then
    last_two "$d/home-manager"
    return
  fi
  return 0
}

pair_to_before_after() {
  # stdin: 0–2 paths; sets before/after (before may be empty).
  local -a links=()
  while IFS= read -r line; do
    [ -n "$line" ] && links+=("$line")
  done
  before=""; after=""
  if [ "${#links[@]}" -ge 2 ]; then
    before=$(readlink -f "${links[0]}")
    after=$(readlink -f "${links[1]}")
  elif [ "${#links[@]}" -eq 1 ]; then
    after=$(readlink -f "${links[0]}")
  fi
}

if [ "$mode" = diff ]; then
  run_diff "$diff_a" "$diff_b" "Diff"
  exit 0
fi

if [ -z "$mode" ] || [ "$mode" = system ]; then
  before=""; after=""
  pair_to_before_after < <(system_pair)
  if [ -n "$after" ] && [ -n "$before" ]; then
    run_diff "$before" "$after" "System"
  elif [ -n "$after" ]; then
    printf 'System: only one generation on disk — rebuild once more to compare\n'
  else
    printf 'System: no NixOS profile found\n'
  fi
fi

if [ -z "$mode" ] || [ "$mode" = home ]; then
  before=""; after=""
  pair_to_before_after < <(home_pair)
  if [ -n "$after" ] && [ -n "$before" ]; then
    run_diff "$before" "$after" "Desktop"
  elif [ -n "$after" ]; then
    printf 'Desktop: only one generation on disk — switch once more to compare\n'
  else
    printf 'Desktop: no Home Manager profile found\n'
  fi
fi
