#!/bin/sh
#
# git-issue-import-github - Import issues from GitHub
#
# Usage: git issue import github:<owner>/<repo> [options]
#

set -e

usage() {
	cat <<EOF
usage: git issue import github:<owner>/<repo> [options]

Options:
   --state <state>   Filter by state: open, closed, all (default: open)
   --dry-run         Show what would be imported without importing
   -h, --help        Show this help

Authentication:
   Requires GitHub CLI (gh) to be installed and authenticated.
   Run 'gh auth login' if not already authenticated.
EOF
	exit 1
}

provider=""
state_filter="open"
dry_run=0

while test $# -gt 0
do
	case "$1" in
		--state)
			test $# -ge 2 || { echo "error: --state requires a value" >&2; exit 1; }
			case "$2" in
				open|closed|all) ;;
				*) echo "error: --state must be open, closed, or all" >&2; exit 1 ;;
			esac
			state_filter="$2"
			shift 2
			;;
		--dry-run)
			dry_run=1
			shift
			;;
		-h|--help)
			usage
			;;
		--)
			shift
			break
			;;
		-*)
			echo "error: unknown option '$1'" >&2
			usage
			;;
		*)
			if test -z "$provider"
			then
				provider="$1"
			fi
			shift
			;;
	esac
done

if test -z "$provider"
then
	echo "error: provider is required (e.g., github:owner/repo)" >&2
	usage
fi

# Parse provider string
case "$provider" in
	github:*/*)
		gh_path="${provider#github:}"
		gh_owner="${gh_path%/*}"
		gh_reponame="${gh_path#*/}"
		;;
	*)
		echo "error: invalid provider string '$provider'" >&2
		echo "       expected format: github:<owner>/<repo>" >&2
		exit 1
		;;
esac

# 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 prerequisites
command -v gh >/dev/null 2>&1 || {
	echo "error: 'gh' (GitHub CLI) is required but not found" >&2
	echo "       install: https://cli.github.com/" >&2
	exit 1
}

command -v jq >/dev/null 2>&1 || {
	echo "error: 'jq' is required but not found" >&2
	echo "       install: https://stedolan.github.io/jq/" >&2
	exit 1
}

# Check gh auth
gh auth status >/dev/null 2>&1 || {
	echo "error: 'gh' is not authenticated. Run 'gh auth login' first." >&2
	exit 1
}

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

# Build Provider-ID index from existing issues
_import_tmpdir="$(mktemp -d)"
trap 'rm -rf "$_import_tmpdir"' EXIT
provider_index="$_import_tmpdir/provider-index"
user_cache_dir="$_import_tmpdir/user-cache"
mkdir -p "$user_cache_dir"

git for-each-ref --format='%(refname)' refs/issues/ | while IFS= read -r ref
do
	# Walk all commits in the chain looking for Provider-ID
	git log --format='%(trailers:key=Provider-ID,valueonly)' "$ref" | \
		sed '/^$/d' | sed 's/^[[:space:]]*//' | while IFS= read -r pid
	do
		test -n "$pid" && printf '%s\n' "$pid"
	done
done > "$provider_index"

# Build API state parameter
case "$state_filter" in
	open)   api_state="state=open" ;;
	closed) api_state="state=closed" ;;
	all)    api_state="state=all" ;;
esac

# Fetch issues list (paginated)
issues_json="$(gh api --paginate "/repos/$gh_owner/$gh_reponame/issues?$api_state&per_page=100" 2>&1)" || {
	echo "error: failed to fetch issues from github:$gh_owner/$gh_reponame" >&2
	echo "       $issues_json" >&2
	exit 1
}

# Filter out pull requests and get issue numbers
issue_numbers="$(printf '%s' "$issues_json" | jq -r '.[] | select(.pull_request == null) | .number')"

imported=0
skipped=0

# Get the empty tree SHA
empty_tree="$(git hash-object -t tree /dev/null)"

