#!/bin/sh
#
# git-issue-ls - List issues
#
# Performance: Uses a single git-log call per issue with awk aggregation
# to avoid the N×M subprocess problem (was ~22 forks/issue, now ~1).
#

set -e

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

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

Options:
   -s, --state <state>     Filter by state (open, closed, all) [default: open]
   -l, --label <label>     Filter by label
   --assignee <email>      Filter by assignee
   --priority <level>      Filter by priority (low, medium, high, critical)
   -a, --all               Show all issues (same as --state all)
   -f, --format <format>   Output format: short (default), full, oneline
   --sort <field>          Sort by: created (default), updated, priority, state
   --reverse               Reverse sort order
   -h, --help              Show this help

Formats:
   short    <id> [<state>] <title>
   full     <id> [<state>] <title> with labels, assignee, priority, milestone
   oneline  <id> <state> <title> (no brackets, for scripting)
EOF
	exit 1
}

filter_state="open"
filter_label=""
filter_assignee=""
filter_priority=""
output_format="short"
sort_field="created"
sort_reverse=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
			;;
		-l|--label)
			test $# -ge 2 || { echo "error: -l requires a value" >&2; exit 1; }
			filter_label="$2"
			shift 2
			;;
		--assignee)
			test $# -ge 2 || { echo "error: --assignee requires a value" >&2; exit 1; }
			filter_assignee="$2"
			shift 2
			;;
		--priority)
			test $# -ge 2 || { echo "error: --priority requires a value" >&2; exit 1; }
			case "$2" in
				low|medium|high|critical) ;;
				*) echo "error: priority must be low, medium, high, or critical" >&2; exit 1 ;;
			esac
			filter_priority="$2"
			shift 2
			;;
		-a|--all)
			filter_state="all"
			shift
			;;
		-f|--format)
			test $# -ge 2 || { echo "error: -f requires a value" >&2; exit 1; }
			case "$2" in
				short|full|oneline) ;;
				*) echo "error: format must be short, full, or oneline" >&2; exit 1 ;;
			esac
			output_format="$2"
			shift 2
			;;
		--sort)
			test $# -ge 2 || { echo "error: --sort requires a value" >&2; exit 1; }
			case "$2" in
				created|updated|priority|state) ;;
				*) echo "error: sort must be created, updated, priority, or state" >&2; exit 1 ;;
			esac
			sort_field="$2"
			shift 2
			;;
		--reverse)
			sort_reverse=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
}

# 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
	exit 0
fi

# Collect issue data into a temp file for sorting
data_file="$(mktemp)"
trap 'rm -f "$data_file"' EXIT

# --- Optimized extraction ---
# For each issue: ONE git log call outputs commit metadata lines (COMMIT\t...)
# followed by %(trailers) as Key: Value lines, then COMMIT_END markers.
# A SINGLE awk process aggregates per-issue data (first non-empty value
# for each trailer key), applies filters, and outputs sort-ready rows.
#
# Old approach: ~22 subprocess forks per issue (5× git log + 10× sed + 5× head + cut)
# New approach: 1 git log fork per issue + 1 awk process total

{
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' "$ref"
done
} | awk \
	-v filter_state="$filter_state" \
	-v filter_label="$filter_label" \
	-v filter_assignee="$filter_assignee" \
	-v filter_priority="$filter_priority" \
	-v sort_field="$sort_field" \
