#!/usr/bin/env bash

preserve-suite-bookend-functions() {
  if type setUp &>/dev/null; then
    # shellcheck disable=SC2034
    eval "_suite_$(typeset -f setUp)"
  else
    _suite_setUp() { :; }
  fi
  if type tearDown &>/dev/null; then
    # shellcheck disable=SC2034
    eval "_suite_$(typeset -f tearDown)"
  else
    _suite_tearDown() { :; }
  fi
}

preserve-suite-bookend-functions

try() {
  output=$(mktemp -d)
  PATH="$fake_path:$PATH" "$@" > "$output/stdout" 2> "$output/stderr"
  EXIT_CODE=$?
  STDOUT=$(<"$output/stdout")
  STDERR=$(<"$output/stderr")
  export EXIT_CODE STDOUT STDERR
  rm -rf "$output"
}

setUp() {
  export TMPDIR="$PWD/.generated"
  export-repo-root
  move-to-working-directory
  create-fake-path
  _suite_setUp
}

tearDown() {
  _suite_tearDown
  [ "${_shunit_test_:=}" = '' ] && return 0

  clean-up-working-directory
  clean-up-fake-path

  if [ "${__shunit_testSuccess:?}" != 0 ]; then
    output-outputs
  fi
}

export-repo-root() {
  export REPO_ROOT="$PWD"
  while ! [[ -d $REPO_ROOT/.git ]]; do
    export REPO_ROOT=$(dirname $REPO_ROOT)
  done
}

move-to-working-directory() {
  original_dir="$PWD"
  working_dir=$(mktemp -d)
  cd $working_dir
}

create-fake-path() {
  fake_path=$(mktemp -d)
  invokations=$(mktemp -d)
}

clean-up-working-directory() {
  cd $original_dir
  rm -rf $working_dir
}

clean-up-fake-path() {
  rm -rf $fake_path $invokations
}

output-outputs() {
  (
  bold '*******Test failed********'
  echo "$(bold EXIT_CODE =) $EXIT_CODE"
  bold STDOUT    :
  echo "$STDOUT"
  bold STDERR    :
  echo "$STDERR"
  ) >&2
}

bold() {
  echo -ne "\033[1m"
  echo -ne "$@"
  echo -e "\033[0m"
}

mock() {
  command="$1"; shift
  [[ -n "$1" ]] && output="$1"; shift

  if ! [[ -f $fake_path/$command ]]; then
    echo 'echo "$@" >> '$invokations/$command > $fake_path/$command
    chmod +x $fake_path/$command
  fi

  if [[ -n "$output" ]]; then
    echo "echo '$output'" >> $fake_path/$command
  fi
}

assertCalledWithArguments() {
  if ! [[ -f $invokations/$1 ]]; then
    fail "$1 never invoked"
    return 1
  fi
  if ! assertContains "$1 not invoked with expected arguments:" "$(<$invokations/$1)" "$2"; then
    bold Actual invokations:
    cat "$invokations/$1"
    return 1
  fi
}

assertNotCalled() {
  if [[ -f $invokations/$1 ]]; then
    fail "$1 has been invoked"
    return 1
  fi
}

SHUNIT_PATH=./.generated/shunit2-2.1.8/shunit2
if [ ! -f $SHUNIT_PATH ]; then (
  mkdir -p .generated
  cd .generated
  curl -LO https://github.com/kward/shunit2/archive/v2.1.8.zip
  unzip v2.1.8.zip
)
fi

source $SHUNIT_PATH