# Helper: resolve GitHub user to name and email
# Also populates the persistent bidirectional cache for export reverse-lookup
resolve_user() {
	login="$1"
	cache_file="$user_cache_dir/$login"

	if test -f "$cache_file"
	then
		cat "$cache_file"
		return
	fi

	user_json="$(gh api "/users/$login" 2>/dev/null || echo '{}')"
	name="$(printf '%s' "$user_json" | jq -r '.name // empty')"
	email="$(printf '%s' "$user_json" | jq -r '.email // empty')"
	user_id="$(printf '%s' "$user_json" | jq -r '.id // empty')"

	if test -z "$name"
	then
		name="$login"
	fi

	if test -z "$email"
	then
		email="${login}@users.noreply.github.com"
	fi

	# Session cache (fast, in-memory via filesystem)
	printf '%s\n%s\n' "$name" "$email" > "$cache_file"

	# Persistent bidirectional cache (survives across sessions)
	if test -n "$user_id"
	then
		cache_platform_user "$email" "$login" "$user_id" "github"
	fi

	cat "$cache_file"
}

tmpfile="$_import_tmpdir/msg"

# Helper: find local ref for a Provider-ID
find_ref_for_provider_id() {
	pid="$1"
	git for-each-ref --format='%(refname)' refs/issues/ | while IFS= read -r ref
	do
		if git log --format='%(trailers:key=Provider-ID,valueonly)' "$ref" | grep -qF "$pid"
		then
			printf '%s\n' "$ref"
			return 0
		fi
	done
	# Don't return 1 here - just return 0 with empty output
	# (set -e would kill script otherwise)
	return 0
}

# Helper: get all GitHub comment IDs already imported for an issue
get_imported_comment_ids() {
	ref="$1"
	git log --format='%(trailers:key=Provider-Comment-ID,valueonly)' "$ref" | \
		sed '/^$/d' | sed 's/^[[:space:]]*//'
}

updated=0


