#!/usr/bin/env bash
#ddev-generated
## Description: Provision the proving ground - install the base recipe, apply the named fixture set, capture baseline B for that set
## Usage: ai-eval-provision [fixture...]
## Example: "ddev ai-eval-provision kb_docs canvas_home"
set -euo pipefail

APPROOT="${DDEV_APPROOT:-/var/www/html}"
AI_PG_ROOT="${AI_PG_ROOT:-${APPROOT}/recipes/ai_proving_ground}"
BASELINE_DIR="${AI_PG_BASELINE_DIR:-${APPROOT}/.ai-proving-ground}"

# Fixture set: args, else AI_PG_FIXTURES, else the default v1 set. A fixture
# short-name <name> is resolved by the shared PHP fixture resolver. The target
# owns this binding (LOCKED decision 3); here it is passed on the command line.
if [ "$#" -gt 0 ]; then
  FIXTURES=("$@")
else
  # shellcheck disable=SC2206
  FIXTURES=(${AI_PG_FIXTURES:-kb_docs canvas_home})
fi

# Set key: content hash of the composed set (base version + sorted
# fixture@version list), computed by scripts/hash.php in plain PHP so YAML
# parsing is real (milestone 3, LOCKED decision 1). Reset runs the same call.
# Abort on failure: never fall back to a naive key, or a stale baseline could
# be reused silently.
if ! SET_KEY="$(php "${AI_PG_ROOT}/scripts/hash.php" "${FIXTURES[@]}")"; then
  echo "ERROR: fixture-set hash failed for [${FIXTURES[*]}]; aborting." >&2
  exit 1
fi
if ! [[ "${SET_KEY}" =~ ^[0-9a-f]{12}$ ]]; then
  echo "ERROR: unexpected hash output '${SET_KEY}' for [${FIXTURES[*]}]; aborting." >&2
  exit 1
fi
DB_DUMP="${BASELINE_DIR}/baseline.${SET_KEY}.sql.gz"
BASELINE_REF="${AI_PG_BASELINE_REF:-ai-pg-baseline-${SET_KEY}}"

# A new tag captures HEAD; an existing baseline must satisfy the same check.
TRACKED_PATHS=("config/sync" "web/modules/custom" "recipes")
CHECK_REF=HEAD
if [ -f "${DB_DUMP}" ] && git -C "${APPROOT}" rev-parse --verify "${BASELINE_REF}" >/dev/null 2>&1; then
  CHECK_REF="${BASELINE_REF}"
fi
(cd "${APPROOT}" && bash "${AI_PG_ROOT}/scripts/check-tracked-paths.sh" PROVISION "${CHECK_REF}" "${TRACKED_PATHS[@]}")

# No-op when this exact composed set already has a baseline: both halves of
# the (git ref, DB dump) pair must exist (LOCKED decision 1). Delete the dump
# and the tag to force a recapture.
if [ -f "${DB_DUMP}" ] && git -C "${APPROOT}" rev-parse --verify "${BASELINE_REF}" >/dev/null 2>&1; then
  if ! drush php:script --script-path="${AI_PG_ROOT}/scripts" versions.php -- compare "${BASELINE_DIR}/versions.${SET_KEY}" "${FIXTURES[@]}"; then
    echo "    Remove ${DB_DUMP} and tag ${BASELINE_REF} to force recapture." >&2
    exit 1
  fi
  # Composer code beyond the recipe modules: a composer require or update since
  # capture would otherwise reuse the old baseline silently.
  set +e
  php "${AI_PG_ROOT}/scripts/composer-state.php" check "${BASELINE_DIR}/composer.${SET_KEY}" "${APPROOT}" APG_PROVISION_STALE_BASELINE
  composer_status=$?
  set -e
  if [ "${composer_status}" -eq 2 ] && [ "${AI_PG_ALLOW_STALE_BASELINE:-}" != "1" ]; then
    echo "    Composer code differs from baseline. Remove ${DB_DUMP} and tag ${BASELINE_REF} to force recapture; AI_PG_ALLOW_STALE_BASELINE=1 keeps the existing baseline." >&2
    exit 1
  elif [ "${composer_status}" -ne 0 ] && [ "${composer_status}" -ne 2 ] && [ "${composer_status}" -ne 3 ]; then
    echo "ERROR: Composer state check failed; aborting." >&2
    exit 1
  fi
  echo "OK: baseline already captured for set [${FIXTURES[*]}] (hash ${SET_KEY}); nothing to do."
  echo "    Restore it with 'ddev ai-eval-reset ${FIXTURES[*]}', or remove ${DB_DUMP} and tag ${BASELINE_REF} to force recapture."
  exit 0
