#!/usr/bin/env bash
# mm-harness — the single front door for the MetaMask recipe loop.
#
# The entry is the commander-based CLI (src/mm-harness-cli.ts) that presents the
# end-state surface; it resolves node/tsx/dist the same way whether the package is
# installed as a dependency or run from a source checkout.
set -euo pipefail
INVOKED_AS="$0"
if [[ "$INVOKED_AS" == */* ]]; then
  INVOKED_AS="$(cd "$(dirname "$INVOKED_AS")" && pwd -L)/${INVOKED_AS##*/}"
else
  RESOLVED_INVOKED_AS="$(command -v "$INVOKED_AS" 2>/dev/null || true)"
  if [[ -z "$RESOLVED_INVOKED_AS" ]]; then
    echo "ERROR: Cannot resolve the invoked mm-harness executable: $INVOKED_AS" >&2
    exit 1
  fi
  INVOKED_AS="$RESOLVED_INVOKED_AS"
fi
SOURCE="${BASH_SOURCE[0]}"
while [ -L "$SOURCE" ]; do
  SOURCE_DIR="$(cd "$(dirname "$SOURCE")" && pwd -P)"
  TARGET="$(readlink "$SOURCE")"
  if [[ "$TARGET" == /* ]]; then
    SOURCE="$TARGET"
  else
    SOURCE="$SOURCE_DIR/$TARGET"
  fi
done
SCRIPT_DIR="$(cd "$(dirname "$SOURCE")" && pwd -P)"
RUNNER_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"

# Dev/prod switch (the single override point). MM_HARNESS_BIN, when set, is the
# authoritative path to a dev checkout's bin/mm-harness with active pre-release
# changes; unset resolves the installed/global mm-harness (prod). Handing the
# invocation over here, before any dependency work, lets the dev checkout fully
# own the run. The self-path guard prevents an exec loop when MM_HARNESS_BIN
# already points at this script — including via a symlink (both sides are resolved
# to their real paths before comparing).
if [ -n "${MM_HARNESS_BIN:-}" ]; then
  self_real="$SCRIPT_DIR/${SOURCE##*/}"
  # Resolve MM_HARNESS_BIN through any symlink chain so a symlink-to-self is caught.
  mm_bin_src="$MM_HARNESS_BIN"
  while [ -L "$mm_bin_src" ]; do
    mm_bin_dir="$(cd "$(dirname "$mm_bin_src")" && pwd -P)"
    mm_bin_target="$(readlink "$mm_bin_src")"
    if [[ "$mm_bin_target" == /* ]]; then
      mm_bin_src="$mm_bin_target"
    else
      mm_bin_src="$mm_bin_dir/$mm_bin_target"
    fi
  done
  bin_real_dir="$(cd "$(dirname "$mm_bin_src")" 2>/dev/null && pwd -P || true)"
  bin_real="${bin_real_dir:+$bin_real_dir/${mm_bin_src##*/}}"
  if [ "$bin_real" != "$self_real" ]; then
    if [ ! -x "$MM_HARNESS_BIN" ]; then
      echo "mm-harness: MM_HARNESS_BIN is not an executable: $MM_HARNESS_BIN" >&2
      echo "  Next: point MM_HARNESS_BIN at a checkout's bin/mm-harness, or unset it to use the global install" >&2
      exit 1
    fi
    exec "$MM_HARNESS_BIN" "$@"
  fi
fi

# Internal executable identity (not part of the public env surface): the CLI
# embeds this in emitted recovery commands so they run verbatim even when
# mm-harness is not on PATH (task-local installs). Set AFTER the MM_HARNESS_BIN
# delegation above so the final executable entered always overwrites any
# inherited/stale value with its own resolved self path.
export MM_HARNESS_EXECUTABLE="$SCRIPT_DIR/${SOURCE##*/}"
export MM_HARNESS_INVOKED_AS="$INVOKED_AS"

ENTRY_TS="$RUNNER_DIR/src/mm-harness-cli.ts"
ENTRY_DIST="$RUNNER_DIR/dist/mm-harness-cli.js"

# Published installs ship dist/ only. Teach before deps work so a broken global
# install never runs ensure-runner-deps against a partial node_modules tree.
case "$RUNNER_DIR/" in
  */node_modules/*)
    if [ ! -f "$ENTRY_DIST" ]; then
      echo "mm-harness: installed package is missing dist/mm-harness-cli.js." >&2
      echo "  Next: reinstall @deeeed/metamask-harness@latest (npm i -g @deeeed/metamask-harness@latest)" >&2
      exit 1
    fi
    ;;
esac

ENSURE_DEPS="$RUNNER_DIR/adapters/shared/ensure-runner-deps.sh"
if [ -f "$ENSURE_DEPS" ]; then
  # shellcheck disable=SC1090
  bash "$ENSURE_DEPS" "$RUNNER_DIR"
fi

find_protocol_root() {
  local start="$1"
  local dir
  dir="$(cd "$start" && pwd -P)"
  while [ "$dir" != "/" ]; do
    if [ -f "$dir/packages/recipe-harness/package.json" ] && [ -f "$dir/packages/protocol/package.json" ]; then
      printf '%s\n' "$dir"
      return 0
    fi
    if [ -f "$dir/farmslot/packages/recipe-harness/package.json" ] && [ -f "$dir/farmslot/packages/protocol/package.json" ]; then
      printf '%s\n' "$dir/farmslot"
      return 0
    fi
    dir="$(dirname "$dir")"
  done
  return 1
}

node_can_run_source_typescript() {
  # Node refuses type-stripping for files under node_modules (ERR_UNSUPPORTED_NODE_
  # MODULES_TYPE_STRIPPING). When this runner is installed as a dependency, the
  # source lives under node_modules and cannot run under plain node — a /tmp probe
  # would be a false positive. Skip the node-source path in that case and fall
  # through to dist/tsx.
  case "$RUNNER_DIR/" in
    */node_modules/*) return 1 ;;
  esac
  local probe_dir probe_file
  probe_dir="$(mktemp -d "${TMPDIR:-/tmp}/mm-harness-node-ts.XXXXXX")"
  probe_file="$probe_dir/probe.ts"
  printf 'const answer: number = 42; if (answer !== 42) process.exit(1);\n' > "$probe_file"
  if ! node --no-warnings "$probe_file" >/dev/null 2>&1; then
    rm -rf "$probe_dir"
    return 1
  fi
  rm -rf "$probe_dir"
}

# Published installs use dist. A source checkout uses dist only while it is at
# least as new as every TypeScript source it would shadow.
USE_DIST=0
if [ -f "$ENTRY_DIST" ] && [ -f "$ENTRY_TS" ]; then
  case "$RUNNER_DIR/" in
    */node_modules/*)
      USE_DIST=1
      export MM_HARNESS_RUN_MODE="dist"
      ;;
    *)
      newer_source=""
      while IFS= read -r newer_source; do
        break
      done < <(find "$RUNNER_DIR/src" -name '*.ts' -newer "$ENTRY_DIST" -print 2>/dev/null)
      if [ -n "$newer_source" ]; then
        export MM_HARNESS_RUN_MODE="src"
        echo "mm-harness: dist/ is older than src/ — running the current source checkout." >&2
        echo "  Next: npm run build to refresh dist/" >&2
      else
        USE_DIST=1
        export MM_HARNESS_RUN_MODE="dist"
      fi
      ;;
  esac
elif [ -f "$ENTRY_DIST" ]; then
  USE_DIST=1
  export MM_HARNESS_RUN_MODE="dist"
elif [ -f "$ENTRY_TS" ]; then
  export MM_HARNESS_RUN_MODE="src"
fi

if [ "$USE_DIST" -eq 1 ]; then
  exec node "$ENTRY_DIST" "$@"
fi

if [ -f "$ENTRY_TS" ]; then
  case " ${NODE_OPTIONS:-} " in
    *" --conditions=development "*) ;;
    *)
      export NODE_OPTIONS="${NODE_OPTIONS:+$NODE_OPTIONS }--conditions=development"
      ;;
  esac
fi

if [ ! -f "$ENTRY_TS" ]; then
  echo "mm-harness: no CLI entry found (expected dist/mm-harness-cli.js or src/mm-harness-cli.ts)." >&2
  echo "  Next: from a source checkout run npm run build, or install dependencies (tsx) for the dev path" >&2
  exit 1
fi

TSX_BIN="${TSX_BIN:-$RUNNER_DIR/node_modules/.bin/tsx}"
if [ ! -x "$TSX_BIN" ] && [ -x "$RUNNER_DIR/../../.bin/tsx" ]; then
  TSX_BIN="$RUNNER_DIR/../../.bin/tsx"
fi
if [ ! -x "$TSX_BIN" ]; then
  if [ -z "${FARMSLOT_ROOT:-}" ] && [ -f "$RUNNER_DIR/.farmslot-root" ]; then
    FARMSLOT_ROOT="$(cat "$RUNNER_DIR/.farmslot-root")"
  fi
  if [ -z "${FARMSLOT_ROOT:-}" ]; then
    FARMSLOT_ROOT="$(find_protocol_root "$RUNNER_DIR" || find_protocol_root "$PWD" || true)"
  fi
  if [ -n "${FARMSLOT_ROOT:-}" ]; then
    FARMSLOT_ROOT="$(cd "$FARMSLOT_ROOT" && pwd -P)"
    TSX_BIN="$FARMSLOT_ROOT/node_modules/.bin/tsx"
  fi
fi

if node_can_run_source_typescript; then
  exec node --no-warnings "$ENTRY_TS" "$@"
fi

if [ -x "$TSX_BIN" ]; then
  exec "$TSX_BIN" "$ENTRY_TS" "$@"
fi

echo "mm-harness: source checkout requires dist/mm-harness-cli.js, Node.js TypeScript type stripping, or a local tsx binary." >&2
echo "  Next: npm run build   or   yarn install (tsx devDependency)   or   set TSX_BIN for local protocol co-development" >&2
exit 1
