#!/bin/bash

#
# git recent 2.0 - switching branches, but so fancy
#
# - view recently edited local branches
# - see unique commits to that branch, and optionally the branch diff (against the main/master/primary branch) with Ctrl-o
# - hit Enter to checkout the selected branch.
# - text filtering against branch names, too.
#

if ! command -v fzf >/dev/null 2>&1; then
  echo "Error: fzf is not installed. Please install fzf to use git-recent." >&2
  exit 1
fi
[ "$(git rev-parse --is-inside-work-tree)" = "true" ] || exit

if [[ "$1" == "--help" ]]; then
  echo "git-recent: Browse and checkout recently used Git branches."
  echo
  echo "Options:"
  echo "  --cl, --pr  Show Gerrit CL or GitHub PR links next to branches"
  echo
  echo "Keybindings:"
  echo "  Enter:      Checkout the selected branch (or jump to its worktree if open elsewhere)"
  echo "  Ctrl-O:     Show the diff of the selected branch against the main/master branch"
  echo "  Ctrl-C:     Exit"
  exit 0
fi

# ---------------------------------------------------------------------------------------

# The HEAD of the primary branch (eg main or master or w/e), for diffing.
# TODO: some branch mgmt approaches don't work well with this. And may prefer `git log --pretty=format:%H --merges -n 1`.  See https://github.com/paulirish/git-recent/issues/28
diff_base=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null || git rev-parse origin/main 2>/dev/null || git rev-parse origin/master 2>/dev/null || echo "HEAD")

# Extract branch name (without any trailing text, like the Chromium link)
define_branchname="branchname=\\\$(echo {1} | cut -d' ' -f1)"

# Colorized hash, author, date, then commit subject followed by commit message body (wrapped and indented).
commits_format="%C(red bold)%h %C(bold blue)%an %C(bold green)%ad %Creset%s%w(0,4,4)%+b%w(0,0,0)"
uniqcommits_cmd="sh -c \"$define_branchname; git log --date=human --color=always --format='$commits_format' --no-merges $diff_base..\\\$branchname\""

# Progressive enhancement if you have delta or diff-so-fancy
diff_pager_cmd=$(command -v delta || command -v diff-so-fancy)
pipe_to_pager=${diff_pager_cmd:+" | $diff_pager_cmd"}
diffbranch_cmd="sh -c \"$define_branchname; git diff --color=always $diff_base...\\\$branchname $pipe_to_pager\""

# Copy to clipboard, variants for mac/linux
copy_cmd="echo 'Could not copy {} to clipboard.'"
if command -v pbcopy >/dev/null; then
  copy_cmd="printf '%s' {} | pbcopy"
elif command -v wl-copy >/dev/null; then
  copy_cmd="printf '%s' {} | wl-copy"
elif command -v xclip >/dev/null; then
  copy_cmd="printf '%s' {} | xclip -selection clipboard"
elif command -v xsel >/dev/null; then
  copy_cmd="printf '%s' {} | xsel --clipboard"
fi

_get_worktree_for_branch() {
  local branch="$1"
  local current_toplevel
  current_toplevel=$(git rev-parse --show-toplevel 2>/dev/null)

  # Find the worktree path for the given branch, excluding the current one.
  # Using substr to handle paths with spaces correctly.
  git worktree list --porcelain | awk -v target="refs/heads/$branch" -v current="$current_toplevel" '
    /^worktree / { wt = substr($0, 10) }
    /^branch / && $2 == target {
      if (wt != current) {
        print wt
        exit
      }
    }
  '
}

YELLOW='\033[0;33m'
DIM='\033[2m'
NC='\033[0m' # No Color

# if show_links passed we'll collect CL/PR info (gerrit/github repos) and add links in the TUI.
[[ "$1" == "--cl" || "$1" == "-cl" || "$1" == "--pr" || "$1" == "-pr" ]] && show_links=true || show_links=false

# if extra arg passed (eg `git recent remotename`), then list those remote branches, rather than local ones
[[ -n "$1" && "$show_links" != true ]] && heads="refs/remotes/$1" || heads="refs/heads"


# fzf git inspiration:
# - https://github.com/junegunn/fzf/wiki/Examples#git
# - https://github.com/junegunn/fzf/wiki/Examples-(fish)#git
# - https://github.com/junegunn/fzf-git.sh (intense.)

# If there's a GIT_RECENT_QUERY environment variable, use it for non-interactive filtering. (Primarily added for testing: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/g/git-recent.rb#L41-L46)
filterarg=${GIT_RECENT_QUERY:+"--filter=$GIT_RECENT_QUERY"}

