#!/usr/bin/env bash

set -eo pipefail

# https://stackoverflow.com/a/72183258/134409
# this hangs in CI (no tty?)
# yes 'will cite' | parallel --citation 2>/dev/null 1>/dev/null || true
if [ -n "${HOME:-}" ] && [ -d "$HOME" ]; then
  mkdir -p "$HOME/.parallel"
  touch "$HOME/.parallel/will-cite"
fi

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

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


# 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 git_ls_files
git_ls_files="$(git ls-files)"
export git_ls_nonbinary_files
git_ls_nonbinary_files="$(echo "$git_ls_files" | xargs file --mime | grep -v "; charset=binary" | cut -d: -f1)"

export git_ls_nonbinary_files
git_ls_nonbinary_files="$(echo "$git_ls_files" | xargs file --mime | grep -v "; charset=binary" | cut -d: -f1)"

function check_nix() {
  set -eo pipefail

  # shellcheck disable=SC2046
  nixpkgs-fmt --check $(echo "$git_ls_nonbinary_files" | grep -E '.*\.nix$') 2> >(grep -v "0 / 7 would have been reformatted")
}
export -f check_nix


function check_cargo_fmt() {
  set -eo pipefail

  # Note: avoid `cargo fmt --all` so we don't need extra stuff in `ci` shell
  # so that CI is faster
  # shellcheck disable=SC2046
  cargo fmt --all --check
}
export -f check_cargo_fmt


function check_dbg() {
  set -eo pipefail

  errors=""
  for path in $(echo "$git_ls_nonbinary_files" | 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_dbg

function check_semgrep_log() {
  set -eo pipefail

  if ! command -v semgrep > /dev/null ; then
    >&2 echo "Skipping semgrep check"
    return 0
  fi

  env SEMGREP_ENABLE_VERSION_CHECK=0 \
    semgrep -q --error --config .semgrep.log.yaml fedimint-server/ fedimint-core/
}
export -f check_semgrep_log

function check_semgrep_all() {
  set -eo pipefail

  if ! command -v semgrep > /dev/null ; then
    >&2 echo "Skipping semgrep check"
    return 0
  fi

  env SEMGREP_ENABLE_VERSION_CHECK=0 \
    semgrep -q --error --config .semgrep.all.yaml
}
export -f check_semgrep_all

function check_shellcheck() {
  set -eo pipefail

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

function check_eof() {
  set -eo pipefail

  errors=""
  for path in $(echo "$git_ls_nonbinary_files" | grep -v -E '.*\.(ods|jpg|png|log)' | grep -v -E '^db/'); 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_eof

function trailing_space_check(){
  set -eo pipefail

  for path in $(echo "$git_ls_nonbinary_files" | grep -v -E '.*\.(ods|log)' | grep -v -E '^db/'); do

    if git diff --check --cached; then
      echo "Trailing whitespace detected. Please remove them before committing."
      return 1
    fi
  done

}
export -f trailing_space_check

function check_cargo_lock() {
  set -eo pipefail

  # https://users.rust-lang.org/t/check-if-the-cargo-lock-is-up-to-date-without-building-anything/91048/5
  cargo update --workspace --locked
}
export -f check_cargo_lock

function check_typos() {
  set -eo pipefail

  if ! echo "$git_ls_nonbinary_files" | parallel typos {} ; then
    >&2 echo "Typos found: Valid new words can be added to '_typos.toml'"
    return 1
  fi
}
export -f check_typos

parallel ::: \
  check_semgrep_all \
  check_semgrep_log \
  check_nix \
  check_cargo_fmt \
  check_dbg \
  check_shellcheck \
  check_eof \
  check_cargo_lock \
  check_typos
