#!/usr/bin/env bash

set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly ROOT
readonly RESULTS="${ROOT}/results"
readonly IMAGE="serein-test-04-php"
readonly PROVIDER_URL="http://127.0.0.1:8080"

mode="${1:-all}"

case "${mode}" in
  clean|fake-defect|adapter-defect|all) ;;
  *)
    echo "usage: ./run.sh [clean|fake-defect|adapter-defect|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 divergence" >&2
    return 1
  fi

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

provider_test() {
  docker run --rm \
    --env PROVIDER_URL="${PROVIDER_URL}" \
    "${IMAGE}" \
    bin/test-with-provider.sh \
    php vendor/bin/phpunit \
    "$@"
}

fake_defect_test() {
  docker run --rm \
    --env PROVIDER_URL="${PROVIDER_URL}" \
    --env FAKE_DEFECT=1 \
    "${IMAGE}" \
    bin/test-with-provider.sh \
    php vendor/bin/phpunit \
    "$@"
}

adapter_defect_test() {
  docker run --rm \
    --env PROVIDER_URL="${PROVIDER_URL}" \
    --env ADAPTER_DEFECT=1 \
    "${IMAGE}" \
    bin/test-with-provider.sh \
    php vendor/bin/phpunit \
    "$@"
}

prepare() {
  if [[ "${mode}" == "all" ]]; then
    rm -rf \
      "${RESULTS}/clean" \
      "${RESULTS}/fake-defect" \
      "${RESULTS}/adapter-defect" \
      "${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/http-contract pass \
    provider_test --testsuite="HTTP contract"
  capture clean/fake-contract pass \
    provider_test --testsuite="Fake contract"
  capture clean/consumer-controls pass \
    provider_test --testsuite="Consumer controls"
}

run_fake_defect() {
  capture fake-defect/http-contract pass \
    fake_defect_test --testsuite="HTTP contract"
  capture fake-defect/fake-contract fail \
    fake_defect_test --testsuite="Fake contract"
  capture fake-defect/consumer-controls pass \
    fake_defect_test --testsuite="Consumer controls"
}

run_adapter_defect() {
  capture adapter-defect/http-contract fail \
    adapter_defect_test --testsuite="HTTP contract"
  capture adapter-defect/fake-contract pass \
    adapter_defect_test --testsuite="Fake contract"
  capture adapter-defect/consumer-controls pass \
    adapter_defect_test --testsuite="Consumer controls"
}

prepare

case "${mode}" in
  clean) run_clean ;;
  fake-defect) run_fake_defect ;;
  adapter-defect) run_adapter_defect ;;
  all)
    run_clean
    run_fake_defect
    run_adapter_defect
    ;;
esac

echo "Evidence retained in ${RESULTS}"
