#!/usr/bin/env bash
# Launch the native mobile app (Expo dev client or bare React Native) against
# Metro running in Docker.
set -euo pipefail

# shellcheck source=common.sh
source "$(dirname "${BASH_SOURCE[0]}")/common.sh"
# shellcheck source=../docker/frontend/mobile-stack.sh
source "${PACKAGE_DIR}/docker/frontend/mobile-stack.sh"
# shellcheck source=sync-mobile-port.sh
source "$(dirname "${BASH_SOURCE[0]}")/sync-mobile-port.sh"
# shellcheck source=configure-bundler.sh
source "$(dirname "${BASH_SOURCE[0]}")/configure-bundler.sh"

PLATFORM="${1:?usage: open-mobile.sh ios|android|ios-device|android-device [app]}"
APP_ARG="${2:-}"
PORT="${MOBILE_CLIENT_PORT:-8081}"
APP_LABEL="${PROJECT_LABEL:-${PROJECT_NAME}}"
MOBILE_APP="${MOBILE_APP:-mobile-client}"
BACKEND_PORT="${BACKEND_PORT:-8000}"
IOS_BUNDLE_ID="${IOS_BUNDLE_ID:-}"
ANDROID_PACKAGE="${ANDROID_PACKAGE:-}"

require_docker
ensure_env
check_layout
# Before the port sync: it records what the native app was pointed at, per app.
use_mobile_app "${APP_ARG}" || exit 1
sync_mobile_app_port quiet

# Re-read after ensure_env — configure-bundler.sh may have set defaults earlier.
PORT="${MOBILE_CLIENT_PORT:-8081}"
IOS_BUNDLE_ID="${IOS_BUNDLE_ID:-}"
ANDROID_PACKAGE="${ANDROID_PACKAGE:-}"
MOBILE_APP="${MOBILE_APP:-mobile-client}"
BACKEND_PORT="${BACKEND_PORT:-8000}"
APP_LABEL="${PROJECT_LABEL:-${PROJECT_NAME}}"

first_udid() {
  local pattern="$1"
  xcrun simctl list devices available | sed -n "s/.*${pattern}.*(\\([A-F0-9-]\\{36\\}\\)).*/\\1/p" | head -1
}

ensure_host_deps() {
  if [ ! -d "${FRONTEND_DIR}/node_modules" ]; then
    echo "Host pnpm deps missing. In ${FRONTEND_DIR} run: pnpm --filter ${MOBILE_APP}... install" >&2
    exit 1
  fi
}

run_native() {
  ensure_host_deps
  echo "Building/launching the native app (first time is slow). Metro stays in Docker."
  mobile_run_in_frontend "${MOBILE_APP}" "$@"
}

run_ios_build() {
  case "$(mobile_stack_kind "${MOBILE_APP}")" in
    expo) run_native expo run:ios --no-bundler ;;
    *)    run_native react-native run-ios --no-packager ;;
  esac
  mobile_native_port_record
}

run_android_build() {
  case "$(mobile_stack_kind "${MOBILE_APP}")" in
    expo) run_native expo run:android --no-bundler ;;
    *)    run_native react-native run-android --no-packager ;;
  esac
  mobile_native_port_record
}

ios_app_installed() {
  local udid="$1"
  [ -n "${IOS_BUNDLE_ID}" ] || return 1
  xcrun simctl get_app_container "${udid}" "${IOS_BUNDLE_ID}" data >/dev/null 2>&1
}

launch_ios_app() {
  local udid="$1"
  # Terminate first so the Expo deep link (or relaunch) loads the new Metro URL.
  xcrun simctl terminate "${udid}" "${IOS_BUNDLE_ID}" >/dev/null 2>&1 || true
  set_ios_bundler_host "${udid}" local
  if [ -z "$(expo_dev_client_scheme || true)" ]; then
    xcrun simctl launch "${udid}" "${IOS_BUNDLE_ID}"
  fi
  mobile_native_port_record
}

open_ios() {
  if ! command -v xcrun >/dev/null 2>&1; then
    echo "Xcode command line tools not found. Install Xcode, then run: xcode-select --install" >&2
    exit 1
  fi

  open -a Simulator

  local udid
  udid="$(booted_udid || true)"
  if [ -z "${udid}" ]; then
    udid="$(first_udid iPhone || true)"
    if [ -z "${udid}" ]; then
      echo "No iPhone simulator available. Open Xcode → Settings → Platforms and install iOS." >&2
      exit 1
    fi
    xcrun simctl boot "${udid}"
  fi

  xcrun simctl bootstatus "${udid}" -b

  if ios_app_installed "${udid}"; then
    launch_ios_app "${udid}"
    echo "Launched ${APP_LABEL} on the iOS Simulator"
    return
  fi

  run_ios_build
  if ios_app_installed "${udid}"; then
    set_ios_bundler_host "${udid}" local
  fi
}

