#!/bin/sh
#
# git-issue-fsck - Validate issue data integrity
#
# Performance: Uses git cat-file --batch for bulk tree checks and
# single-pass trailer extraction to avoid per-commit subprocess forks.
#

set -e

. "$(dirname "$0")/git-issue-lib"

usage() {
	cat <<EOF
usage: git issue fsck [options]

Validates all issue refs for data integrity.

Checks:
   - Ref names match UUID format
   - All commits use the empty tree
   - Root commits have State: and Format-Version: trailers
   - Commit chain is well-formed (single root)

Options:
   --quiet    Only show errors (no OK messages)
   -h, --help Show this help
EOF
	exit 1
}

quiet=0

while test $# -gt 0
do
	case "$1" in
		--quiet)
			quiet=1
			shift
			;;
		-h|--help)
			usage
			;;
		*)
			echo "error: unknown option '$1'" >&2
			usage
			;;
	esac
done

# Verify we are inside a git repository
git rev-parse --git-dir >/dev/null 2>&1 || {
	echo "fatal: not a git repository" >&2
	exit 128
}

empty_tree="$(git hash-object -t tree /dev/null)"

# --- Optimized fsck ---
# For each issue: ONE git log call outputs commit metadata + trailers.
# Awk validates UUID format, tree hashes, root trailer presence, and chain integrity.
# Tree check uses %T format inline (no separate cat-file --batch needed).


refs_file="$(mktemp)"
trap 'rm -f "$refs_file"' EXIT

git for-each-ref --format='%(refname)' refs/issues/ > "$refs_file"

# If no issues exist
if ! test -s "$refs_file"
then
	printf 'No issues found.\n'
	exit 0
fi

# Process all issues through a single awk pipeline
result="$({
while IFS= read -r ref
do
	printf 'ISSUE\t%s\n' "$ref"
	git log --format='COMMIT%x09%T%x09%P%x09%at%n%(trailers)COMMIT_END' "$ref"
done < "$refs_file"
} | awk \
	-v empty_tree="$empty_tree" \
	-v quiet="$quiet" \
'
function emit() {
	if (ref == "") return
	checked++

	uuid = ref
	sub(/^refs\/issues\//, "", uuid)
	short_id = substr(uuid, 1, 7)
	issue_ok = 1

	# Check UUID format
	if (uuid !~ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/) {
		printf "error: %s: invalid UUID format: %s\n", short_id, uuid > "/dev/stderr"
		err_count++
		issue_ok = 0
	}

	# Check all commits use empty tree
	for (i = 1; i <= commit_n; i++) {
		if (trees[i] != empty_tree) {
			printf "error: %s: commit %s does not use empty tree\n", short_id, substr(shas[i], 1, 7) > "/dev/stderr"
			err_count++
			issue_ok = 0
		}
	}

	# Check single root (commits with no parents)
	if (root_count != 1) {
		printf "error: %s: expected 1 root commit, found %d\n", short_id, root_count > "/dev/stderr"
		err_count++
		issue_ok = 0
	}

	# Check root has State: and Format-Version: trailers
	if (!has_state) {
		printf "error: %s: root commit missing State: trailer\n", short_id > "/dev/stderr"
		err_count++
		issue_ok = 0
	}
	if (!has_fv) {
		printf "error: %s: root commit missing Format-Version: trailer\n", short_id > "/dev/stderr"
		err_count++
		issue_ok = 0
	}

	if (issue_ok && !quiet) printf "ok %s\n", short_id
}

BEGIN { err_count = 0; checked = 0 }

/^ISSUE\t/ {
	emit()
	ref = substr($0, 7)
	commit_n = 0; root_count = 0; in_root_trailers = 0
	has_state = 0; has_fv = 0
	delete trees; delete shas
	next
}

/^COMMIT\t/ {
	split($0, c, "\t")
	commit_n++
	trees[commit_n] = c[2]
	parents = c[3]
	# Root commit has no parents
	if (parents == "") {
		root_count++
		in_root_trailers = 1
	} else {
		in_root_trailers = 0
	}
	next
}

/^COMMIT_END/ { in_root_trailers = 0; next }

# Only check trailers on root commits
in_root_trailers && /^State: /          { has_state = 1 }
in_root_trailers && /^Format-Version: / { has_fv = 1 }

END {
	emit()
	if (checked == 0) {
		printf "No issues found.\n"
	} else if (err_count == 0) {
		printf "Checked %d issues, no errors found.\n", checked
	} else {
		printf "Checked %d issues, %d errors found.\n", checked, err_count > "/dev/stderr"
		print "EXIT_1"
	}
}
')"

# Check if awk signaled an error exit
case "$result" in
	*EXIT_1*)
		printf '%s\n' "$result" | grep -v '^EXIT_1$'
		exit 1
		;;
	*)
		printf '%s\n' "$result"
		;;
esac
