#!/usr/bin/env bash

# git-my v0.3.0
#
# Lists a user's remote branches and shows if it was merged
# and/or available locally
#
# Copyright (c) 2015 David O'Trakoun <me@davidosomething.com>
#
# Usage:
#   git my
#

set -eu

# _check_in_repository
#
# Exits with error if not a git repository
#
_check_in_repository() {
  git rev-parse --git-dir > /dev/null 2>&1
}

# _get_local_branches
#
# Proper way to get a porcelain list of local branches for shell script use
#
_get_local_branches() {
  local fmt
  local cmd_get_local_branches

  # shellcheck disable=SC2016
  fmt='
    r=%(refname)
    refname_without_prefix=${r#refs/heads/}
    printf "%s\t%s\n" "$refname_without_prefix"
  '

  cmd_get_local_branches=$(
    git for-each-ref --shell --format="$fmt" refs/heads
  )

  eval "$cmd_get_local_branches"
}

# _get_everyones_remotes
#
# Get porcelain list of all remote branches
#
# @TODO support remote_name (currently origin)
#
_get_everyones_remotes() {
  local fmt
  local cmd_everyones_remotes

  # user.name<TAB>branch.name
  # the TAB is used as a delimiter to awk with
  # shellcheck disable=SC2016
  fmt='
    a=%(authorname)
    r=%(refname)
    refname_without_prefix=${r#refs/remotes/origin/}
    printf "%s\t%s\n" "$a" "$refname_without_prefix"
  '

  cmd_everyones_remotes=$(
    git for-each-ref --shell --format="$fmt" refs/remotes/origin
  )

  eval "$cmd_everyones_remotes"
}

# _get_merged_remote_branches
#
# @TODO support param remote_branche (currently origin/master)
#
# @output names of remote branches that are merged into origin/master
_get_merged_remote_branches() {
  local remote_name
  local remote_refname
  local merged_remote_branches
  local stripped_branchnames

  remote_name="origin"
  remote_refname="master"

  merged_remote_branches=$( \
    git branch --no-color \
      --remotes \
      --merged $remote_name/$remote_refname \
    )

  # remove "origin/"
  # shellcheck disable=SC2001
  stripped_branchnames=$( \
    echo "$merged_remote_branches" | sed "s/^\s*$remote_name\///g" \
    )

  echo "$stripped_branchnames"
}

# _filter_mine
#
# @param git_user
# @param branchnames
# @output branchnames owned by current git user
_filter_mine() {
  local git_user
  local branchnames
  git_user=$1
  branchnames=$2

  # use eval to not parse the fmt string
  local my_remotes
  my_remotes=$(echo "$branchnames" | grep -i "^$git_user")

  # output only the branchname
  echo "$my_remotes" | awk -F'\t' '{ print $2 }'
}

# _decorate_merged
#
# @TODO support param branch to check against
#
# @param branchnames to check if merged into against
# @output branchnames with [merged]
_decorate_merged() {
  local merged_remote_branches
  local decorated

  merged_remote_branches=$(_get_merged_remote_branches)
  local_branches=$(_get_local_branches)

  # heading
  decorated=$( printf '%12s %12s %s' \
      "local copy?" "in master? " "branch name"
  )
  decorated+=$'\n'

  # body
  for branchname in $1
  do
    local local_text="..........."
    local merged_text='............'

    echo "$local_branches" | grep -q "$branchname" && \
      local_text="..[local].."

    echo "$merged_remote_branches" | grep -q "$branchname" && \
      merged_text="..[merged].."

    decorated+=$(printf "%12s.%12s %s" \
      "$local_text" "$merged_text" "$branchname"
    )
    decorated+=$'\n'
  done

  echo "$decorated"
}

_main() {
  local everyones_remotes
  local my_remotes
  local output
  local git_user
  local git_remote
  local heading

  git_user=$(git config --get user.name)
  git_remote=$(git config --get remote.origin.url)
  heading="$git_user's remote branches in $git_remote"

  everyones_remotes=$(_get_everyones_remotes)
  my_remotes=$(_filter_mine "$git_user" "$everyones_remotes")
  output=$(_decorate_merged "$my_remotes")

  printf '+%78s+\n' | tr ' ' -
  printf '| %-76s |\n' "$heading"
  printf '+%78s+\n' | tr ' ' -
  echo "$output"
}

if ! _check_in_repository ; then
  echo "This is not a git repository."
  exit 1
fi

_main