adb_has() {
  local adb="$1" kind="$2"
  "${adb}" devices | awk -v kind="${kind}" '
    NR>1 && $2=="device" {
      emu = ($1 ~ /^emulator/)
      if (kind=="any" || (kind=="emulator" && emu) || (kind=="physical" && !emu)) found=1
    }
    END { exit !found }
  '
}

android_app_installed() {
  local adb="$1"
  [ -n "${ANDROID_PACKAGE}" ] || return 1
  "${adb}" shell pm path "${ANDROID_PACKAGE}" >/dev/null 2>&1
}

launch_android_app() {
  local adb="$1" kind="${2:-local}"
  "${adb}" reverse "tcp:${PORT}" "tcp:${PORT}"
  "${adb}" reverse "tcp:${BACKEND_PORT}" "tcp:${BACKEND_PORT}"
  "${adb}" shell am force-stop "${ANDROID_PACKAGE}" >/dev/null 2>&1 || true
  set_android_bundler_host "${adb}" "${kind}"
  if [ -z "$(expo_dev_client_scheme || true)" ]; then
    "${adb}" shell monkey -p "${ANDROID_PACKAGE}" -c android.intent.category.LAUNCHER 1 >/dev/null
  fi
  mobile_native_port_record
}

ensure_android_emulator() {
  local adb="$1" emulator_bin avd i
  if adb_has "${adb}" emulator; then
    return
  fi
  emulator_bin="$(resolve_sdk_bin emulator || true)"
  if [ -z "${emulator_bin}" ]; then
    echo "No Android emulator running. Start one from Android Studio." >&2
    exit 1
  fi
  avd="$("${emulator_bin}" -list-avds | head -1 || true)"
  if [ -z "${avd}" ]; then
    echo "No Android Virtual Device found. Create one in Android Studio → Device Manager." >&2
    exit 1
  fi
  "${emulator_bin}" -avd "${avd}" >/dev/null 2>&1 &
  "${adb}" wait-for-device
  for i in $(seq 1 40); do
    if [ "$("${adb}" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" = "1" ]; then
      return
    fi
    sleep 2
  done
}

open_android() {
  local adb
  adb="$(resolve_sdk_bin adb || true)"
  if [ -z "${adb}" ]; then
    echo "adb not found. Install Android Studio (platform-tools)." >&2
    exit 1
  fi

  ensure_android_emulator "${adb}"

  if android_app_installed "${adb}"; then
    launch_android_app "${adb}" local
    echo "Launched ${APP_LABEL} on the Android emulator"
    return
  fi

  run_android_build
  if android_app_installed "${adb}"; then
    set_android_bundler_host "${adb}" local
  fi
}

open_ios_device() {
  case "$(mobile_stack_kind "${MOBILE_APP}")" in
    expo) run_native expo run:ios --device --no-bundler ;;
    *)    run_native react-native run-ios --device --no-packager ;;
  esac
  mobile_native_port_record
  echo "[run] on a physical iPhone, set Configure Bundler to $(mobile_bundler_address device) if Metro does not connect"
}

open_android_device() {
  local adb
  adb="$(resolve_sdk_bin adb || true)"
  if [ -n "${adb}" ] && adb_has "${adb}" physical && android_app_installed "${adb}"; then
    launch_android_app "${adb}" device
    echo "Launched ${APP_LABEL} on the USB Android device"
    return
  fi
  case "$(mobile_stack_kind "${MOBILE_APP}")" in
    expo) run_native expo run:android --device --no-bundler ;;
    *)    run_native react-native run-android --device --no-packager ;;
  esac
  mobile_native_port_record
  if [ -n "${adb}" ] && android_app_installed "${adb}"; then
    set_android_bundler_host "${adb}" device
  fi
}

case "${PLATFORM}" in
  ios) open_ios ;;
  android) open_android ;;
  ios-device) open_ios_device ;;
  android-device) open_android_device ;;
  *)
    echo "usage: open-mobile.sh ios|android|ios-device|android-device" >&2
    exit 1
    ;;
esac
