#!/bin/bash
# PHP CodeSniffer pre-commit hook for git
#
# based on https://github.com/s0enke/git-hooks

# @author Fabian Derschatta <fabian.derschatta@gtotaralearning.com>
#
# see the README

# In case we symlinked the hook we want to determine the real path
SCRIPTPATH="$(dirname $(readlink "$0"))"
PROJECTPATH=$(cd $SCRIPTPATH && cd ..; pwd -P)
GIT_PROJECT_PATH="$( cd "$(dirname "$0")" ; cd ../../; pwd -P )"
GHERKIN_LINT_PATH="$GIT_PROJECT_PATH/node_modules/.bin/gherkin-lint"
GHERKIN_LINT_CONFIG="$GIT_PROJECT_PATH/.gherkin-lintrc"

# Set some defaults
PHPCS_BIN=$PROJECTPATH/vendor/bin/phpcs
DIFFFILTER_BIN=$PROJECTPATH/vendor/bin/diffFilter
PHPCS_CODING_STANDARD=$PROJECTPATH/src/Standards/Totara/ruleset.xml
PHPCS_IGNORE=
PHPCS_FILE_EXTENSIONS=php
BEHAT_RUN_LINT=1

TMP_DIFF_FILES=/tmp/pre-commit-files.txt
TMP_DIFF=/tmp/pre-commit-diff.txt
TMP_JSON=/tmp/pre-commit-phpcs.json

# parse config
CONFIG_FILE=$SCRIPTPATH/config
if [ -e $CONFIG_FILE ]; then
    . $CONFIG_FILE
fi

# simple check if code sniffer is set up correctly
if [ ! -x $PHPCS_BIN ]; then
    echo "PHP CodeSniffer bin not found or executable -> $PHPCS_BIN"
    exit 1
fi

# simple check if code sniffer is set up correctly
if [ ! -x $PHPCS_BIN ]; then
    echo "PHP CodeSniffer bin not found or executable -> $PHPCS_BIN"
    exit 1
fi

# if changed lines is active we require diffilter
if [ "$PHPCS_CHANGED_LINES_ONLY" == "1" ]; then
    if [ ! -x $DIFFFILTER_BIN ]; then
        echo "Difffilter bin not found or executable -> $DIFFFILTER_BIN"
        exit 1
    fi
fi

# make sure we have a valid HEAD
COMMIT_HASH=$(git rev-parse --verify HEAD)
if [ "$COMMIT_HASH" != "" ]; then
    against=HEAD
else
    echo "Invalid HEAD revision. Exiting."
    exit 1
fi

# this is the magic:
# retrieve all files in staging area that are added, modified or renamed
# but no deletions etc

git diff-index --name-only --cached --diff-filter=ACMR $against -- > $TMP_DIFF_FILES

# if file is empty (means no changes) exit successfully
if [ ! -s $TMP_DIFF_FILES ]; then
    rm -f $TMP_DIFF_FILES
    exit 0
fi

BEHAT_FAILED=0
if [ "$BEHAT_RUN_LINT" == "1" ] && [ -e $GHERKIN_LINT_PATH ] && [ -e $GHERKIN_LINT_CONFIG ]; then
    BEHAT_FILES=$(git diff-index --name-only --cached --diff-filter=ACMR HEAD -- "*.feature")
    for BEHAT_FILE in $BEHAT_FILES
    do
        $GHERKIN_LINT_PATH -c $GHERKIN_LINT_CONFIG $BEHAT_FILE
        RETVAL=$?

        if [ $RETVAL -ne 0 ]; then
            BEHAT_FAILED=1
        fi
    done
fi

# Set the params for Code Sniffer

if [ "$PHPCS_IGNORE" != "" ]; then
    IGNORE="--ignore=$PHPCS_IGNORE"
else
    IGNORE=""
fi

if [ "$PHPCS_SNIFFS" != "" ]; then
    SNIFFS="--sniffs=$PHPCS_SNIFFS"
else
    SNIFFS=""
fi

if [ "$PHPCS_ENCODING" != "" ]; then
    ENCODING="--encoding=$PHPCS_ENCODING"
else
    ENCODING=""
fi

if [ "$PHPCS_IGNORE_WARNINGS" == "1" ]; then
    IGNORE_WARNINGS="-n"
else
    IGNORE_WARNINGS=""
fi

if [ "$PHPCS_FILE_EXTENSIONS" != "" ]; then
    EXTENSIONS="$PHPCS_FILE_EXTENSIONS"
else
    EXTENSIONS=""
fi

PHPCS_COMMAND="$PHPCS_BIN -s $IGNORE_WARNINGS $REPORT --extensions=$EXTENSIONS --standard=$PHPCS_CODING_STANDARD $ENCODING $IGNORE $SNIFFS --file-list=$TMP_DIFF_FILES"

if [ "$PHPCS_CHANGED_LINES_ONLY" == "1" ]; then
    git diff HEAD > $TMP_DIFF
    JSON=$($PHPCS_COMMAND --report=json)
    echo $JSON > $TMP_JSON
    RETVAL=$?

    if [ $RETVAL -ne 0 ]; then
        echo "Error writing phpcs json report to temp dir"
        rm -f $TMP_DIFF_FILES
        rm -f $TMP_DIFF
        exit 1
    fi

    # diffFilter gives us only the violations for the changed lines
    OUTPUT="$($DIFFFILTER_BIN --phpcs $TMP_DIFF $TMP_JSON)"
else
    # This will give us the violations for the complete content of the changed files
    OUTPUT=$($PHPCS_COMMAND)
fi

RETVAL=$?

rm -f $TMP_DIFF_FILES
rm -f $TMP_DIFF
rm -f $TMP_JSON

if [ $RETVAL -ne 0 ]; then
    echo "$OUTPUT"
fi

if [ $BEHAT_FAILED == 1 ]; then
    exit 1
fi

exit $RETVAL
