#!/usr/bin/env bash

set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly ROOT
readonly RESULTS="${ROOT}/results"
readonly IMAGE="serein-test-03-php"

mode="${1:-all}"

case "${mode}" in
  clean|reorder|serializer|all) ;;
  *)
    echo "usage: ./run.sh [clean|reorder|serializer|all]" >&2
    exit 64
    ;;
esac

if [[ "${RESULTS}" != "${ROOT}/results" ]]; then
  echo "refusing to clear an unexpected results directory" >&2
  exit 65
fi

cd "${ROOT}"

milliseconds() {
  perl -MTime::HiRes=time -e 'printf "%.0f\n", time * 1000'
}

capture() {
  local name="$1"
  local expectation="$2"
  shift 2

  local directory="${RESULTS}/${name}"
  local started
  local finished
  local exit_code

  mkdir -p "${directory}"
  printf '%q ' "$@" >"${directory}/command.txt"
  printf '\n' >>"${directory}/command.txt"

  started="$(milliseconds)"
  set +e
  "$@" >"${directory}/output.txt.tmp" 2>&1
  exit_code=$?
  set -e
  finished="$(milliseconds)"

  sed -E 's/[[:space:]]+$//' \
    "${directory}/output.txt.tmp" >"${directory}/output.txt"
  rm "${directory}/output.txt.tmp"

  {
    printf 'expectation=%s\n' "${expectation}"
    printf 'exit_code=%s\n' "${exit_code}"
    printf 'duration_ms=%s\n' "$((finished - started))"
  } >"${directory}/metadata.txt"

  if [[ "${expectation}" == "pass" && ${exit_code} -ne 0 ]]; then
    echo "FAILED: ${name} should have passed" >&2
    return 1
  fi

  if [[ "${expectation}" == "fail" && ${exit_code} -eq 0 ]]; then
    echo "FAILED: ${name} should have detected the counterfactual" >&2
    return 1
  fi

  printf '%-44s %s\n' "${name}" "expected ${expectation} (exit ${exit_code})"
}

prepare() {
  if [[ "${mode}" == "all" ]]; then
    rm -rf \
      "${RESULTS}/clean" \
      "${RESULTS}/reorder" \
      "${RESULTS}/serializer" \
      "${RESULTS}/environment" \
      "${RESULTS}/setup"
  else
    rm -rf \
      "${RESULTS:?}/${mode}" \
      "${RESULTS:?}/environment" \
      "${RESULTS:?}/setup"
  fi

  mkdir -p "${RESULTS}"

  capture setup/build pass docker build --tag "${IMAGE}" .
  capture environment/php pass \
    docker run --rm "${IMAGE}" php --version
  capture environment/packages pass \
    docker run --rm "${IMAGE}" php bin/versions.php
  capture environment/composer-audit pass \
    docker run --rm "${IMAGE}" \
      composer audit --locked --no-interaction
}

run_clean() {
  capture clean/quote pass \
    docker run --rm "${IMAGE}" php bin/quote.php
  capture clean/fortress pass \
    docker run --rm "${IMAGE}" \
      php vendor/bin/phpunit --testsuite=Fortress
  capture clean/collaboration pass \
    docker run --rm "${IMAGE}" \
      php vendor/bin/phpunit --testsuite=Collaboration
}

run_reorder() {
  capture reorder/baseline-quote pass \
    docker run --rm "${IMAGE}" php bin/quote.php
  capture reorder/reordered-quote pass \
    docker run --rm -e REORDER_POLICIES=1 "${IMAGE}" php bin/quote.php
  capture reorder/output-equality pass \
    cmp \
      "${RESULTS}/reorder/baseline-quote/output.txt" \
      "${RESULTS}/reorder/reordered-quote/output.txt"
  capture reorder/fortress-call-script fail \
    docker run --rm -e REORDER_POLICIES=1 "${IMAGE}" \
      php vendor/bin/phpunit --testsuite=Fortress
  capture reorder/collaboration-outcome pass \
    docker run --rm -e REORDER_POLICIES=1 "${IMAGE}" \
      php vendor/bin/phpunit --testsuite=Collaboration
}

run_serializer() {
  capture serializer/baseline-quote pass \
    docker run --rm "${IMAGE}" php bin/quote.php
  capture serializer/defective-quote pass \
    docker run --rm -e SERIALIZER_DEFECT=1 "${IMAGE}" php bin/quote.php
  capture serializer/output-difference fail \
    cmp \
      "${RESULTS}/serializer/baseline-quote/output.txt" \
      "${RESULTS}/serializer/defective-quote/output.txt"
  capture serializer/fortress-with-mocked-output pass \
    docker run --rm -e SERIALIZER_DEFECT=1 "${IMAGE}" \
      php vendor/bin/phpunit --testsuite=Fortress
  capture serializer/collaboration-with-real-output fail \
    docker run --rm -e SERIALIZER_DEFECT=1 "${IMAGE}" \
      php vendor/bin/phpunit --testsuite=Collaboration
}

prepare

case "${mode}" in
  clean) run_clean ;;
  reorder) run_reorder ;;
  serializer) run_serializer ;;
  all)
    run_clean
    run_reorder
    run_serializer
    ;;
esac

echo "Evidence retained in ${RESULTS}"
