#!/usr/bin/env bash

# Code modified from https://github.com/kamranahmedse/git-standup,
# under the MIT LICENSE.
usage() {
    cat <<EOS
    Usage:
    git standup [-a <author name>] [-w <weekstart-weekend>|-d <days-ago>] [-m <max-dir-depth>] [-D date-format] [-L] [-h] [-f] [-B] [-n <number-of-commits] [-F <gpg|authordate>]

    -a      - Specify author to restrict search to, default to current git user.
              Use "-a all" if you don't want the restriction.
    -w      - Specify weekday range to limit search to
    -m      - Specify the depth of recursive directory search
    -L      - Toggle inclusion of symbolic links in recursive directory search
    -d      - Specify the number of days back to include
    -D      - Specify the date format for "git log" (default: relative)
    -h      - Display this help screen
    -f      - Fetch the latest commits beforehand
    -B      - Display the commits in branch groups
    -n      - Limit the number of commits displayed per group
    -F gpg  - Show if commit is GPG signed (G) or not (N)
    -F authordate - Print author date instead of commit date

    Examples:
    git standup -a "John Doe" -w "MON-FRI" -m 3
EOS
}

warn() {
    >&2 echo "${BOLD}${RED}WARNING: $1${NORMAL}"
}

git rev-parse --show-toplevel > /dev/null 2>&1
in_git_repo=$?

# Use colors, but only if connected to a terminal, and that terminal
# supports them.
if command -v tput &>/dev/null; then
    ncolors=$(tput colors)
fi
if [[ -t 1 ]] && [[ -n "$ncolors" ]] && [[ "$ncolors" -ge 8 ]] ; then
    RED="$(tput setaf 1)"
    GREEN="$(tput setaf 2)"
    YELLOW="$(tput setaf 3)"
    BOLD="$(tput bold)"
    NORMAL="$(tput sgr0)"
    BOLD=$(tput bold)
    UNDERLINE=$(tput smul)
    NORMAL=$(tput sgr0)
    COLOR=always
else
    RED=""
    GREEN=""
    YELLOW=""
    BOLD=""
    NORMAL=""
    BOLD=""
    UNDERLINE=""
    NORMAL=""
    COLOR=never
fi

# Only enable exit-on-error after the non-critical colorization stuff,
# which may fail on systems lacking tput or terminfo
set -e

RANGE_SPECIFIED=
COMMIT_DATE_FORMAT=%cd
USE_GPG_FORMAT=no

while getopts "hgfF:Bd:a:w:m:D:n:L" opt; do
    case $opt in
        h)
            usage
            exit 0
            ;;
        a)
            if [[ "$OPTARG" = 'all' ]] ; then
                AUTHOR=".*"
            else
                AUTHOR="$OPTARG"
            fi
            ;;
        d)
            test -n "$RANGE_SPECIFIED" && warn "-d option is conflict with -w"
            RANGE_SPECIFIED=yes
            if [ "$OPTARG" -lt 1 ]; then
                >&2 echo "Specify days less than one is invalid"
                exit 1
            fi
            SINCE="$OPTARG days ago"
            ;;
        w)
            if [ -n "$RANGE_SPECIFIED" ]; then
                warn "-w option is conflict with -d"
                continue
            fi
            RANGE_SPECIFIED=yes

            week_range=${OPTARG}
            week_start="${week_range%%-*}"
            week_start="${week_start:="Mon"}"
            week_end="${week_range##*-}"
            week_end=${week_end:="Fri"}

            ## In case it is the start of week, we need to
            ## show the commits since the last weekend
            shopt -s nocasematch
            if [[ "$week_start" == "$(LC_ALL=C date +%a)" ]] ; then
                SINCE="last $week_end";
            fi
            ;;
        f)
            FETCH_LAST_COMMIT=true
            ;;
        m)
            MAXDEPTH=$((OPTARG + 1))
            if [ "$MAXDEPTH" -lt 1 ]; then
                >&2 echo "Specify depth less than one is invalid"
                exit 1
            fi
            ;;
        L)
            INCLUDE_LINKS=-L
            ;;
        D)
            GIT_DATE_FORMAT=${OPTARG}
            ;;
        g)
            warn "-g option is deprecated, use '-F gpg' instead"
            USE_GPG_FORMAT=yes
            ;;
        B)
            GROUP_BY_BRANCHES=true
            ;;
        n)
            MAX_COMMIT_NUM=${OPTARG}
            ;;
        F)
            case $OPTARG in
                gpg)
                    USE_GPG_FORMAT=yes
                    ;;
                authordate)
                    COMMIT_DATE_FORMAT=%ad
                    ;;
                *)
                    warn "Invalid argument for -F: $OPTARG"
                    usage
                    exit 1
                    ;;
            esac
            ;;
        \?)
            usage
            exit 1
            ;;
    esac
done

shift $((OPTIND-1))

