#!/usr/bin/env bash
# yesterday -- map a file path to latest snapshot
#
# Inspired by the Plan 9 'yesterday' command, and by AFS 'vos backup' (where
# it's common practice to link the backup snapshots into home directories).
#
# https://9fans.github.io/plan9port/man/man1/yesterday.html
# https://computing.help.inf.ed.ac.uk/yesterday

. lib.bash || exit

usage() {
	echo "Usage: ${0##*/} [-acdl] <path>"
	echo ""
	echo_opt "-c" "copy file from latest snapshot"
	echo_opt "-d" "diff against latest snapshot"
	echo ""
	echo_opt "-a" "show all unique versions"
	echo_opt "-l" "long listing"
}

echo_l() {
	if (( opt_l )); then
		ls -dUlogh "$@"
	elif [[ -t 1 ]]; then
		ls -dU1 "$@"
	else
		printf '%s\n' "$@"
	fi
}

opt_a=0
opt_c=0
opt_d=0
opt_l=0

while getopts :acdl OPT; do
	case $OPT in
	a) opt_a=1;;
	c) opt_c=1;; # copy over
	d) opt_d=1;; # diff
	l) opt_l=1;;
	*) lib:die_getopts;;
	esac
done; shift $((OPTIND-1))

if (( opt_l + opt_c + opt_d > 1 )); then
	vdie "only one option out of -l, -c, -d can be used"
fi
if (( opt_l )); then
	opt_a=1
fi
if (( opt_a && (opt_c + opt_d) )); then
	vdie "options -c or -d cannot be used with multiple files"
fi

if (( ! $# )); then
	vdie "no path specified"
fi

for path; do
	# Determine the snapshot root (location of the '.old' directory),
	# and the path of the specified file relative to said root.
	abspath=$(realpath "$path")
	if [[ -d $HOME/.old && "$abspath/" == "$HOME/"* ]]; then
		base=$HOME
	elif [[ -d /.old ]]; then
		base=/
	else
		vdie "no snapshots on $HOSTNAME"
	fi
	base=${base%/}
	relpath=$(realpath --relative-base="${base:-/}" "$path")
	yespath=$base/.old/latest/$relpath

	if (( opt_a )); then
		shopt -s nullglob
		paths=("$base"/.old/*/????-??-??/"$relpath")
		shopt -u nullglob
		if (( ! ${#paths[@]} )); then
			vdie "no snapshots for '$base/$relpath'"
		fi
		# Sort by date, then filter duplicates by mtime
		{
			nf=$(echo "/$base///" | tr -dc / | wc -c)
			printf '%s\n' "${paths[@]}" | sort -t/ -k$nf
			realpath "$path"
		} | {
			opaths=()
			lastm=
			while IFS= read -r opath; do
				mtime=$(stat -c %Y "$opath")
				if [[ "$mtime" != "$lastm" ]]; then
					opaths+=("$opath")
					lastm=$mtime
				fi
			done
			echo_l "${opaths[@]}"
		}
	elif (( opt_c )); then
		cp -avib "$yespath" "$path"
	elif (( opt_d )); then
		diff --color -u "$yespath" "$abspath"
	else
		if [[ ! -e "$yespath" ]]; then
			vdie "no snapshots for '$base/$relpath'"
		fi
		echo_l "$(realpath "$yespath")"
	fi
done
