#!/usr/bin/env bash
# wait-for-bridge — poll the CDP bridge until a route target is registered.
#
# Calls cdp-bridge.cjs status in a poll loop. Emits structured diagnostics on
# each probe failure: Metro reachability, React Native target visibility, bundle
# progress. Fails with non-zero if the bridge is not ready within the poll limit.
#
# Inputs:
#   --target <metamask-mobile dir>  (default $PWD)
#   --port <number>                 (default WATCHER_PORT env, else METRO_PORT, else 8081)
#   --max-polls <n>                 (default MOBILE_BRIDGE_READY_POLLS env, else 90; 2s each = 180s)
#   --platform ios|android          bind the required platform: the answering target
#                                   must belong to it, regardless of ambient device env
# Outputs:
#   Progress on stderr; exit 0 bridge ready; 1 timeout.
#
# Single device op: wait for bridge readiness. Does not start Metro or open the dev client.
set -uo pipefail

TARGET="$PWD"
PORT="${WATCHER_PORT:-${METRO_PORT:-8081}}"
MAX_POLLS="${MOBILE_BRIDGE_READY_POLLS:-90}"
REQUIRE_PLATFORM=""

while [ "$#" -gt 0 ]; do
  case "$1" in
    --target)     [ "$#" -ge 2 ] || { echo "Missing value for --target" >&2; exit 2; }; TARGET="$2"; shift 2 ;;
    --port)       [ "$#" -ge 2 ] || { echo "Missing value for --port" >&2; exit 2; }; PORT="$2"; shift 2 ;;
    --max-polls)  [ "$#" -ge 2 ] || { echo "Missing value for --max-polls" >&2; exit 2; }; MAX_POLLS="$2"; shift 2 ;;
    --platform)   [ "$#" -ge 2 ] || { echo "Missing value for --platform" >&2; exit 2; }; REQUIRE_PLATFORM="$2"; shift 2 ;;
    -h|--help)
      printf 'Usage: wait-for-bridge.sh [--target <dir>] [--port <n>] [--max-polls <n>] [--platform ios|android]\n'
      exit 0
      ;;
    *) printf 'wait-for-bridge: unknown arg: %s\n' "$1" >&2; exit 2 ;;
  esac
done

case "$MAX_POLLS" in
  ''|*[!0-9]*) echo "wait-for-bridge: --max-polls must be an integer of at least 2." >&2; exit 2 ;;
esac
[ "$MAX_POLLS" -ge 2 ] || { echo "wait-for-bridge: --max-polls must be at least 2 for stable readiness." >&2; exit 2; }

TARGET="$(cd "$TARGET" && pwd -P)"

# The matcher reads the required platform from the environment so the value survives
# into the node subprocess that runs the shared matcher against the status output.
export WAIT_FOR_BRIDGE_PLATFORM="$REQUIRE_PLATFORM"

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
. "$SCRIPT_DIR/../shared/harness-path.sh"
if ! command -v recipe_runtime_dir >/dev/null 2>&1; then
  echo "wait-for-bridge: shared lib adapters/shared/harness-path.sh not found; reinstall the runner." >&2
  exit 1
fi
LOG_DIR="$TARGET/$(recipe_runtime_dir)" || exit 1
mkdir -p "$LOG_DIR"

BRIDGE_CJS="$(dirname "$0")/bridge-runtime/cdp-bridge.cjs"
MATCH_LIB="$(dirname "$0")/bridge-runtime/lib/match-bridge-target.cjs"
STATUS_LOG="$LOG_DIR/bridge-status.log"
METRO_LOG="$LOG_DIR/metro.log"

if [ ! -f "$BRIDGE_CJS" ]; then
  printf 'wait-for-bridge: cdp-bridge.cjs not found at %s\n' "$BRIDGE_CJS" >&2
  exit 1
fi

metro_ready() {
  curl -sf --max-time 2 "http://localhost:${PORT}/status" 2>/dev/null | grep -q 'packager-status:running'
}

