#!/usr/bin/env bash
# Point the installed native app at Metro (CMD+D Configure Bundler equivalent).
# Expo Dev Client needs the exp+… deep link; bare RN uses UserDefaults / SharedPreferences.
source "$(dirname "${BASH_SOURCE[0]}")/common.sh"
# shellcheck source=../docker/frontend/mobile-stack.sh
source "${PACKAGE_DIR}/docker/frontend/mobile-stack.sh"

PORT="${MOBILE_CLIENT_PORT:-8081}"
MOBILE_APP="${MOBILE_APP:-mobile-client}"
IOS_BUNDLE_ID="${IOS_BUNDLE_ID:-}"
ANDROID_PACKAGE="${ANDROID_PACKAGE:-}"

mobile_bundler_host() {
  local kind="${1:-local}"
  case "${kind}" in
    device) echo "${REACT_NATIVE_PACKAGER_HOSTNAME:-localhost}" ;;
    *)      echo "127.0.0.1" ;;
  esac
}

mobile_bundler_address() {
  echo "$(mobile_bundler_host "${1:-local}"):${PORT}"
}

mobile_bundler_http_url() {
  echo "http://$(mobile_bundler_address "${1:-local}")"
}

# Expo Dev Client URL scheme (exp+slug). Falls back empty for bare RN.
expo_dev_client_scheme() {
  local app_dir
  app_dir="$(mobile_app_dir "${MOBILE_APP}")"
  [ -n "${app_dir}" ] && [ -d "${app_dir}" ] || return 0
  python3 - "${app_dir}" <<'PY'
import json, re, sys
from pathlib import Path

app_dir = Path(sys.argv[1])
pkg_path = app_dir / "package.json"
if not pkg_path.is_file():
    sys.exit(0)
try:
    pkg = json.loads(pkg_path.read_text(encoding="utf-8"))
except (OSError, ValueError):
    sys.exit(0)
deps = {**(pkg.get("dependencies") or {}), **(pkg.get("devDependencies") or {})}
if "expo-dev-client" not in deps and "expo" not in deps:
    sys.exit(0)

# Prefer a scheme already baked into the native project.
for plist in app_dir.glob("ios/**/Info.plist"):
    try:
        text = plist.read_text(encoding="utf-8", errors="ignore")
    except OSError:
        continue
    for match in re.findall(r"<string>(exp\+[A-Za-z0-9+\-._]+)</string>", text):
        print(match)
        sys.exit(0)

def load_expo():
    for name in ("app.json", "app.config.json"):
        path = app_dir / name
        if not path.is_file():
            continue
        try:
            data = json.loads(path.read_text(encoding="utf-8"))
        except (OSError, ValueError):
            continue
        return data.get("expo") or data
    return {}

expo = load_expo()
scheme = expo.get("scheme")
if isinstance(scheme, list):
    for item in scheme:
        if isinstance(item, str) and item.startswith("exp+"):
            print(item)
            sys.exit(0)
    scheme = next((s for s in scheme if isinstance(s, str)), None)
if isinstance(scheme, str) and scheme.startswith("exp+"):
    print(scheme)
    sys.exit(0)

slug = expo.get("slug") or app_dir.name
slug = re.sub(r"[^A-Za-z0-9+\-._]", "", str(slug))
if slug:
    print(f"exp+{slug}")
PY
}

urlencode() {
  python3 -c 'import sys,urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=""))' "$1"
}

# Bare RN + Expo fallback prefs (what older Configure Bundler paths used).
write_ios_rn_prefs() {
  local udid="$1" address="$2"
  [ -n "${IOS_BUNDLE_ID}" ] || return 0
  xcrun simctl spawn "${udid}" defaults write "${IOS_BUNDLE_ID}" jsLocation -string "${address}" >/dev/null 2>&1 || true
  xcrun simctl spawn "${udid}" defaults write "${IOS_BUNDLE_ID}" RCT_jsLocation -string "${address}" >/dev/null 2>&1 || true
  xcrun simctl spawn "${udid}" defaults write "${IOS_BUNDLE_ID}" RCT_packager_scheme -string http >/dev/null 2>&1 || true
}

# Expo Dev Client: open the launcher deep link (this is what CMD+D actually drives).
open_ios_expo_bundler() {
  local udid="$1" kind="${2:-local}"
  local scheme url encoded deep
  scheme="$(expo_dev_client_scheme || true)"
  [ -n "${scheme}" ] || return 1
  url="$(mobile_bundler_http_url "${kind}")"
  encoded="$(urlencode "${url}")"
  deep="${scheme}://expo-development-client/?url=${encoded}"
  xcrun simctl openurl "${udid}" "${deep}" >/dev/null 2>&1 || return 1
  echo "[run] bundler → ${url} via ${scheme}:// (Expo Dev Client)"
}

set_ios_bundler_host() {
  local udid="$1" kind="${2:-local}"
  local address
  address="$(mobile_bundler_address "${kind}")"
  write_ios_rn_prefs "${udid}" "${address}"
  if open_ios_expo_bundler "${udid}" "${kind}"; then
    return 0
  fi
  echo "[run] bundler → ${address} (RCT_jsLocation)"
}

