#!/usr/bin/env bash

set -euo pipefail

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

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_test() {
  local evidence_path="$1"
  local filter="$2"

  docker run --rm \
    --volume "${RESULTS}/${evidence_path}:/artifacts" \
    --env "ARTIFACT_DIR=/artifacts" \
    "${IMAGE}" \
    php vendor/bin/phpunit --filter "${filter}"
}

mutant_test() {
  local mutation="$1"
  local filter="$2"

  docker run --rm "${IMAGE}" sh -lc \
    "php mutate.php ${mutation} && php vendor/bin/phpunit --filter ${filter}"
}

mutant_analysis() {
  local mutation="$1"

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

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/composer-version 0 in_container composer --version
capture environment/packages 0 in_container composer show --locked --direct --no-ansi
capture environment/composer-validate 0 in_container composer validate --no-ansi
capture environment/composer-audit 0 \
  in_container composer audit --locked --no-ansi --no-interaction
capture sources/laravel 0 in_container php source-evidence.php
capture analysis/inventory 0 in_container php analyze.php
capture behavior/full-suite 0 \
  in_container php vendor/bin/phpunit --testdox

capture behavior/direct-prerequisite-accepts 0 \
  evidence_test behavior/direct-prerequisite-accepts \
  direct_prerequisite_accepts_before_the_fact_exists
capture behavior/direct-prerequisite-refuses 0 \
  evidence_test behavior/direct-prerequisite-refuses \
  direct_prerequisite_refuses_before_any_fact_or_write
capture behavior/inside-transaction-success 0 \
  evidence_test behavior/inside-transaction-success \
  two_explicit_subscribers_run_inside_one_transaction
capture behavior/inside-transaction-failure 0 \
  evidence_test behavior/inside-transaction-failure \
  inside_transaction_listener_failure_rolls_everything_back
capture behavior/after-commit-success 0 \
  evidence_test behavior/after-commit-success \
  after_commit_subscribers_observe_a_committed_order
capture behavior/after-commit-failure 0 \
  evidence_test behavior/after-commit-failure \
  after_commit_listener_failure_leaves_order_and_reports_gap
capture behavior/aggregate-refusal 0 \
  evidence_test behavior/aggregate-refusal \
  refused_aggregate_transition_records_no_fact
capture behavior/no-subscribers 0 \
  evidence_test behavior/no-subscribers \
  an_event_with_no_subscribers_does_not_change_order_behavior
capture behavior/duplicate-publication 0 \
  evidence_test behavior/duplicate-publication \
  duplicate_publication_has_reaction_specific_semantics

capture negative/record-before-validation 1 \
  mutant_test record-before-validation \
  refused_aggregate_transition_records_no_fact
capture negative/prerequisite-event 1 \
  mutant_test prerequisite-event \
  direct_prerequisite_refuses_before_any_fact_or_write
capture negative/missing-subscriber 1 \
  mutant_test missing-subscriber \
  two_explicit_subscribers_run_inside_one_transaction
capture negative/required-action-hidden 1 \
  mutant_test required-action-hidden \
  direct_prerequisite_accepts_before_the_fact_exists
capture negative/dispatch-before-save 1 \
  mutant_test dispatch-before-save \
  two_explicit_subscribers_run_inside_one_transaction
capture negative/inside-to-after-commit 1 \
  mutant_test inside-to-after-commit \
  inside_transaction_listener_failure_rolls_everything_back
capture negative/after-commit-to-inside 1 \
  mutant_test after-commit-to-inside \
  after_commit_listener_failure_leaves_order_and_reports_gap
capture negative/remove-activity-idempotency 2 \
  mutant_test remove-activity-idempotency \
  duplicate_publication_has_reaction_specific_semantics
capture negative/domain-dispatcher-import 1 \
  mutant_analysis domain-dispatcher-import

capture diffs/record-before-validation 1 \
  mutation_diff record-before-validation app/Domain/Order.php
capture diffs/prerequisite-event 1 \
  mutation_diff prerequisite-event app/Application/PlaceOrderHandler.php
capture diffs/missing-subscriber 1 \
  mutation_diff missing-subscriber app/Providers/DomainEventServiceProvider.php
capture diffs/required-action-hidden 1 \
  mutation_diff required-action-hidden app/Application/PlaceOrderHandler.php
capture diffs/dispatch-before-save 1 \
  mutation_diff dispatch-before-save app/Application/PlaceOrderHandler.php
capture diffs/inside-to-after-commit 1 \
  mutation_diff inside-to-after-commit app/Application/PlaceOrderHandler.php
capture diffs/after-commit-to-inside 1 \
  mutation_diff after-commit-to-inside app/Application/PlaceOrderHandler.php
capture diffs/remove-activity-idempotency 1 \
  mutation_diff remove-activity-idempotency app/Listeners/RecordOrderActivity.php
capture diffs/domain-dispatcher-import 1 \
  mutation_diff domain-dispatcher-import app/Domain/Order.php

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

echo "Evidence retained in ${RESULTS}"