bundle_progress() {
  local log="$1"
  [ -f "$log" ] || return 0
  local p
  p="$(grep -E '^[[:space:]]*(iOS|Android).*index\.(js|tsx?).*%|Bundl(ed|ing)|Finished' "$log" 2>/dev/null | tail -1 | tr -d '\r' || true)"
  [ -n "$p" ] && printf ' (%s)' "$p"
}

bridge_wait_reason() {
  if ! metro_ready; then
    printf 'Metro not reachable on port %s' "$PORT"
    return
  fi
  local targets
  targets="$(curl -sf --max-time 2 "http://localhost:${PORT}/json/list" 2>/dev/null || true)"
  if [ "$targets" = "[]" ] || [ -z "$targets" ]; then
    printf 'Metro running; no React Native debug target yet'
    bundle_progress "$METRO_LOG"
    return
  fi
  if [ -s "$STATUS_LOG" ] && grep -qxF '[]' "$STATUS_LOG"; then
    printf 'React Native target visible; in-app bridge not ready yet'
    bundle_progress "$METRO_LOG"
    return
  fi
  printf 'bridge probe failed'
  bundle_progress "$METRO_LOG"
}

# Matcher is the shared boundary-safe module (match-bridge-target.cjs), so the
# launch ready-path confirm and this poll enforce identical platform/device rules.
bridge_has_route() {
  (cd "$TARGET" && APP_ROOT="$TARGET" node "$BRIDGE_CJS" status > "$STATUS_LOG" 2>&1) \
  && node -e '
const { hasMatchingRoute } = require(process.argv[1]);
const fs = require("fs");
try {
  const value = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
  process.exit(hasMatchingRoute(value) ? 0 : 1);
} catch { process.exit(1); }
' "$MATCH_LIB" "$STATUS_LOG"
}

# The device/platform this run is pinned to — the bridge target must belong to it.
requested_target() {
  node -e 'process.stdout.write(require(process.argv[1]).describeRequested())' "$MATCH_LIB" 2>/dev/null \
    || printf 'any platform'
}

# Summarise the targets that DID answer (platform / deviceName), so a timeout teaches
# what responded vs what was requested — e.g. an iOS target answering an android launch.
answered_targets() {
  [ -s "$STATUS_LOG" ] || { printf 'none'; return; }
  node -e '
const { describeTargets } = require(process.argv[1]);
const fs = require("fs");
try {
  process.stdout.write(describeTargets(JSON.parse(fs.readFileSync(process.argv[2], "utf8"))));
} catch { process.stdout.write("none"); }
' "$MATCH_LIB" "$STATUS_LOG" 2>/dev/null || printf 'none'
}

READY_STREAK=0
for ATTEMPT in $(seq 1 "$MAX_POLLS"); do
  if bridge_has_route; then
    READY_STREAK=$((READY_STREAK + 1))
    # The bridge can appear briefly while React Native is still completing its
    # startup navigation/reload. Require two consecutive healthy snapshots so a
    # following command never inherits that transient gap.
    if [ "$READY_STREAK" -ge 2 ]; then
      printf 'Mobile bridge ready\n' >&2
      exit 0
    fi
  else
    READY_STREAK=0
  fi
  if [ "$ATTEMPT" = "1" ] || [ $((ATTEMPT % 5)) -eq 0 ]; then
    printf 'Waiting for Mobile bridge (%s/%s): %s\n' "$ATTEMPT" "$MAX_POLLS" "$(bridge_wait_reason)" >&2
  fi
  [ "$ATTEMPT" -ge "$MAX_POLLS" ] || sleep 2
done

cat "$STATUS_LOG" >&2 || true
printf 'wait-for-bridge: no bridge target matched the requested %s on port %s (%s polls × 2s = %ss)\n' \
  "$(requested_target)" "$PORT" "$MAX_POLLS" "$((MAX_POLLS * 2))" >&2
printf '  requested: %s\n' "$(requested_target)" >&2
printf '  answered:  %s\n' "$(answered_targets)" >&2
printf '  Next: check %s — or run mm-harness status --json\n' "$STATUS_LOG" >&2
exit 1
