#!/bin/sh
#
# git-pr - Create and update pull requests via AGit flow
#
# Usage:
#   git pr <remote> <branch> [-t title] [-d description] [-f] [-n]
#
# By default, if no -t is given and a TTY is available, the editor
# is opened to compose the PR title and description. Use -t to skip
# the editor.
#
# Examples:
#   git pr origin main                          # open editor (if TTY)
#   git pr upstream main -t "Fix login bug"     # skip editor
#   git pr origin main -f                       # force push
#   git pr origin main -n                       # dry-run
#

set -e

usage() {
	cat <<'EOF'
Usage: git pr <remote> <target-branch> [options]

Create or update a pull request via AGit flow.
The topic branch defaults to the current branch name.

If no -t is given and stdin is a TTY, an editor is opened to
compose the PR title and description (like git commit).

Options:
  -t <title>        PR title (skips editor)
  -d <description>  PR description
  -o <topic>        Topic branch name (default: current branch)
  -f                Allow force push
  -n                Dry run (show what would be pushed)
  -h                Show this help
EOF
	exit "${1:-0}"
}

git_editor() {
	GIT_EDITOR=$(git var GIT_EDITOR 2>/dev/null) || \
	GIT_EDITOR="${VISUAL:-${EDITOR:-vi}}"
}

# Open an editor with a PR template, parse the result.
# Sets $title and $description.
edit_pr_message() {
	msgfile=$(mktemp "${TMPDIR:-/tmp}/git-pr.XXXXXX")
	trap 'rm -f "$msgfile"' EXIT

	# Pre-fill from commit message
	default_title=$(git log -1 --pretty=%s HEAD)
	default_desc=$(git log -1 --pretty=%b HEAD)

	cat > "$msgfile" <<EOF
${default_title}

${default_desc}
# Enter the pull request title on the first line above.
# Everything after the first blank line is the description.
# Lines starting with '#' will be ignored.
#
# Target: ${target}
# Topic:  ${topic}
#
# Changes to be submitted:
EOF
	git log --oneline "${target}..HEAD" 2>/dev/null \
		| sed 's/^/#   /' >> "$msgfile" || true

	git_editor
	$GIT_EDITOR "$msgfile" < /dev/tty > /dev/tty

	# Strip comments, first line = title, rest = description
	cleaned=$(sed '/^#/d' "$msgfile")
	title=$(printf '%s\n' "$cleaned" | sed 1q)
	description=$(printf '%s\n' "$cleaned" | sed '1,2d')

	# Trim trailing blank lines
	description=$(printf '%s\n' "$description" | sed '
:a
/^[[:space:]]*$/{
$d
N
ba
}
')

	if [ -z "$title" ]; then
		echo "error: empty title, aborting" >&2
		exit 1
	fi
}

if [ $# -lt 2 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
	usage
fi

remote="$1"
target="$2"
shift 2

title=""
description=""
topic=""
force=false
dryrun=false

while getopts "t:d:o:fnh" opt; do
	case "$opt" in
	t) title="$OPTARG" ;;
	d) description="$OPTARG" ;;
	o) topic="$OPTARG" ;;
	f) force=true ;;
	n) dryrun=true ;;
	h) usage ;;
	*) usage 1 ;;
	esac
done

# Default topic to current branch name
if [ -z "$topic" ]; then
	topic=$(git symbolic-ref --short HEAD 2>/dev/null) || {
		echo "error: cannot determine current branch (detached HEAD?)" >&2
		exit 1
	}
	# Strip the target prefix if the branch is already named like target/topic
	case "$topic" in
	"$target"/*)
		topic="${topic#"$target"/}"
		;;
	esac
fi

# Open editor if no title given and we have a TTY
if [ -z "$title" ] && [ -t 0 ]; then
	edit_pr_message
fi

# Build push options
set -- push
if [ -n "$title" ]; then
	set -- "$@" -o "title=$title"
fi
if [ -n "$description" ]; then
	set -- "$@" -o "description=$description"
fi
if [ "$force" = true ]; then
	set -- "$@" -o force-push=true
fi

refspec="HEAD:refs/for/${target}/${topic}"

set -- "$@" "$remote" "$refspec"

if [ "$dryrun" = true ]; then
	printf 'git'
	for arg in "$@"; do printf ' %s' "$arg"; done
	printf '\n'
	exit 0
fi

exec git "$@"