# Chromium/Gerrit/GitHub hackers may want reference to their relevant CL/PR.
WT_BRANCHES=$(git worktree list --porcelain | grep "^branch " | sed 's|^branch refs/heads/||')

_browse_branches() {
  local cs="" ct="" gs="" gp="" gc="" sl="$show_links"
  if [[ "$sl" == true ]]; then
    gc=$(git config --get-regexp "branch\..*\.gerrit" 2>/dev/null)
    gs=$( (git config --get "gerrit.host" || git config --get "gerritserver") 2>/dev/null | sed -E 's|https?://||')
    gp=$(git config --get "gerrit.project" 2>/dev/null)
    local rems=$(git remote -v 2>/dev/null)
    if [[ "$rems" == *"github.com"* ]] && command -v gh >/dev/null 2>&1; then
      ct="github"; cs=$(gh pr list --state all --author "@me" --limit 100 --json headRefName,number,url --template '{{range .}}{{ .headRefName }} : {{ .url }} {{ .number }}{{"\n"}}{{end}}')
    fi
  fi

  git for-each-ref --sort=-authordate "$heads" --format="%(refname:short)" \
    | while read -r branch_name; do
        wt_symbol=""; [[ "$WT_BRANCHES" == *"$branch_name"* ]] && wt_symbol=" ⇶"
        label="" url=""
        if [[ "$sl" == true ]]; then
          num=$(echo "$gc" | grep "branch\.${branch_name}\.gerritissue" | awk '{print $2}')
          if [[ -n "$num" ]]; then
            srv=$(echo "$gc" | grep "branch\.${branch_name}\.gerritserver" | awk '{print $2}' | sed -E 's|https?://||')
            if [[ -n "$gp" ]]; then
              url="https://${srv:-$gs}/c/$gp/+/$num"
            else
              url="https://${srv:-$gs}/c/$num"
            fi
            label="$num"
          elif [[ -n "$cs" ]]; then
             info=$(echo "$cs" | grep -F "${branch_name} :" | head -n 1)
             if [[ -n "$info" ]]; then
               url=$(echo "$info" | grep -o -E 'https://[^ ]+')
               label=$(echo "$url" | grep -o -E '[0-9]+$')
               [[ "$ct" == "github" ]] && label="#$label"
             fi
          fi
        fi

        if [[ -n "$label" ]]; then
          # Clickable hyperlink: https://iterm2.com/feature-reporting/Hyperlinks_in_Terminal_Emulators.html
          # We skip the hyperlink escape sequence if TERM=dumb (for easier testing/plain logs)
          if [[ "$TERM" == "dumb" ]]; then
            printf "$YELLOW%s$DIM%s %s ($url)$NC\n" "$branch_name" "$wt_symbol" "$label"
          else
            printf "$YELLOW%s$DIM%s \033]8;;%s\a%s\033]8;;\a$NC\n" "$branch_name" "$wt_symbol" "$url" "$label"
          fi
        else
          printf "$YELLOW%s$DIM%s$NC\n" "$branch_name" "$wt_symbol"
        fi
      done \
    | fzf  \
        $filterarg --ansi -- --layout=reverse --multi --height=90% --min-height=20  \
        --border-label-pos=2 --border-label '🌲 Branches' --border \
        --no-hscroll --no-multi \
        --preview-window='right,70%,border-left,border-rounded' --preview="$uniqcommits_cmd" --preview-label="Commits unique to branch" \
        --header $'ENTER (checkout)\nCTRL-O (show branch diff)\nCTRL-Y (copy name to clipboard)' \
        --bind 'preview-scroll-up:preview-up+preview-up+preview-up' \
        --bind 'preview-scroll-down:preview-down+preview-down+preview-down' \
        --bind "ctrl-y:execute-silent($copy_cmd)" \
        --bind "ctrl-o:preview:$diffbranch_cmd"
}
output="$(_browse_branches)"
# If fzf returned a single branch in non-interactive mode (GIT_RECENT_QUERY set), 
# it normally auto-checkouts. We detect if we should skip that for testing.
line_count=$(printf "%s" "$output" | wc -l)

if [[ -n "$output" ]] && (( line_count == 0 )) && [[ "$GIT_RECENT_TEST_NO_CHECKOUT" != "true" ]]; then
  chosen_branch=$(echo "$output" | cut -d' ' -f1)

  wt_path=$(_get_worktree_for_branch "$chosen_branch")

  if [[ -n "$wt_path" ]]; then
    echo "Branch '$chosen_branch' is open in worktree: $wt_path" >&2
    echo "$wt_path"
  else
    echo "git checkout $chosen_branch" >&2
    git checkout "$chosen_branch"
  fi
else
  echo "$output"
fi
