#!/bin/sh
#
# git-issue-search - Search issues by text pattern
#
# Performance: Uses a single git-log call per issue with awk matching
# to avoid per-issue subprocess overhead (was ~10 forks/issue, now ~1).
#

set -e

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

usage() {
	cat <<EOF
usage: git issue search <pattern> [options]

Search issue titles, bodies, and comments for a text pattern.

Options:
   -s, --state <state>   Filter by state (open, closed, all) [default: all]
   -i, --ignore-case     Case-insensitive search
   -h, --help            Show this help

The pattern is matched as a fixed string (not regex) by default.
EOF
	exit 1
}

pattern=""
filter_state="all"
ignore_case=0

while test $# -gt 0
do
	case "$1" in
		-s|--state)
			test $# -ge 2 || { echo "error: -s requires a value" >&2; exit 1; }
			filter_state="$2"
			shift 2
			;;
		-i|--ignore-case)
			ignore_case=1
			shift
			;;
		-h|--help)
			usage
			;;
		--)
			shift
			break
			;;
		-*)
			echo "error: unknown option '$1'" >&2
			usage
			;;
		*)
			if test -z "$pattern"
			then
				pattern="$1"
			fi
			shift
			;;
	esac
done

if test -z "$pattern"
then
	echo "error: search pattern is required" >&2
	usage
fi

# 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
}

# Check if any issues exist
if ! git for-each-ref --count=1 refs/issues/ >/dev/null 2>&1 ||
   test -z "$(git for-each-ref --count=1 --format='x' refs/issues/)"
then
	printf 'No issues found.\n'
	exit 0
fi

# --- Optimized search ---
# For each issue: ONE git log call outputs commit metadata + trailers + text.
# A SINGLE awk process does pattern matching, state filtering, and formatting.

{
git for-each-ref --format='%(refname)' refs/issues/ | while IFS= read -r ref
do
	printf 'ISSUE\t%s\n' "$ref"
	git log --format='COMMIT%x09%P%x09%s%x09%at%n%(trailers)COMMIT_END%n%B%nTEXT_END' "$ref"
done
} | awk \
	-v pattern="$pattern" \
	-v filter_state="$filter_state" \
	-v ignore_case="$ignore_case" \
'
BEGIN {
	if (ignore_case) pat = tolower(pattern)
	else pat = pattern
}

function check_match(line) {
	if (ignore_case) return index(tolower(line), pat)
	return index(line, pat)
}

function emit() {
	if (ref == "") return

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

	if (state == "") state = "open"

	# State filter
	if (filter_state != "all" && state != filter_state) return

	# Title
	title = (title_over != "") ? title_over : root_subj

	# Check for matches across all collected text
	matched = 0
	match_lines = ""
	match_count = 0
	for (i = 1; i <= text_n; i++) {
		if (check_match(text_arr[i])) {
			matched = 1
			match_count++
			if (match_count <= 5) {
				match_lines = match_lines "       " match_count ":" text_arr[i] "\n"
			}
		}
	}

	if (matched) {
		printf "%s [%s] %s\n", short_id, state, title
		if (match_lines != "") printf "%s", match_lines
	}
}

/^ISSUE\t/ {
	emit()
	ref = substr($0, 7)
	state = ""; title_over = ""; root_subj = ""
	text_n = 0; in_text = 0
	delete text_arr
	next
}

/^COMMIT\t/ {
	in_text = 0
	split($0, c, "\t")
	c_parents = c[2]
	c_subject = c[3]
	if (c_parents == "") root_subj = c_subject
	# Store subject as searchable text
	text_n++; text_arr[text_n] = c_subject
	next
}

/^COMMIT_END/ { in_text = 1; next }
/^TEXT_END/ { in_text = 0; next }

# Trailer lines (between COMMIT and COMMIT_END)
!in_text && /^State: /  { if (state == "") { state = $0; sub(/^State: /, "", state) } next }
!in_text && /^Title: /  { if (title_over == "") { title_over = $0; sub(/^Title: /, "", title_over) } next }

# Body text (between COMMIT_END and TEXT_END)
in_text {
	text_n++; text_arr[text_n] = $0
}

END { emit() }
'