fi

# NOTE: the OUTER bootstrap (DDEV + Drupal CMS + Canvas + agent) is delegated to
# one_line_installer. This command assumes DDEV is up and the recipes are on disk;
# it does the Drupal-level base+fixture apply and baseline capture.

# Rebuild caches first: recipe validation checks the install list against the
# CACHED extension list, so modules dropped on disk after the last rebuild
# (the normal provisioning order) would fail as "not a known module or theme".
# Found live on the first disposable-bed run, 2026-07-18.
echo "==> Rebuilding cache (pick up modules newly on disk)"
drush cr

echo "==> Applying base recipe: ${AI_PG_ROOT}"
drush recipe "${AI_PG_ROOT}"

for f in "${FIXTURES[@]}"; do
  fdir="$(drush php:script --script-path="${AI_PG_ROOT}/scripts" fixture-dir.php -- "${f}")"
  if [ ! -f "${fdir}/recipe.yml" ]; then
    echo "ERROR: fixture '${f}' not found at ${fdir}" >&2
    exit 1
  fi
  echo "==> Applying fixture: ${f} (${fdir})"
  drush recipe "${fdir}"
done

echo "==> Rebuilding cache"
drush cr

echo "==> Capturing baseline B for set [${FIXTURES[*]}] (hash ${SET_KEY})"
mkdir -p "${BASELINE_DIR}"
drush php:script --script-path="${AI_PG_ROOT}/scripts" grader-tree.php -- record "${BASELINE_DIR}/grader.${SET_KEY}"
drush php:script --script-path="${AI_PG_ROOT}/scripts" versions.php -- record "${BASELINE_DIR}/versions.${SET_KEY}" "${FIXTURES[@]}"
php "${AI_PG_ROOT}/scripts/composer-state.php" record "${BASELINE_DIR}/composer.${SET_KEY}" "${APPROOT}"
# Custom code folders that git does not track become scratch paths: reset
# removes what an attempt adds there. Also records settings*.php and a file
# manifest of the installed packages.
SCRATCH_PATHS=()
for p in "web/modules/custom" "web/themes/custom"; do
  if [ -z "$(git -C "${APPROOT}" ls-tree -r --name-only HEAD -- "${p}" 2>/dev/null)" ]; then
    SCRATCH_PATHS+=("${p}")
  fi
done
php "${AI_PG_ROOT}/scripts/site-files.php" record "${BASELINE_DIR}/site.${SET_KEY}" "${APPROOT}" "${SCRATCH_PATHS[@]}"
drush php:script --script-path="${AI_PG_ROOT}/scripts" public-files.php -- record "${BASELINE_DIR}/files.${SET_KEY}"
drush sql:dump --gzip --result-file="${DB_DUMP%.gz}"
if git -C "${APPROOT}" rev-parse --git-dir >/dev/null 2>&1; then
  git -C "${APPROOT}" tag -f "${BASELINE_REF}" >/dev/null
  echo "    git baseline ref: ${BASELINE_REF}"
else
  echo "    (codebase is not a git repo here; set AI_PG_BASELINE_REF / init git for file-based reset)"
fi
echo "OK: proving ground provisioned for set [${FIXTURES[*]}] (hash ${SET_KEY}); baseline B captured at ${DB_DUMP}"
