#!/bin/bash

function default_dates()
{
  case "${OSTYPE}" in
    darwin*)
      date "+%Y-%m-%d"
      ;;
    *)
      date --rfc-3339=date
      ;;
  esac
}

function default_minutes()
{
  case "${OSTYPE}" in
    darwin*)
      pad_with_zero "$( jot -r 1 0 59 )"
      ;;
    *)
      pad_with_zero "$( rand -M 60 )"
      ;;
  esac
}

function default_hours()
{
  seq -w 0 23
}

function pad_with_zero()
{
  case "${OSTYPE}" in
    darwin*)
      echo "0${1}" | sed -E "s|.+(..)|\1|g"
      ;;
    *)
      echo "0${1}" | sed "s|.\+\(..\)\$|\1|g"
      ;;
  esac
}

function get_runtime_for()
{
  local test_file="${1}"

  case "$( file -b "${test_file}" )" in
    "POSIX shell"*)
      echo "bash"
      ;;
    "Python script"*)
      echo "python3"
      ;;
    *)
      ;;
  esac
}

if ! command -v faketime >/dev/null 2>&1 ; then
  echo "timemachine requires libfaketime to be installed!"
  exit 1
fi

declare -a tests=()
declare -a dates=()
declare -a hours=()
declare -a minutes=()

# parse options/arguments
until [[ -z "${1}" ]] ; do
  case "${1}" in
    --minute|--minutes)
      shift
      minutes=( "${minutes[@]}" "$( pad_with_zero "${1}" )" )
      ;;
    --hour|--hours)
      shift
      hours=( "${hours[@]}" "$( pad_with_zero "${1}" )" )
      ;;
    --date)
      shift
      dates=( "${dates[@]}" "${1}" )
      ;;
    --fail-at-end)
      fail_at_end="yes"
      ;;
    -*)
      echo "Unknown option '${1}'"
      exit 1
      ;;
    *)
      tests=( "${tests[@]}" "${1}" )
      ;;
  esac
  shift
done

if [[ "${#tests[@]}" -eq 0 ]] ; then
  echo "No tests specified!"
  exit 1
fi

for single_test in "${tests[@]}" ; do
  if [[ ! -e "${single_test}" ]] ; then
    echo "Test '${single_test}' does not exist!"
    exit 1
  fi
done

if [[ "${#dates[@]}" -eq 0 ]] ;  then
  read -d '' -r -a dates <<< "$( default_dates )"
fi

if [[ "${#hours[@]}" -eq 0 ]] ;  then
  read -d '' -r -a hours <<< "$( default_hours )"
fi

if [[ "${#minutes[@]}" -eq 0 ]] ;  then
  read -d '' -r -a minutes <<< "$( default_minutes )"
fi

REPORT=

for date in "${dates[@]}" ; do
  for hour in "${hours[@]}" ; do
    for minute in "${minutes[@]}" ; do
      date_time="${date}T${hour}:${minute}"
      for single_test in "${tests[@]}" ; do
        runtime=$( get_runtime_for "${single_test}" )
        echo "Running test ${single_test} at ${date_time}${runtime:+" with ${runtime}"}"

        if ! faketime "${date_time}" "${runtime}" "${single_test}" ; then
          REPORT+="${REPORT+$'\n'}Test ${single_test} broke at ${date_time}!"
          [[ "${fail_at_end-"no"}" == "no" ]] && break 4
        fi
      done
    done
  done
done

if [[ -n "${REPORT}" ]] ; then
  echo "There were test failures:"
  echo -e "${REPORT}"
else
  echo "All tests passed!"
fi
