#!/bin/sh
set -eu

core_langs=debian/core-langs.txt
languages_toml=${1:-languages.toml}
vendor=vendor
workdir="$(pwd)/$vendor/.work"
mrconfig="$vendor/.mrconfig"
tab=$(printf '\t')
max_retries=${MAX_RETRIES:-3}
mr_jobs=${MR_JOBS:-8}
upstream_branch=${UPSTREAM_BRANCH:-upstream/latest}
sync_upstream_branch=${SYNC_UPSTREAM_BRANCH:-1}

cleanup() {
	rm -rf "$workdir" "$mrconfig"
}

retry() {
	desc=$1
	shift
	attempt=1
	while [ "$attempt" -le "$max_retries" ]; do
		if "$@"; then
			return 0
		fi
		if [ "$attempt" -eq "$max_retries" ]; then
			printf >&2 '%s failed after %s attempts\n' "$desc" "$max_retries"
			return 1
		fi
		attempt=$((attempt + 1))
		printf >&2 '%s failed; retrying (%s/%s)\n' "$desc" "$attempt" "$max_retries"
		sleep "$attempt"
	done
}

extract_grammars() {
	awk -v core_file="$core_langs" '
		BEGIN {
			while ((getline lang < core_file) > 0) {
				n = split(lang, langs, /[[:space:]]+/)
				for (i = 1; i <= n; i++) {
					if (langs[i] != "") core[langs[i]] = 1
				}
			}
			close(core_file)
		}
		/^\[\[grammar\]\]/ { name = ""; next }
		/^name = "/ {
			name = $0
			sub(/^name = "/, "", name)
			sub(/".*$/, "", name)
			next
		}
		name != "" && /^source = / && /git = "/ && /rev = "/ {
			repo = rev = $0
			sub(/^.*git = "/, "", repo)
			sub(/".*$/, "", repo)
			sub(/^.*rev = "/, "", rev)
			sub(/".*$/, "", rev)
			set = (name in core) ? "highlight-core" : "highlight-extra"
			print set "\t" name "\t" repo "\t" rev
		}
	' "$languages_toml"
}

write_mrconfig() {
	rm -rf "$workdir"
	mkdir -p "$workdir"
	{
		printf '[DEFAULT]\n'
		printf 'lib=clone () { git clone --quiet --filter=blob:none --depth=1 --no-checkout "$1" "$2"; git -C "$2" sparse-checkout set --no-cone "/*" "!/bindings/swift/"; git -C "$2" checkout --quiet; }\n\n'
		extract_grammars | while IFS="$tab" read -r set name repo rev; do
			printf '[.work/%s]\n' "$name"
			printf 'checkout = clone %s %s\n\n' "$repo" "$name"
		done
	} > "$mrconfig"
}

copy_clean_tree() {
	set=$1
	name=$2
	src="$workdir/$name"
	dest="$vendor/$set/$name"

	rm -rf "$dest"
	mkdir -p "$dest"
	cp -a "$src/." "$dest/"
	find "$dest" -depth \( \
		-path '*/.git/*' -o -name .git -o \
		-path '*/.github/*' -o -name .github -o \
		-path '*/target/*' -o -name target -o \
		-name .gitignore -o -name '*.wasm' -o -name '*.png' -o \
		-name '*.jpg' -o -name '*.jpeg' -o -name '*.gif' -o \
		-name '*.pdf' -o -name '*.lockb' -o -name '*.node' -o \
		-name '*.dylib' -o -name '*.jar' -o -name '*.so' -o \
		-name '*.dll' -o -name '*.exe' -o -name '*.ico' -o \
		-name '*.bak' -o \
		-name '*.gch' -o -name .DS_Store -o -name a.out \
	\) -delete
	find "$dest" -type l -lname '/*' -delete
	find "$dest" -xtype l -delete
}

sync_upstream_branch() {
	if [ "$sync_upstream_branch" = 0 ]; then
		return 0
	fi
	if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
		return 0
	fi

	find "$vendor" -name .DS_Store -o -name '*.bak' | xargs -r rm -f

	index=$(mktemp)
	rm -f "$index"
	trap 'rm -f "$index"; cleanup' EXIT HUP INT TERM
	GIT_INDEX_FILE=$index git read-tree --empty
	GIT_INDEX_FILE=$index git add -f "$vendor"
	tree=$(GIT_INDEX_FILE=$index git write-tree)

	if git rev-parse --verify -q "$upstream_branch" >/dev/null; then
		if [ "$(git rev-parse "$upstream_branch^{tree}")" = "$tree" ]; then
			printf '%s already matches current %s snapshot\n' "$upstream_branch" "$vendor"
			rm -f "$index"
			return 0
		fi
		commit=$(git commit-tree -p "$(git rev-parse "$upstream_branch")" \
			-m "Update grammar source snapshot" "$tree")
	else
		commit=$(git commit-tree -m "Update grammar source snapshot" "$tree")
	fi
	git update-ref "refs/heads/$upstream_branch" "$commit"
	printf 'Updated %s to %s\n' "$upstream_branch" "$commit"
	rm -f "$index"
}

mkdir -p "$vendor/highlight-core" "$vendor/highlight-extra"
write_mrconfig
# The generated config contains only clone commands derived from languages.toml;
# --trust-all avoids requiring maintainers to register this transient file in ~/.mrtrust.
retry "mr checkout" mr --trust-all -j"$mr_jobs" -c "$mrconfig" checkout

extract_grammars | while IFS="$tab" read -r set name repo rev; do
	retry "checkout $name@$rev" git -C "$workdir/$name" checkout --quiet --force "$rev"
done

extract_grammars | while IFS="$tab" read -r set name repo rev; do
	copy_clean_tree "$set" "$name"
done

cleanup
sync_upstream_branch
