#!/usr/bin/env bash

set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly ROOT
readonly RESULTS="${ROOT}/results"

mode="${1:-all}"

case "${mode}" in
  unit|integration|contract|e2e|all) ;;
  *)
    echo "usage: ./run.sh [unit|integration|contract|e2e|all]" >&2
    exit 64
    ;;
esac

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

cd "${ROOT}"

cleanup() {
  docker compose down --volumes >/dev/null 2>&1 || true
}

trap cleanup EXIT

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 seeded defect" >&2
    return 1
  fi

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

prepare() {
  if [[ "${mode}" == "all" ]]; then
    rm -rf \
      "${RESULTS}/unit" \
      "${RESULTS}/integration" \
      "${RESULTS}/contract" \
      "${RESULTS}/e2e" \
      "${RESULTS}/environment" \
      "${RESULTS}/setup"
  else
    rm -rf \
      "${RESULTS:?}/${mode}" \
      "${RESULTS:?}/environment" \
      "${RESULTS:?}/setup"
  fi

  mkdir -p "${RESULTS}"
  cleanup

  capture setup/build pass docker compose build app browser
  capture environment/php pass \
    docker compose run --rm --no-deps test php --version
  capture environment/laravel pass \
    docker compose run --rm --no-deps test php artisan --version
  capture environment/phpunit pass \
    docker compose run --rm --no-deps test php vendor/bin/phpunit --version
  capture environment/composer-audit pass \
    docker compose run --rm --no-deps test composer audit --locked --no-interaction
  capture environment/browser pass \
    docker compose run --rm --no-deps --entrypoint sh browser \
      -lc 'node --version && npm --version && npx playwright --version'
  capture environment/npm-audit pass \
    docker compose run --rm --no-deps --entrypoint npm browser audit
  capture environment/postgresql pass \
    docker compose run --rm --no-deps postgres postgres --version
}

run_unit() {
  capture unit/clean pass \
    docker compose run --rm --no-deps test \
      php vendor/bin/phpunit tests/Unit/AllocationPolicyTest.php
  capture unit/seeded-policy-defect fail \
    docker compose run --rm --no-deps -e POLICY_DEFECT=1 test \
      php vendor/bin/phpunit tests/Unit/AllocationPolicyTest.php
}

run_integration() {
  capture integration/services pass docker compose up -d --wait postgres
  capture integration/clean-migration pass \
    docker compose run --rm test php artisan migrate:fresh --force
  capture integration/clean pass \
    docker compose run --rm test php vendor/bin/phpunit --testsuite=Integration
  capture integration/seeded-migration pass \
    docker compose run --rm -e DB_DEFECT=1 test \
      php artisan migrate:fresh --force
  capture integration/in-memory-negative-control pass \
    docker compose run --rm test php vendor/bin/phpunit \
      tests/Unit/InMemoryReservationRepositoryTest.php
  capture integration/seeded-database-defect fail \
    docker compose run --rm test php vendor/bin/phpunit --testsuite=Integration
}

run_contract() {
  capture contract/clean-services pass \
    docker compose up -d --wait provider
  capture contract/clean pass \
    docker compose run --rm test php vendor/bin/phpunit --testsuite=Contract
  capture contract/seeded-provider pass \
    env PROVIDER_DEFECT=1 docker compose up -d --force-recreate provider
  capture contract/stub-negative-control pass \
    env PROVIDER_DEFECT=1 docker compose run --rm test php vendor/bin/phpunit \
      tests/Unit/ConsumerWithStubTest.php
  capture contract/seeded-provider-defect fail \
    env PROVIDER_DEFECT=1 docker compose run --rm test \
      php vendor/bin/phpunit --testsuite=Contract
}

run_e2e() {
  capture e2e/clean-services pass \
    docker compose up -d --wait postgres app
  capture e2e/clean-migration pass \
    docker compose run --rm test php artisan migrate:fresh --force
  capture e2e/clean pass docker compose run --rm browser
  capture e2e/seeded-app pass \
    env BROWSER_DEFECT=1 docker compose up -d --force-recreate app
  capture e2e/seeded-migration pass \
    env BROWSER_DEFECT=1 docker compose run --rm test \
      php artisan migrate:fresh --force
  capture e2e/http-negative-control pass \
    env BROWSER_DEFECT=1 docker compose run --rm test \
      php vendor/bin/phpunit --testsuite=HTTP
  capture e2e/seeded-migration-after-control pass \
    env BROWSER_DEFECT=1 docker compose run --rm test \
      php artisan migrate:fresh --force
  capture e2e/seeded-browser-defect fail \
    env BROWSER_DEFECT=1 docker compose run --rm browser
}

prepare

case "${mode}" in
  unit) run_unit ;;
  integration) run_integration ;;
  contract) run_contract ;;
  e2e) run_e2e ;;
  all)
    run_unit
    run_integration
    cleanup
    run_contract
    cleanup
    run_e2e
    ;;
esac

echo "Evidence retained in ${RESULTS}"