'
function emit() {
	if (ref == "") return

	# Extract short ID from ref
	uuid = ref
	sub(/^refs\/issues\//, "", uuid)
	short_id = substr(uuid, 1, 7)

	# Default state
	if (state == "") state = "open"

	# Apply filters
	if (filter_state != "all" && state != filter_state) return

	if (filter_label != "") {
		found = 0
		n = split(labels, la, /[[:space:]]*,[[:space:]]*/)
		for (i = 1; i <= n; i++) {
			if (la[i] == filter_label) { found = 1; break }
		}
		if (!found) return
	}

	if (filter_assignee != "" && assignee != filter_assignee) return
	if (filter_priority != "" && priority != filter_priority) return

	# Title: trailer override or root commit subject
	title = (title_over != "") ? title_over : root_subj

	# Sort key
	if (sort_field == "created") sk = created_ts
	else if (sort_field == "updated") sk = updated_ts
	else if (sort_field == "priority") {
		if (priority == "critical") sk = 4
		else if (priority == "high") sk = 3
		else if (priority == "medium") sk = 2
		else if (priority == "low") sk = 1
		else sk = 0
	}
	else if (sort_field == "state") {
		sk = (state == "open") ? 1 : 0
	}

	# Use "-" placeholder for empty fields to prevent IFS field collapse in shell read
	printf "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", sk, short_id, state, title, \
		(labels == "" ? "-" : labels), \
		(assignee == "" ? "-" : assignee), \
		(priority == "" ? "-" : priority), \
		(milestone == "" ? "-" : milestone)
}

/^ISSUE\t/ {
	emit()
	ref = substr($0, 7)
	state = ""; labels = ""; assignee = ""; priority = ""; milestone = ""
	title_over = ""; root_subj = ""; created_ts = ""; updated_ts = ""
	has_assignee = 0; has_priority = 0; has_milestone = 0
	next
}

/^COMMIT\t/ {
	# Fields: COMMIT \t parents \t subject \t timestamp
	split($0, c, "\t")
	c_parents = c[2]
	c_subject = c[3]
	c_ts = c[4]
	# First commit line = tip (newest) -> updated timestamp
	if (updated_ts == "") updated_ts = c_ts
	# Root commit has no parents
	if (c_parents == "") { root_subj = c_subject; created_ts = c_ts }
	next
}

/^COMMIT_END/ { next }

# Trailer lines: "Key: Value" — aggregate first occurrence per key (even if empty)
/^State: /     { if (state == "")     { state = $0;     sub(/^State: /, "", state) } }
/^Labels: /    { if (labels == "")    { labels = $0;    sub(/^Labels: /, "", labels) } }
/^Assignee: /  { if (!has_assignee)   { assignee = $0;  sub(/^Assignee: /, "", assignee); has_assignee = 1 } }
/^Priority: /  { if (!has_priority)   { priority = $0;  sub(/^Priority: /, "", priority); has_priority = 1 } }
/^Milestone: / { if (!has_milestone)  { milestone = $0; sub(/^Milestone: /, "", milestone); has_milestone = 1 } }
/^Title: /     { if (title_over == "") { title_over = $0; sub(/^Title: /, "", title_over) } }

END { emit() }
' > "$data_file"

# Sort the results
if test "$sort_reverse" -eq 1
then
	sorted="$(sort -t'	' -k1,1n "$data_file")"
else
	sorted="$(sort -t'	' -k1,1rn "$data_file")"
fi

# Output the sorted results
printf '%s\n' "$sorted" | while IFS='	' read -r _sort_key short_id state title labels issue_assignee issue_priority issue_milestone
do
	# Skip empty lines
	test -n "$short_id" || continue

	# Convert "-" placeholders back to empty strings
	test "$labels" = "-" && labels=""
	test "$issue_assignee" = "-" && issue_assignee=""
	test "$issue_priority" = "-" && issue_priority=""
	test "$issue_milestone" = "-" && issue_milestone=""

	case "$output_format" in
		oneline)
			printf '%s %s %s\n' "$short_id" "$state" "$title"
			;;
		full)
			printf '%s [%s] %s\n' "$short_id" "$state" "$title"
			detail=""
			test -n "$labels" && detail="${detail} labels:${labels}"
			test -n "$issue_assignee" && detail="${detail} assignee:${issue_assignee}"
			test -n "$issue_priority" && detail="${detail} priority:${issue_priority}"
			test -n "$issue_milestone" && detail="${detail} milestone:${issue_milestone}"
			test -n "$detail" && printf '       %s\n' "$detail"
			;;
		*)
			printf '%s [%s] %s\n' "$short_id" "$state" "$title"
			;;
	esac
done