set_android_bundler_host() {
  local adb="$1" kind="${2:-local}"
  local address url encoded scheme pkg prefs xml
  address="$(mobile_bundler_address "${kind}")"
  url="$(mobile_bundler_http_url "${kind}")"
  pkg="${ANDROID_PACKAGE}"
  [ -n "${pkg}" ] || return 0

  prefs="${pkg}_preferences.xml"
  xml="$(
    {
      "${adb}" shell run-as "${pkg}" cat "shared_prefs/${prefs}" 2>/dev/null || true
    } | tr -d '\r' | python3 -c '
import re, sys
address = sys.argv[1]
text = sys.stdin.read().strip()
pattern = r"(<string name=\"debug_http_host\">)[^<]*(</string>)"
if re.search(pattern, text):
    text = re.sub(pattern, rf"\g<1>{address}\g<2>", text, count=1)
elif "</map>" in text:
    text = text.replace("</map>", f"  <string name=\"debug_http_host\">{address}</string>\n</map>", 1)
else:
    text = (
        "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n"
        f"<map>\n  <string name=\"debug_http_host\">{address}</string>\n</map>\n"
    )
sys.stdout.write(text if text.endswith("\n") else text + "\n")
' "${address}"
  )" || true

  if [ -n "${xml}" ]; then
    printf '%s' "${xml}" | "${adb}" shell run-as "${pkg}" sh -c "mkdir -p shared_prefs && cat > shared_prefs/${prefs}" >/dev/null 2>&1 || true
  fi

  scheme="$(expo_dev_client_scheme || true)"
  if [ -n "${scheme}" ]; then
    encoded="$(urlencode "${url}")"
    "${adb}" shell am start -a android.intent.action.VIEW \
      -d "${scheme}://expo-development-client/?url=${encoded}" >/dev/null 2>&1 \
      && echo "[run] bundler → ${url} via ${scheme}:// (Expo Dev Client)" \
      && return 0
  fi
  echo "[run] bundler → ${address} (debug_http_host)"
}

booted_udid() {
  xcrun simctl list devices | sed -n 's/.*(\([A-F0-9-]\{36\}\)) (Booted).*/\1/p' | head -1
}

resolve_sdk_bin() {
  local name="$1" dir
  if command -v "${name}" >/dev/null 2>&1; then
    command -v "${name}"
    return
  fi
  for dir in "${ANDROID_HOME:-}" "${ANDROID_SDK_ROOT:-}" "${HOME}/Library/Android/sdk"; do
    [ -n "${dir}" ] || continue
    if [ -x "${dir}/platform-tools/${name}" ]; then
      echo "${dir}/platform-tools/${name}"
      return
    fi
  done
  return 1
}

# Standalone: run-stack bundler [ios|android] [app]
configure_bundler_main() {
  local target="${1:-ios}" udid adb idkey=""
  require_docker
  ensure_env
  check_layout
  mobile_enabled || {
    echo "error: mobile is off (RUN_MOBILE=false)" >&2
    exit 1
  }
  use_mobile_app "${2:-}" || exit 1
  # The globals above were read at source time, before an app was chosen.
  PORT="${MOBILE_CLIENT_PORT:-8081}"
  IOS_BUNDLE_ID="${IOS_BUNDLE_ID:-}"
  ANDROID_PACKAGE="${ANDROID_PACKAGE:-}"
  # An extra app names its native ids after itself: MOBILE_DRIVER_IOS_BUNDLE_ID.
  [ "${MOBILE_SERVICE:-mobile-client}" = mobile-client ] \
    || idkey="$(extra_app_key "${MOBILE_APP}")_"

  case "${target}" in
    ios|simulator)
      if ! command -v xcrun >/dev/null 2>&1; then
        echo "error: Xcode / xcrun not found" >&2
        exit 1
      fi
      udid="$(booted_udid || true)"
      if [ -z "${udid}" ]; then
        echo "error: no booted iOS Simulator — open Simulator first" >&2
        exit 1
      fi
      if [ -z "${IOS_BUNDLE_ID}" ]; then
        echo "error: set ${idkey}IOS_BUNDLE_ID in run.config.toml" >&2
        exit 1
      fi
      set_ios_bundler_host "${udid}" local
      # Expo openurl already loads the app; bare RN needs an explicit launch.
      if [ -z "$(expo_dev_client_scheme || true)" ]; then
        xcrun simctl terminate "${udid}" "${IOS_BUNDLE_ID}" >/dev/null 2>&1 || true
        xcrun simctl launch "${udid}" "${IOS_BUNDLE_ID}" >/dev/null 2>&1 || true
      fi
      echo "Pointed ${IOS_BUNDLE_ID} at $(mobile_bundler_http_url local)"
      ;;
    android|emulator)
      adb="$(resolve_sdk_bin adb || true)"
      if [ -z "${adb}" ]; then
        echo "error: adb not found" >&2
        exit 1
      fi
      if [ -z "${ANDROID_PACKAGE}" ]; then
        echo "error: set ${idkey}ANDROID_PACKAGE in run.config.toml" >&2
        exit 1
      fi
      "${adb}" reverse "tcp:${PORT}" "tcp:${PORT}" >/dev/null 2>&1 || true
      set_android_bundler_host "${adb}" local
      if [ -z "$(expo_dev_client_scheme || true)" ]; then
        "${adb}" shell am force-stop "${ANDROID_PACKAGE}" >/dev/null 2>&1 || true
        "${adb}" shell monkey -p "${ANDROID_PACKAGE}" -c android.intent.category.LAUNCHER 1 >/dev/null 2>&1 || true
      fi
      echo "Pointed ${ANDROID_PACKAGE} at $(mobile_bundler_http_url local)"
      ;;
    *)
      echo "usage: ${RUN_CMD:-run-stack} bundler [ios|android] [app]" >&2
      exit 1
      ;;
  esac
}

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
  configure_bundler_main "$@"
fi