if [[ $# -gt 0 ]]; then
    warn "please upgrade to new-style interface. Run 'git help standup' to get more info."
    if [[ $# -gt 3 ]] ; then
        usage
        exit 1
    fi
    AUTHOR=$1
    SINCE=$2
    UNTIL=$3
fi

AUTHOR=${AUTHOR:="$(git config user.name || echo '')"}
if [ -z "${AUTHOR}" ]; then
    warn "please configure an author with 'git config user.name' or specify an author via '-a'"
    exit 1
fi

FETCH_LAST_COMMIT=${FETCH_LAST_COMMIT:=false}
MAXDEPTH=${MAXDEPTH:=2}
GIT_PRETTY_FORMAT="%Cred%h%Creset - %s %Cgreen(${COMMIT_DATE_FORMAT}) %C(bold blue)<%an>%Creset"
GIT_DATE_FORMAT=${GIT_DATE_FORMAT:=relative}
if [[ "$USE_GPG_FORMAT" == 'yes' ]]; then
    GIT_PRETTY_FORMAT="$GIT_PRETTY_FORMAT %C(yellow)gpg: %G?%Creset"
fi

# Handle config of implicit week
IMPLICIT_WEEK=$(git config --get git-extras.standup.implicit-week || echo '')
if [[ -z "$RANGE_SPECIFIED" ]] && [[ -n "${IMPLICIT_WEEK}" ]]; then
    week_start=${IMPLICIT_WEEK%%-*}
    week_end=${IMPLICIT_WEEK##*-}
    shopt -s nocasematch
    if [[ "$week_start" == "$(LC_ALL=C date +%a)" ]]; then
        SINCE="last $week_end"
    fi
    UNTIL=today
else
    SINCE=${SINCE:=yesterday}
    UNTIL=${UNTIL:=today}
fi

GIT_LOG_COMMAND="git --no-pager log \
    --no-merges
    --since \"$SINCE\"
    --until \"$UNTIL\"
    --author=\"$AUTHOR\"
    --abbrev-commit
    --oneline
    --color=$COLOR
    --pretty=format:'$GIT_PRETTY_FORMAT'
    --date='$GIT_DATE_FORMAT'"
if [[ -n "$MAX_COMMIT_NUM" ]]; then
    GIT_LOG_COMMAND="$GIT_LOG_COMMAND --max-count=$MAX_COMMIT_NUM"
fi

git_output() {
    if [ "$GROUP_BY_BRANCHES" = true ]; then
        local branches
        branches=$(git branch --sort=-committerdate | awk '{print substr($0, 3)}')
        for branch in $branches; do
            # shellcheck disable=SC2086
            if output=$(eval $GIT_LOG_COMMAND "$branch"); then
                if [[ -n "$output" ]] ;  then
                    echo "${GREEN}${branch}${NORMAL}"
                    echo "$output"
                    echo ""
                fi
            fi
            # TODO optimize:return if the latest commit of a branch is earlier than the 'since' day
        done
    else
        # shellcheck disable=SC2086
        eval $GIT_LOG_COMMAND --all
    fi
}

## For when the command has been run in a non-repo directory
if [[ $in_git_repo != 0 ]]; then
    ## Set delimiter to newline for the loop
    IFS=$'\n'
    ## Recursively search for git repositories
    # shellcheck disable=SC2086
    PROJECT_DIRS=$(find $INCLUDE_LINKS . -maxdepth "$MAXDEPTH" -mindepth 0 -name .git)

    # Fetch the latest commits, if required
    if [ "$FETCH_LAST_COMMIT" = true ]; then

        echo "${BOLD}${GREEN}Fetching commits ..${NORMAL}"

        # Foreach of the project directories, fetch the commits
        for DIR in $PROJECT_DIRS; do
            DIR="$(dirname "$DIR")"
            pushd "$DIR" > /dev/null

            if [[ -d ".git" ]] ; then
                echo "   ${YELLOW}$(basename "$DIR")${NORMAL}"
                git fetch --all > /dev/null 2>&1
            fi

            popd > /dev/null
        done
    fi

    # Get the standup details for each of the projects
    for DIR in $PROJECT_DIRS; do
        DIR="$(dirname "$DIR")"
        pushd "$DIR" > /dev/null
        ## Show the detail only if it is a git repository
        if [[ -d ".git" || -f ".git" ]] ; then
            if GITOUT=$(git_output); then
                ## Only output if there is some activities
                if [[ -n "$GITOUT" ]] ;  then
                    echo "${BOLD}${UNDERLINE}${YELLOW}$(basename "$DIR")${NORMAL}"
                    echo "$GITOUT"
                    echo ""
                fi
            else
                echo "Repository under $DIR could not be queried." >&2
            fi
        fi
        popd > /dev/null
    done
else
    if [ "$FETCH_LAST_COMMIT" = true ]; then
        echo "${GREEN}Fetching commits ..${NORMAL}"
        git fetch --all > /dev/null 2>&1
    fi

    if GITOUT=$(git_output); then
        if [[ -n "$GITOUT" ]] ;  then
            echo "$GITOUT"
        else
            if [[ $AUTHOR = '.*' ]] ; then
                AUTHOR="all the contributors"
            fi

            echo "${YELLOW}Seems like $AUTHOR did nothing!${NORMAL}"
        fi
    fi
fi
