#!/bin/bash
# =============================================================================
# machine-setup — writing files into somebody's home
# =============================================================================
#
# Definitions only, like the other lib/ files.
#
# ── The rule ──
#
# A setup script may create a config file. It may not silently replace one the
# user wrote. The original did the second: `cp .tmux.conf $USER_HOME/` on every
# run, over whatever was there, and five separate `cat >>` into .zshrc with no
# guard — so a second pass duplicated the starship init, the nvim PATH, bun, deno
# and the aliases.
#
# Both of those are the same mistake in different shapes: writing without looking
# first. The two helpers here are the two safe shapes.

[[ -n "${MACHINE_SETUP_FILES_LOADED:-}" ]] && return 0
MACHINE_SETUP_FILES_LOADED=1

# Put a config file in place, asking before it replaces one the user has.
#
# Three outcomes, and the caller can tell them apart by the return code:
#
#   0  installed — there was nothing there, or the user chose to replace
#   1  identical — already exactly this, nothing done
#   2  kept      — the user chose to keep theirs
#
# When the file exists and differs, this ASKS rather than deciding. Silently
# keeping theirs is safe but unhelpful — they never learn that a newer version
# exists — and silently replacing it is how a setup script eats somebody's
#configuration. So: keep, replace, or show the difference first, and a replaced file is
# always kept beside the new one.
#
# NOTE for callers: 1 and 2 are outcomes, not failures — but they are still
# non-zero, so calling this as a plain command under `set -e` ends the script
# before the result can be read. Always capture it:
#
#   install_config "$src" "$dest" "$user" && rc=0 || rc=$?
install_config() {
  local src="$1" dest="$2" owner="$3" answer

  if [[ ! -f "$dest" ]]; then
    install -D -m 0644 -o "$owner" -g "$(user_group "$owner")" "$src" "$dest"
    # Recorded here rather than at the call site: "which files did it write" is
    # the question a reviewer asks first, and a per-section report would drift
    # from what this function actually did.
    declare -F report_changed >/dev/null && report_changed "wrote ${dest} (0644, owner ${owner}) — did not exist"
    return 0
  fi

  if cmp -s "$src" "$dest"; then
    declare -F report_kept >/dev/null && report_kept "${dest} already identical to the shipped version — not touched"
    return 1
  fi

  echo ""
  warn "${dest} already exists here, and differs from the one this script ships."

  # Never replace a file the user has without being told to. An unattended run
  # answers "keep", because the alternative is destroying configuration nobody
  # was present to defend.
  if [[ "${ASSUME_YES:-}" == "1" ]] || [[ ! -t 0 ]]; then
    echo "     keeping yours (nothing was asked, so nothing is replaced)"
    declare -F report_kept >/dev/null && report_kept "${dest} differs from ours and was KEPT — unattended run, nothing replaced"
    return 2
  fi

  while true; do
    echo "     [1] keep yours — nothing changes"
    echo "     [2] use ours — yours is kept as ${dest}.before-machine-setup"
    echo "     [3] show me the difference first"
    echo ""
    if ! read -rp "  Which one? (1/2/3) [1]: " answer; then
      echo ""
      echo "     keeping yours"
      declare -F report_kept >/dev/null && report_kept "${dest} differs from ours and was KEPT — no answer available"
      return 2
    fi
    case "${answer:-1}" in
      1)
        echo "     keeping yours"
        declare -F report_kept >/dev/null && report_kept "${dest} differs from ours and was KEPT by choice"
        return 2
        ;;
      2)
        cp -a "$dest" "${dest}.before-machine-setup"
        install -D -m 0644 -o "$owner" -g "$(user_group "$owner")" "$src" "$dest"
        ok "replaced — yours is at ${dest}.before-machine-setup"
        declare -F report_changed >/dev/null && report_changed "REPLACED ${dest} by choice — previous kept at ${dest}.before-machine-setup"
        return 0
        ;;
      3)
        echo ""
        # yours on the left, ours on the right: - is what you would lose,
        # + is what you would gain.
        diff -u --label "yours: ${dest}" --label "ours: ${src}" "$dest" "$src" | page
        echo ""
        ;;
      *) warn "Pick 1, 2 or 3." ;;
    esac
  done
}

# Append a block to a file exactly once.
#
# The block is wrapped in markers naming what it is, so a second run recognises
# its own work instead of adding it again — and so a human reading the file can
# see which lines came from here and delete them as a unit.
#
#   append_once ~/.zshrc bun <<'EOF'
#   export PATH="$HOME/.bun/bin:$PATH"
#   EOF
#
# Returns 0 if it wrote, 1 if the block was already there.
#
# One limitation, and it bites the author rather than the user: RENAMING a marker
# orphans the block that used the old name. append_once only recognises the name
# it is given, so the previous block stays in the file doing whatever it did.
# Changing a block's CONTENT has the same shape — the marker is found, so the new
# content is never written. Both need the old block removed by hand.
append_once() {
  local file="$1" name="$2"
  local begin="# >>> machine-setup: ${name} >>>"
  local end="# <<< machine-setup: ${name} <<<"

  if [[ -f "$file" ]] && grep -qF "$begin" "$file"; then
    return 1
  fi

  {
    echo ""
    echo "$begin"
    cat
    echo "$end"
  } >>"$file"
}

# -----------------------------------------------------------------------------
# Where tmux actually reads its config
# -----------------------------------------------------------------------------
#
# tmux 3.1 added an XDG location and it takes PRECEDENCE. Verified on 3.4 by
# creating both and asking tmux which marker it ended up with:
#
#   both present            -> ~/.config/tmux/tmux.conf
#   only ~/.tmux.conf       -> ~/.tmux.conf
#   only the XDG one        -> the XDG one
#
# So installing to ~/.tmux.conf on a machine that has the XDG file writes a file
# tmux will never read, and the script would report success having changed
# nothing anybody can see. That is the failure this exists to prevent.
#
# Rules, in order:
#   1. an existing XDG config wins  -> that is their real config, target it
#   2. an existing ~/.tmux.conf     -> target it, since it is what tmux reads
#   3. neither                      -> ~/.tmux.conf, the path every guide names
tmux_config_target() {
  local home="$1"
  local xdg="${XDG_CONFIG_HOME:-$home/.config}/tmux/tmux.conf"
  if [[ -f "$xdg" ]]; then
    echo "$xdg"
  else
    echo "$home/.tmux.conf"
  fi
}

# True when a ~/.tmux.conf would be shadowed by an XDG config that already exists.
tmux_dot_conf_is_shadowed() {
  local home="$1"
  [[ -f "${XDG_CONFIG_HOME:-$home/.config}/tmux/tmux.conf" && -f "$home/.tmux.conf" ]]
}
