#!/usr/bin/env bash

set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly ROOT
readonly RESULTS="${ROOT}/results"
readonly IMAGE="serein-evt-02-event-sourcing"

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 expected_exit="$2"
  shift 2

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

  mkdir -p "${directory}"
  {
    printf '%q' "$1"
    for argument in "${@:2}"; do
      printf ' %q' "${argument}"
    done
    printf '\n'
  } >"${directory}/command.txt"

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

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

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

  if [[ ${actual_exit} -ne ${expected_exit} ]]; then
    echo "FAILED: ${name} exited ${actual_exit}; expected ${expected_exit}" >&2
    return 1
  fi

  printf '%-52s exit %s as expected\n' "${name}" "${actual_exit}"
}

in_container() {
  docker run --rm \
    --volume "${RESULTS}:/experiment/results" \
    "${IMAGE}" \
    "$@"
}

evidence_case() {
  local evidence_path="$1"
  local case_name="$2"

  docker run --rm \
    --volume "${RESULTS}/${evidence_path}:/artifacts" \
    --env "ARTIFACT_DIR=/artifacts" \
    "${IMAGE}" \
    php tests/run.php "${case_name}"
}

mutant_case() {
  local mutation="$1"
  local case_name="$2"

  docker run --rm "${IMAGE}" sh -lc \
    "php mutate.php ${mutation} && php tests/run.php ${case_name}"
}

mutation_diff() {
  local mutation="$1"
  local path="$2"

  docker run --rm "${IMAGE}" sh -lc \
    "cp ${path} /tmp/original.php && php mutate.php ${mutation} >/dev/null && diff -u /tmp/original.php ${path}"
}

rm -rf \
  "${RESULTS}/analysis" \
  "${RESULTS}/behavior" \
  "${RESULTS}/diffs" \
  "${RESULTS}/environment" \
  "${RESULTS}/negative" \
  "${RESULTS}/setup" \
  "${RESULTS}/sources" \
  "${RESULTS}/verification"
mkdir -p "${RESULTS}"

capture setup/build 0 docker build --tag "${IMAGE}" .
capture environment/php 0 in_container php --version
capture environment/extensions 0 in_container php -m
capture sources/primary 0 in_container php source-evidence.php
capture analysis/inventory 0 in_container php analyze.php
capture behavior/full-suite 0 in_container php tests/run.php --all

capture behavior/current-state-control 0 \
  evidence_case behavior/current-state-control current-state-control
capture behavior/event-sourced-rehydration 0 \
  evidence_case behavior/event-sourced-rehydration event-sourced-rehydration
capture behavior/projection-rebuild 0 \
  evidence_case behavior/projection-rebuild projection-rebuild
capture behavior/mixed-schema-replay 0 \
  evidence_case behavior/mixed-schema-replay mixed-schema-replay
capture behavior/unknown-schema 0 \
  evidence_case behavior/unknown-schema unknown-schema
capture behavior/stale-writer 0 \
  evidence_case behavior/stale-writer stale-writer
capture behavior/stream-gap 0 \
  evidence_case behavior/stream-gap stream-gap
capture behavior/duplicate-observation 0 \
  evidence_case behavior/duplicate-observation duplicate-observation
capture behavior/projection-crash-resume 0 \
  evidence_case behavior/projection-crash-resume projection-crash-resume
capture behavior/projector-repair 0 \
  evidence_case behavior/projector-repair projector-repair
capture behavior/immutable-events 0 \
  evidence_case behavior/immutable-events immutable-events

capture negative/false-current-state-history 1 \
  mutant_case false-current-state-history current-state-control
capture negative/mutable-event-update 1 \
  mutant_case mutable-event-update immutable-events
capture negative/wrong-stream-order 1 \
  mutant_case wrong-stream-order event-sourced-rehydration
capture negative/missing-upcaster 1 \
  mutant_case missing-upcaster mixed-schema-replay
capture negative/accept-unknown-schema 1 \
  mutant_case accept-unknown-schema unknown-schema
capture negative/missing-expected-position 1 \
  mutant_case missing-expected-position stale-writer
capture negative/duplicate-projection 1 \
  mutant_case duplicate-projection duplicate-observation
capture negative/checkpoint-before-write 1 \
  mutant_case checkpoint-before-write projection-crash-resume
capture negative/projector-arithmetic 1 \
  mutant_case projector-arithmetic projector-repair
capture negative/replay-side-effect 1 \
  mutant_case replay-side-effect projection-rebuild

capture diffs/false-current-state-history 1 \
  mutation_diff false-current-state-history src/CurrentStateWallet.php
capture diffs/mutable-event-update 1 \
  mutation_diff mutable-event-update src/EventStore.php
capture diffs/wrong-stream-order 1 \
  mutation_diff wrong-stream-order src/EventStore.php
capture diffs/missing-upcaster 1 \
  mutation_diff missing-upcaster src/EventUpcaster.php
capture diffs/accept-unknown-schema 1 \
  mutation_diff accept-unknown-schema src/EventUpcaster.php
capture diffs/missing-expected-position 1 \
  mutation_diff missing-expected-position src/EventStore.php
capture diffs/duplicate-projection 1 \
  mutation_diff duplicate-projection src/BalanceProjection.php
capture diffs/checkpoint-before-write 1 \
  mutation_diff checkpoint-before-write src/BalanceProjection.php
capture diffs/projector-arithmetic 1 \
  mutation_diff projector-arithmetic src/BalanceProjection.php
capture diffs/replay-side-effect 1 \
  mutation_diff replay-side-effect src/BalanceProjection.php

capture verification/results 0 in_container php verify-results.php

echo "Evidence retained in ${RESULTS}"
