#!/usr/bin/env bash

set -euo pipefail

set +e
git diff-files --quiet
is_unclean=$?
set -e

# Revert `git stash` on exit
function revert_git_stash {
  >&2 echo "Unstashing uncommitted changes..."
  git stash pop -q
}

# Stash pending changes and revert them when script ends
if [ -z "${NO_STASH:-}" ] && [ $is_unclean -ne 0 ]; then
  >&2 echo "Stashing uncommitted changes..."
  GIT_LITERAL_PATHSPECS=0 git stash -q --keep-index
  trap revert_git_stash EXIT
fi

export FLAKEBOX_GIT_LS
FLAKEBOX_GIT_LS="$(git ls-files)"
export FLAKEBOX_GIT_LS_TEXT
FLAKEBOX_GIT_LS_TEXT="$(echo "$FLAKEBOX_GIT_LS" | grep -v -E "\.(png|ods|jpg|jpeg|woff2|keystore|wasm|ttf|jar|ico|gif)\$")"


function check_nothing() {
  true
}
export -f check_nothing

function check_cargo_fmt() {
    set -euo pipefail

    flakebox-in-each-cargo-workspace cargo fmt --all --check

}
export -f check_cargo_fmt

function check_cargo_lock() {
    set -euo pipefail

    # https://users.rust-lang.org/t/check-if-the-cargo-lock-is-up-to-date-without-building-anything/91048/5
    flakebox-in-each-cargo-workspace cargo update --workspace --locked |& while read -r note ; do echo "$note    (cargo)"; done

}
export -f check_cargo_lock

function check_leftover_dbg() {
    set -euo pipefail

    errors=""
    for path in $(echo "$FLAKEBOX_GIT_LS_TEXT" | grep '.*\.rs'); do
      if grep 'dbg!(' "$path" > /dev/null; then
        >&2 echo "$path contains dbg! macro"
        errors="true"
      fi
    done

    if [ -n "$errors" ]; then
      >&2 echo "Fix the problems above or use --no-verify" 1>&2
      return 1
    fi

}
export -f check_leftover_dbg

function check_semgrep() {
    set -euo pipefail

    # semgrep is not available on MacOS
    if ! command -v semgrep > /dev/null ; then
      >&2 echo "Skipping semgrep check: not available"
      return 0
    fi

    if [ ! -f .config/semgrep.yaml ] ; then
      >&2 echo "Skipping semgrep check: .config/semgrep.yaml doesn't exist"
      return 0
    fi

    if [ ! -s .config/semgrep.yaml ] ; then
      >&2 echo "Skipping semgrep check: .config/semgrep.yaml empty"
      return 0
    fi

    env SEMGREP_ENABLE_VERSION_CHECK=0 \
      semgrep -q --error --no-rewrite-rule-ids --config .config/semgrep.yaml

}
export -f check_semgrep

function check_shellcheck() {
    set -euo pipefail

    for path in $(echo "$FLAKEBOX_GIT_LS_TEXT" | grep -E '.*\.sh$'); do
      shellcheck --severity=warning "$path"
    done

}
export -f check_shellcheck

function check_trailing_newline() {
    set -euo pipefail

    errors=""
    for path in $(echo "$FLAKEBOX_GIT_LS_TEXT"); do

      # extra branches for clarity
      if [ ! -s "$path" ]; then
         # echo "$path is empty"
         true
      elif [ -z "$(tail -c 1 < "$path")" ]; then
         # echo "$path ends with a newline or with a null byte"
         true
      else
        >&2 echo "$path doesn't end with a newline" 1>&2
        errors="true"
      fi
    done

    if [ -n "$errors" ]; then
      >&2 echo "Fix the problems above or use --no-verify" 1>&2
      return 1
    fi

}
export -f check_trailing_newline

function check_trailing_whitespace() {
    set -euo pipefail

    rev="HEAD"
    if ! git rev-parse -q 1>/dev/null HEAD 2>/dev/null ; then
      >&2 echo "Warning: no commits yet, checking against --root"
      rev="--root"
    fi
    if ! git diff --check $rev ; then
      >&2 echo "Trailing whitespace detected. Please remove them before committing."
      return 1
    fi

}
export -f check_trailing_whitespace

function check_typos() {
    set -euo pipefail

    if ! echo "$FLAKEBOX_GIT_LS_TEXT" | typos --file-list - --force-exclude ; then
      >&2 echo "Typos found: Valid new words can be added to '.typos.toml'"
      return 1
    fi

}
export -f check_typos

parallel \
  --nonotice \
::: \
    check_cargo_fmt \
    check_cargo_lock \
    check_leftover_dbg \
    check_semgrep \
    check_shellcheck \
    check_trailing_newline \
    check_trailing_whitespace \
    check_typos \
  check_nothing