for number in $issue_numbers
do
	provider_id="github:$gh_owner/$gh_reponame#$number"

	# Check if already imported
	if grep -qF "$provider_id" "$provider_index" 2>/dev/null
	then
		# Issue exists - check for new comments to import
		existing_ref="$(find_ref_for_provider_id "$provider_id")"

		if test -z "$existing_ref"
		then
			# Provider-ID in index but ref not found (shouldn't happen)
			skipped=$((skipped + 1))
			continue
		fi

		# Fetch comments from GitHub
		comments_json="$(gh api --paginate "/repos/$gh_owner/$gh_reponame/issues/$number/comments?per_page=100" 2>/dev/null || echo '[]')"
		comment_count="$(printf '%s' "$comments_json" | jq '. | length')"

		if test "$comment_count" -eq 0
		then
			# No comments on GitHub
			skipped=$((skipped + 1))
			continue
		fi

		# Get list of already-imported comment IDs
		imported_comment_ids="$(get_imported_comment_ids "$existing_ref")"

		# Find new comments (not in imported list)
		new_comments=0
		parent="$(git rev-parse "$existing_ref")"
		i=0

		while test "$i" -lt "$comment_count"
		do
			comment_id="$(printf '%s' "$comments_json" | jq -r ".[$i].id")"
			comment_provider_id="github:$gh_owner/$gh_reponame#comment-$comment_id"

			# Skip if already imported
			if echo "$imported_comment_ids" | grep -qF "$comment_provider_id"
			then
				i=$((i + 1))
				continue
			fi

			# New comment - import it
			comment_body="$(printf '%s' "$comments_json" | jq -r ".[$i].body // \"\"")"
			comment_date="$(printf '%s' "$comments_json" | jq -r ".[$i].created_at")"
			comment_login="$(printf '%s' "$comments_json" | jq -r ".[$i].user.login")"

			comment_info="$(resolve_user "$comment_login")"
			comment_name="$(printf '%s' "$comment_info" | head -1)"
			comment_email="$(printf '%s' "$comment_info" | tail -1)"

			# Split comment into subject and body
			first_line="$(printf '%s\n' "$comment_body" | head -1)"
			remaining="$(printf '%s\n' "$comment_body" | tail -n +2)"

			if test ${#first_line} -gt 72
			then
				comment_subject="$(printf '%.69s...' "$first_line")"
				printf '%s\n\n%s\n' "$comment_subject" "$comment_body" > "$tmpfile"
			elif test -n "$remaining"
			then
				printf '%s\n\n%s\n' "$first_line" "$remaining" > "$tmpfile"
			else
				printf '%s\n' "$first_line" > "$tmpfile"
			fi

			# Add Provider-Comment-ID trailer
			git interpret-trailers --in-place --trailer "Provider-Comment-ID: $comment_provider_id" "$tmpfile"

			if test "$dry_run" -eq 0
			then
				new_commit="$(GIT_AUTHOR_NAME="$comment_name" \
					GIT_AUTHOR_EMAIL="$comment_email" \
					GIT_AUTHOR_DATE="$comment_date" \
					git commit-tree -p "$parent" -- "$empty_tree" < "$tmpfile")"

				parent="$new_commit"
				new_comments=$((new_comments + 1))
			fi

			i=$((i + 1))
		done

		if test "$new_comments" -gt 0
		then
			if test "$dry_run" -eq 1
			then
				short_id="$(basename "$existing_ref" | cut -c1-7)"
				printf '[dry-run] Would update %s with %d new comment(s) from #%s\n' "$short_id" "$new_comments" "$number"
			else
				# Update ref to new head
				old_head="$(git rev-parse "$existing_ref")"
				git update-ref "$existing_ref" "$parent" "$old_head"

				short_id="$(basename "$existing_ref" | cut -c1-7)"
				printf 'Updated %s with %d new comment(s) from #%s\n' "$short_id" "$new_comments" "$number"
				updated=$((updated + 1))
			fi
		else
			skipped=$((skipped + 1))
		fi

		continue
	fi

	# Fetch full issue data
	issue_json="$(gh api "/repos/$gh_owner/$gh_reponame/issues/$number")"

	title="$(printf '%s' "$issue_json" | jq -r '.title')"
	body="$(printf '%s' "$issue_json" | jq -r '.body // ""')"
	issue_state="$(printf '%s' "$issue_json" | jq -r '.state')"
	created_at="$(printf '%s' "$issue_json" | jq -r '.created_at')"
	author_login="$(printf '%s' "$issue_json" | jq -r '.user.login')"

	# Labels as comma-separated
	labels="$(printf '%s' "$issue_json" | jq -r '[.labels[].name] | join(", ")')"

	# Assignee
	assignee_login="$(printf '%s' "$issue_json" | jq -r '.assignee.login // empty')"

	# Milestone
	milestone="$(printf '%s' "$issue_json" | jq -r '.milestone.title // empty')"

	if test "$dry_run" -eq 1
	then
		printf '[dry-run] Would import #%s: %s\n' "$number" "$title"
		imported=$((imported + 1))
		continue
	fi

	# Resolve author
	author_info="$(resolve_user "$author_login")"
	author_name="$(printf '%s' "$author_info" | head -1)"
	author_email="$(printf '%s' "$author_info" | tail -1)"

	# Generate UUID
	if command -v uuidgen >/dev/null 2>&1
	then
		uuid="$(uuidgen | tr '[:upper:]' '[:lower:]')"
	elif test -r /proc/sys/kernel/random/uuid
	then
		uuid="$(cat /proc/sys/kernel/random/uuid)"
	else
		# Fallback: generate from random bytes (od -An -tx1 is portable across BSD/GNU)
		uuid="$(od -An -tx1 -N 16 /dev/urandom | tr -d ' \n' | sed 's/\(........\)\(....\)\(....\)\(....\)\(............\)/\1-\2-\3-\4-\5/')"
	fi

	short_id="$(printf '%s' "$uuid" | cut -c1-7)"

	# Build root commit message
	msg="$title"
	if test -n "$body"
	then
		msg="$msg

$body"
	fi

	printf '%s\n' "$msg" > "$tmpfile"

	# Add trailers
	git interpret-trailers --in-place --trailer "State: open" "$tmpfile"

	if test -n "$labels"
	then
		git interpret-trailers --in-place --trailer "Labels: $labels" "$tmpfile"
	fi

	if test -n "$assignee_login"
	then
		assignee_info="$(resolve_user "$assignee_login")"
		assignee_email="$(printf '%s' "$assignee_info" | tail -1)"
		git interpret-trailers --in-place --trailer "Assignee: $assignee_email" "$tmpfile"
	fi

	if test -n "$milestone"
	then
		git interpret-trailers --in-place --trailer "Milestone: $milestone" "$tmpfile"
	fi

	git interpret-trailers --in-place --trailer "Provider-ID: $provider_id" "$tmpfile"
	git interpret-trailers --in-place --trailer "Format-Version: 1" "$tmpfile"

	# Create root commit with original author and date
	commit="$(GIT_AUTHOR_NAME="$author_name" \
		GIT_AUTHOR_EMAIL="$author_email" \
		GIT_AUTHOR_DATE="$created_at" \
		git commit-tree -- "$empty_tree" < "$tmpfile")"

	# Create the ref
	git update-ref -- "refs/issues/$uuid" "$commit"

	# Fetch and import comments
	comments_json="$(gh api --paginate "/repos/$gh_owner/$gh_reponame/issues/$number/comments?per_page=100" 2>/dev/null || echo '[]')"
	comment_count="$(printf '%s' "$comments_json" | jq '. | length')"

	parent="$commit"
	i=0
	while test "$i" -lt "$comment_count"
	do
		comment_id="$(printf '%s' "$comments_json" | jq -r ".[$i].id")"
		comment_body="$(printf '%s' "$comments_json" | jq -r ".[$i].body // \"\"")"
		comment_date="$(printf '%s' "$comments_json" | jq -r ".[$i].created_at")"
		comment_login="$(printf '%s' "$comments_json" | jq -r ".[$i].user.login")"

		comment_info="$(resolve_user "$comment_login")"
		comment_name="$(printf '%s' "$comment_info" | head -1)"
		comment_email="$(printf '%s' "$comment_info" | tail -1)"

		# Split comment into subject (max 72 chars) and body
		first_line="$(printf '%s\n' "$comment_body" | head -1)"
		remaining="$(printf '%s\n' "$comment_body" | tail -n +2)"

		if test ${#first_line} -gt 72
		then
			# Long first line: truncate for subject, full text as body
			comment_subject="$(printf '%.69s...' "$first_line")"
			printf '%s\n\n%s\n' "$comment_subject" "$comment_body" > "$tmpfile"
		elif test -n "$remaining"
		then
			# Multi-line: first line is subject, rest is body
			printf '%s\n\n%s\n' "$first_line" "$remaining" > "$tmpfile"
		else
			# Short single-line: just subject
			printf '%s\n' "$first_line" > "$tmpfile"
		fi

		# Add Provider-Comment-ID trailer for tracking
		comment_provider_id="github:$gh_owner/$gh_reponame#comment-$comment_id"
		git interpret-trailers --in-place --trailer "Provider-Comment-ID: $comment_provider_id" "$tmpfile"

		new_commit="$(GIT_AUTHOR_NAME="$comment_name" \
			GIT_AUTHOR_EMAIL="$comment_email" \
			GIT_AUTHOR_DATE="$comment_date" \
			git commit-tree -p "$parent" -- "$empty_tree" < "$tmpfile")"

		parent="$new_commit"
		i=$((i + 1))
	done

	# If issue is closed, add a state-change commit
	if test "$issue_state" = "closed"
	then
		closed_at="$(printf '%s' "$issue_json" | jq -r '.closed_at // .updated_at')"
		printf '%s\n' "Close issue" > "$tmpfile"
		git interpret-trailers --in-place --trailer "State: closed" "$tmpfile"

		new_commit="$(GIT_AUTHOR_NAME="$author_name" \
			GIT_AUTHOR_EMAIL="$author_email" \
			GIT_AUTHOR_DATE="$closed_at" \
			git commit-tree -p "$parent" -- "$empty_tree" < "$tmpfile")"

		parent="$new_commit"
	fi

	# Update ref to final commit in chain
	if test "$parent" != "$commit"
	then
		git update-ref -- "refs/issues/$uuid" "$parent" "$commit"
	fi

	imported=$((imported + 1))
	printf 'Imported #%s as %s: %s\n' "$number" "$short_id" "$title"
done

if test "$updated" -gt 0
then
	printf 'Imported %d issues, updated %d issues (%d skipped)\n' "$imported" "$updated" "$skipped"
else
	printf 'Imported %d issues (%d skipped)\n' "$imported" "$skipped"
fi
