#!/bin/bash

. $(dirname $0)/init-functions

## for patches
DIRECTORY=""

# Cleanup function
cleanup() {
	local exit_code=$?
	# Clean up temporary files
	[ -f "$patchlisttmpfile" ] && rm -f "$patchlisttmpfile"
	[ -f "$_patchlisttmpfile" ] && rm -f "$_patchlisttmpfile"
	[ -f "$sortresultfile" ] && rm -f "$sortresultfile"
	# Clean up any remaining .dups files
	rm -f /tmp/*.dups 2>/dev/null
	exit $exit_code
}

# Set up trap for cleanup
trap cleanup EXIT INT TERM

function usage () {
	echo "This script will automatically get a list of all patches that"
	echo "have not yet been submitted for merge, and then automatically"
	echo "sort out a patch list."
	echo "If you specify the -o directory, it will generate a patchset that"
	echo "can be used by git -am"
	echo ""
	echo "-o|--output: put the diffs in this directory."
	echo "-h|--help: show this help"
}

# Verify we're in a git repository
if ! git rev-parse --git-dir > /dev/null 2>&1; then
	echo "Error: This directory is not a git repository." >&2
	exit 1
fi

# Get current branch
current_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
if [ -z "$current_branch" ]; then
	echo "Error: Unable to determine current branch." >&2
	exit 1
fi

# Get unmerged commits
commits=$(git log --pretty=oneline "HEAD...origin/$current_branch" --reverse 2>/dev/null | awk '{print $1}')
if [ -z "$commits" ]; then
	echo "No unmerged commits found." >&2
	exit 1
fi

# Parse command line arguments
if ! ARGS=$(getopt -o ho: -a --long help,output: -- "$@"); then
	echo "Error: Invalid arguments." >&2
	usage
	exit 1
fi

eval set -- "$ARGS"

while true; do
	case $1 in
		-h|--help)
			usage
			exit 0
			;;
		-o|--output)
			DIRECTORY="$2"
			shift 2
			;;
		--)
			shift
			break
			;;
		*)
			echo "Error: Unknown option $1" >&2
			usage
			exit 1
			;;
	esac
done

# Create output directory if specified
if [ -n "$DIRECTORY" ]; then
	if ! mkdir -p "$DIRECTORY"; then
		echo "Error: Failed to create directory '$DIRECTORY'." >&2
		exit 1
	fi
fi
# final sorted list of commit ids
patchlisttmpfile=$(mktemp)

function sort_commit_file_by_authordate() {
	local filen="$1"

	if [ ! -f "$filen" ]; then
		echo "Warning: File '$filen' not found." >&2
		return 1
	fi

	# file contains "commitdate commitid authordate downstreamid"
	sort -k3 -n "$filen" -o "$filen"
	awk '{print $1" "$4}' "$filen" >> "$sortresultfile"
	# delete the temp file
	rm -f "$filen"
}

function sort_commit_list() {
	if [ ! -f "$_patchlisttmpfile" ]; then
		echo "Error: Patch list file not found." >&2
		return 1
	fi

	sort -k1 -n "$_patchlisttmpfile" -o "$_patchlisttmpfile"

	sortresultfile=$(mktemp)
	local prev_cdatefile="/tmp/1.dups"

	# Process each line in the patch list
	while IFS=' ' read -r _cdate _cid _downid; do
		[ -z "$_cdate" ] && continue

		# Get author date for the commit
		local _adate
		_adate=$(git log --pretty=%at -1 "$_cid" 2>/dev/null)
		if [ -z "$_adate" ]; then
			echo "Warning: Unable to get author date for commit $_cid" >&2
			continue
		fi

		# Group commits by commit date
		local cdatefile="/tmp/${_cdate}.dups"
		if [ -e "$cdatefile" ]; then
			# Add to existing group
			echo "$_cdate $_cid $_adate $_downid" >> "$cdatefile"
		else
			# Create new group and process previous one
			echo "$_cdate $_cid $_adate $_downid" > "$cdatefile"
			if [ -e "$prev_cdatefile" ]; then
				sort_commit_file_by_authordate "$prev_cdatefile"
			fi
		fi
		prev_cdatefile="$cdatefile"
	done < "$_patchlisttmpfile"

	# Process the last group
	if [ -f "$_patchlisttmpfile" ] && [ -s "$_patchlisttmpfile" ]; then
		local last_cdate
		last_cdate=$(tail -1 "$_patchlisttmpfile" | awk '{print $1}')
		if [ -n "$last_cdate" ] && [ -f "/tmp/${last_cdate}.dups" ]; then
			sort_commit_file_by_authordate "/tmp/${last_cdate}.dups"
		fi
	fi

	# Replace original file with sorted result
	if [ -f "$sortresultfile" ]; then
		mv "$sortresultfile" "$_patchlisttmpfile"
	fi
}

# Create temporary files for processing
echo "Processing commits..." >&2
_patchlisttmpfile=$(mktemp)

# Process each downstream commit to find upstream mapping
for downstream_commit in $commits; do
	upstream_commit=$(git show "$downstream_commit" | grep Mainline: | head -n 1 | awk '{print $2}')
	if [[ x"$upstream_commit" == x"" ]]; then
		echo "Warning: $downstream_commit doesn't have upstream commit, please re-check." >&2
		echo "$downstream_commit" >> "$patchlisttmpfile"
		continue
	fi

	# Verify upstream_commit exists
	if ! git show "$upstream_commit" >/dev/null 2>&1; then
		echo "Warning: $downstream_commit with $upstream_commit is not valid upstream-commit, please re-check." >&2
		echo "$downstream_commit" >> "$patchlisttmpfile"
		continue
	fi

	# Get short hashes for better readability
	_UP_HASH_SHORT=$(git log -1 --pretty=%h "$upstream_commit" 2>/dev/null)
	[ -n "$_UP_HASH_SHORT" ] && upstream_commit="$_UP_HASH_SHORT"
	_DOWN_HASH_SHORT=$(git log -1 --pretty=%h "$downstream_commit" 2>/dev/null)
	[ -n "$_DOWN_HASH_SHORT" ] && downstream_commit="$_DOWN_HASH_SHORT"

	# Get upstream commit time for sorting
	_UP_CTIME=$(git log "$upstream_commit" -1 --pretty="%ct" | awk '{print $1}')
	echo "$_UP_CTIME" "$_UP_HASH_SHORT" "$_DOWN_HASH_SHORT" >> "$_patchlisttmpfile"
done

# Sort commits by date and author date
echo "Sorting commits by date..." >&2
sort_commit_list
awk -F " " '{print $2}' < "$_patchlisttmpfile" >> "$patchlisttmpfile"
rm -f "$_patchlisttmpfile"

# Check if we have any commits to process
if [ ! -s "$patchlisttmpfile" ]; then
	echo "Error: No valid commits found to process." >&2
	usage
	exit 1
fi

# Process and output patches
echo "Processing patches..." >&2
count=0
processed_count=0

while read -r LINE; do
	commitid=$(echo "$LINE" | awk -F " " '{print $1}')

	# Sanitize commitid to ensure it's valid
	commitid=$(git log --pretty=%h -1 "$commitid" 2>/dev/null)
	if [ -z "$commitid" ]; then
		echo "Warning: Invalid commit ID in line: $LINE" >&2
		continue
	fi

	# Get the patch title
	titlename="$(git log -1 --pretty=%s "$commitid" 2>/dev/null)"

	# Sanity check commitid
	if [ -z "$titlename" ]; then
		echo "Warning: Skipping bad commit ID: $commitid" >&2
		continue
	fi

	# Output commit ID
	echo "$commitid"
	processed_count=$((processed_count + 1))

	# Generate patch file if directory specified
	if [ -z "$DIRECTORY" ]; then
		continue;
	fi

	((count++))
	# Create numbered patch files for better organization
	tfile_name="$(printf "%04d-%s-%s" "$count" "$commitid" "$titlename")"
	# Sanitize filename
	tfile_name=$(echo "$tfile_name" | sed -e 's@[ /]@-@g' -e 's/[":\@|~{}()<>*]//g' -e "s/'//g")
	tfile_name=${tfile_name:0:80}
	tfile_name+=".patch"
	tfile="$DIRECTORY/$tfile_name"

	# Generate the patch file
	if git format-patch -1 --stdout "$commitid" > "$tfile" 2>/dev/null; then
		echo "  -> Generated: $tfile_name"
	else
		echo "Warning: Failed to generate patch for commit $commitid" >&2
	fi
done < "$patchlisttmpfile"

echo "Processed $processed_count commits." >&2
if [ -n "$DIRECTORY" ] && [ $count -gt 0 ]; then
	echo "Generated $count patch files in: $DIRECTORY" >&2
fi
