#!/usr/bin/env bash
# Android smoke test for Ferrous Solitaire.
#
# Builds (optional), installs, launches, captures logcat + screenshot, and
# fails on fatal Android log patterns. Designed as a lightweight device/emulator
# sanity check rather than a full UI automation suite.
#
# Required:
#   adb on PATH
#   Android SDK/NDK env required by scripts/build_android_apk.sh when BUILD_APK=1
#
# Optional environment:
#   BUILD_APK=1|0          Build APK before install (default: 1)
#   INSTALL_APK=1|0        Install APK before launch (default: 1)
#   RESET_ON_SIGNATURE_MISMATCH=1|0
#                           Uninstall/retry if debug signatures differ (default: 1)
#   LAUNCH_APP=1|0         Launch app before checks (default: 1)
#   FORCE_STOP=1|0         Force-stop package before launch for clean logs (default: 1)
#   CAPTURE_SCREENSHOT=1|0 Capture screenshot (default: 1)
#   ADB_SERIAL=...         Device serial to use when multiple devices are connected
#   APK_PATH=...           APK to install (default: target/debug/apk/ferrous-solitaire.apk)
#   PACKAGE=...            Android package (default: com.ferrousapp.solitaire)
#   ACTIVITY=...           Activity class (default: android.app.NativeActivity)
#   OUT_DIR=...            Artifact directory (default: target/android-smoke/<timestamp>)
#   CLEAN_OUT_DIR=1|0      Remove prior artifacts from OUT_DIR first (default: 1)
#   WAIT_SECS=...          Seconds to wait after launch (default: 5)
#   ABIS=...               Passed to build script. If unset and BUILD_APK=1,
#                           defaults to the connected device's primary ABI.
#
# AVD auto-launch (used when no device/emulator is already connected):
#   LAUNCH_AVD=1|0         Auto-launch an AVD when no device is ready (default: 1)
#   AVD_NAME=...           AVD name to launch (default: first from `emulator -list-avds`)
#   AVD_BOOT_TIMEOUT=...   Seconds to wait for the emulator to finish booting (default: 120)
#   AVD_HEADLESS=1|0       Run with -no-window -no-audio for CI/no-display environments (default: 0)
#   AVD_EXTRA_ARGS=...     Extra arguments appended verbatim to the emulator command line
#   SHUTDOWN_AVD_ON_EXIT=1|0
#                           Kill the AVD this script launched on exit (default: 1).
#                           Set to 0 to leave the emulator running after the test.
#
# Examples:
#   scripts/android_smoke.sh
#   BUILD_APK=0 scripts/android_smoke.sh
#   LAUNCH_AVD=0 scripts/android_smoke.sh            # error out if no device, never auto-launch
#   AVD_NAME=Pixel_7_API_34 scripts/android_smoke.sh
#   AVD_HEADLESS=1 scripts/android_smoke.sh          # CI / no-display
#   SHUTDOWN_AVD_ON_EXIT=0 scripts/android_smoke.sh  # keep emulator open after test
#   OUT_DIR=target/android-smoke/latest WAIT_SECS=8 scripts/android_smoke.sh
set -euo pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$REPO_ROOT"

BUILD_APK="${BUILD_APK:-1}"
INSTALL_APK="${INSTALL_APK:-1}"
RESET_ON_SIGNATURE_MISMATCH="${RESET_ON_SIGNATURE_MISMATCH:-1}"
LAUNCH_APP="${LAUNCH_APP:-1}"
FORCE_STOP="${FORCE_STOP:-1}"
CAPTURE_SCREENSHOT="${CAPTURE_SCREENSHOT:-1}"
APK_PATH="${APK_PATH:-target/debug/apk/ferrous-solitaire.apk}"
PACKAGE="${PACKAGE:-com.ferrousapp.solitaire}"
ACTIVITY="${ACTIVITY:-android.app.NativeActivity}"
WAIT_SECS="${WAIT_SECS:-5}"
OUT_DIR="${OUT_DIR:-target/android-smoke/$(date +%Y%m%d-%H%M%S)}"
CLEAN_OUT_DIR="${CLEAN_OUT_DIR:-1}"
REMOTE_SCREENSHOT="/sdcard/ferrous-solitaire-smoke.png"

LAUNCH_AVD="${LAUNCH_AVD:-1}"
AVD_NAME="${AVD_NAME:-}"
AVD_BOOT_TIMEOUT="${AVD_BOOT_TIMEOUT:-120}"
AVD_HEADLESS="${AVD_HEADLESS:-0}"
AVD_EXTRA_ARGS="${AVD_EXTRA_ARGS:-}"
SHUTDOWN_AVD_ON_EXIT="${SHUTDOWN_AVD_ON_EXIT:-1}"

ADB=(adb)
if [ -n "${ADB_SERIAL:-}" ]; then
  ADB+=( -s "$ADB_SERIAL" )
fi

# PID of any emulator we start so the EXIT trap can clean it up.
_LAUNCHED_EMULATOR_PID=""

_cleanup_emulator() {
  if [ -n "$_LAUNCHED_EMULATOR_PID" ] && [ "$SHUTDOWN_AVD_ON_EXIT" = "1" ]; then
    echo ">>> shutdown emulator (PID $_LAUNCHED_EMULATOR_PID)"
    kill "$_LAUNCHED_EMULATOR_PID" 2>/dev/null || true
  fi
}
trap _cleanup_emulator EXIT

require_cmd() {
  command -v "$1" >/dev/null 2>&1 || {
    echo "missing required command: $1" >&2
    exit 1
  }
}

mkdir -p "$OUT_DIR"
if [ "$CLEAN_OUT_DIR" = "1" ]; then
  find "$OUT_DIR" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
fi
require_cmd adb

# ---------------------------------------------------------------------------
# Device / emulator availability
# ---------------------------------------------------------------------------
DEVICE_STATE="$("${ADB[@]}" get-state 2>/dev/null || true)"
if [ "$DEVICE_STATE" != "device" ]; then
  if [ "$LAUNCH_AVD" != "1" ]; then
    adb devices > "$OUT_DIR/adb-devices.txt" 2>&1 || true
    if [ -n "${ADB_SERIAL:-}" ]; then
      echo "Android device '$ADB_SERIAL' is not connected/ready (state: ${DEVICE_STATE:-unknown})." >&2
    else
      echo "No Android device/emulator is connected and ready." >&2
    fi
    echo "Run 'adb devices' or start an emulator, then retry." >&2
    echo "Device list saved to $OUT_DIR/adb-devices.txt" >&2
    exit 1
  fi

  # --- locate emulator binary -----------------------------------------------
  # Priority: ANDROID_HOME env → PATH → common SDK install locations.
  _find_sdk_root() {
    for candidate in \
        "$HOME/Android/Sdk" \
        "$HOME/Library/Android/sdk" \
        "/opt/android-sdk" \
        "/usr/lib/android-sdk"; do
      [ -d "$candidate" ] && echo "$candidate" && return
    done
  }

  EMULATOR_BIN=""
  if [ -n "${ANDROID_HOME:-}" ] && [ -x "$ANDROID_HOME/emulator/emulator" ]; then
    EMULATOR_BIN="$ANDROID_HOME/emulator/emulator"
  elif command -v emulator >/dev/null 2>&1; then
    EMULATOR_BIN="$(command -v emulator)"
  else
    _SDK_ROOT="$(_find_sdk_root)"
    if [ -n "$_SDK_ROOT" ] && [ -x "$_SDK_ROOT/emulator/emulator" ]; then
      EMULATOR_BIN="$_SDK_ROOT/emulator/emulator"
    fi
  fi

  if [ -z "$EMULATOR_BIN" ]; then
    echo "No Android device found and 'emulator' binary is not available." >&2
    echo "  • Install the Android SDK emulator component, or" >&2
    echo "  • Set ANDROID_HOME to your SDK root, or" >&2
    echo "  • Start a device/emulator manually then retry with LAUNCH_AVD=0." >&2
    exit 1
  fi
  echo ">>> emulator binary: $EMULATOR_BIN"

  # --- select AVD -----------------------------------------------------------
  if [ -z "$AVD_NAME" ]; then
    AVD_NAME="$("$EMULATOR_BIN" -list-avds 2>/dev/null | head -n 1 | tr -d '\r')"
    if [ -z "$AVD_NAME" ]; then
      echo "No AVDs found. Create one first, for example:" >&2
      echo "  sdkmanager 'system-images;android-34;google_apis;x86_64'" >&2
      echo "  avdmanager create avd -n Pixel_7_API_34 \\" >&2
      echo "    -k 'system-images;android-34;google_apis;x86_64' --device 'pixel_7'" >&2
      exit 1
    fi
    echo ">>> auto-selected AVD: $AVD_NAME"
  fi

  # --- launch emulator -------------------------------------------------------
  EMULATOR_ARGS=( -avd "$AVD_NAME" -no-snapshot-load )
  [ "$AVD_HEADLESS" = "1" ] && EMULATOR_ARGS+=( -no-window -no-audio )
  # Split AVD_EXTRA_ARGS on whitespace only (disable glob expansion).
  set -f
  # shellcheck disable=SC2206
  [ -n "$AVD_EXTRA_ARGS" ] && EMULATOR_ARGS+=( $AVD_EXTRA_ARGS )
  set +f

  echo ">>> launch emulator: $AVD_NAME"
  "$EMULATOR_BIN" "${EMULATOR_ARGS[@]}" > "$OUT_DIR/emulator.log" 2>&1 &
  _LAUNCHED_EMULATOR_PID=$!
  echo "$_LAUNCHED_EMULATOR_PID" > "$OUT_DIR/emulator.pid"
  echo "    emulator PID: $_LAUNCHED_EMULATOR_PID"
  echo "    emulator log: $OUT_DIR/emulator.log"

  # --- wait for adb transport -----------------------------------------------
  # Poll adb get-state (≠ wait-for-device which blocks indefinitely) so we can
  # honour AVD_BOOT_TIMEOUT for the whole boot sequence.
  echo ">>> waiting for device to appear in adb (timeout: ${AVD_BOOT_TIMEOUT}s)"
  _ELAPSED=0
  while true; do
    _STATE="$("${ADB[@]}" get-state 2>/dev/null || true)"
    if [ "$_STATE" = "device" ] || [ "$_STATE" = "offline" ]; then
      break
    fi
    if [ "$_ELAPSED" -ge "$AVD_BOOT_TIMEOUT" ]; then
      echo "Device did not appear in adb within ${AVD_BOOT_TIMEOUT}s" >&2
      echo "emulator log:" >&2
      tail -20 "$OUT_DIR/emulator.log" >&2 || true
      exit 1
    fi
    sleep 3
    _ELAPSED=$(( _ELAPSED + 3 ))
    echo "    ... ${_ELAPSED}s / ${AVD_BOOT_TIMEOUT}s"
  done

  # Capture emulator serial (emulator-5554 etc.) so all subsequent adb calls
  # target the right device when ADB_SERIAL was not set by the caller.
  if [ -z "${ADB_SERIAL:-}" ]; then
    _EMU_SERIAL="$(adb devices 2>/dev/null | awk '/^emulator-/{print $1; exit}' | tr -d '\r')"
    if [ -n "$_EMU_SERIAL" ]; then
      ADB_SERIAL="$_EMU_SERIAL"
      ADB=(adb -s "$ADB_SERIAL")
      echo ">>> detected emulator serial: $ADB_SERIAL"
    fi
  fi

  # --- wait for full Android boot -------------------------------------------
  # adb get-state returning "device" means the transport is up, but the
  # Android framework may still be initialising. Poll sys.boot_completed.
  echo ">>> waiting for boot_completed (timeout: ${AVD_BOOT_TIMEOUT}s)"
  _BOOT_ELAPSED=0
  _BOOT_INTERVAL=5
  while true; do
    _BOOT="$("${ADB[@]}" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')"
    if [ "$_BOOT" = "1" ]; then
      echo ">>> emulator boot complete"
      break
    fi
    if [ "$_BOOT_ELAPSED" -ge "$AVD_BOOT_TIMEOUT" ]; then
      echo "Emulator did not finish booting within ${AVD_BOOT_TIMEOUT}s" >&2
      echo "emulator log:" >&2
      tail -20 "$OUT_DIR/emulator.log" >&2 || true
      exit 1
    fi
    sleep "$_BOOT_INTERVAL"
    _BOOT_ELAPSED=$(( _BOOT_ELAPSED + _BOOT_INTERVAL ))
    echo "    ... ${_BOOT_ELAPSED}s / ${AVD_BOOT_TIMEOUT}s (boot_completed='${_BOOT}')"
  done

  # Dismiss the lock screen so later screencap shows the app, not the keyguard.
  "${ADB[@]}" shell input keyevent 82 2>/dev/null || true

  # Final sanity check — device must be fully ready before we proceed.
  DEVICE_STATE="$("${ADB[@]}" get-state 2>/dev/null || true)"
  if [ "$DEVICE_STATE" != "device" ]; then
    echo "Emulator is running but adb state is '${DEVICE_STATE:-unknown}'." >&2
    exit 1
  fi
fi

# ---------------------------------------------------------------------------
# Device metadata
# ---------------------------------------------------------------------------
{
  echo "adb_serial=${ADB_SERIAL:-default}"
  echo "package=$PACKAGE"
  echo "activity=$ACTIVITY"
  echo "device_state=$DEVICE_STATE"
  "${ADB[@]}" shell getprop ro.product.model 2>/dev/null | tr -d '\r' | sed 's/^/product_model=/'
  "${ADB[@]}" shell getprop ro.build.version.release 2>/dev/null | tr -d '\r' | sed 's/^/android_release=/'
  "${ADB[@]}" shell getprop ro.build.version.sdk 2>/dev/null | tr -d '\r' | sed 's/^/android_sdk=/'
  "${ADB[@]}" shell wm size 2>/dev/null | tr -d '\r' | sed 's/^/wm_size=/'
  "${ADB[@]}" shell wm density 2>/dev/null | tr -d '\r' | sed 's/^/wm_density=/'
} > "$OUT_DIR/device.txt"
"${ADB[@]}" shell df -h /data > "$OUT_DIR/df-data-before.txt" 2>&1 || true

if [ "$BUILD_APK" = "1" ]; then
  if [ -z "${ABIS:-}" ]; then
    DEVICE_ABI="$("${ADB[@]}" shell getprop ro.product.cpu.abi 2>/dev/null | tr -d '\r')"
    case "$DEVICE_ABI" in
      x86_64|arm64-v8a|armeabi-v7a)
        export ABIS="$DEVICE_ABI"
        ;;
      armeabi*)
        export ABIS="armeabi-v7a"
        ;;
      *)
        echo "Could not map device ABI '$DEVICE_ABI'; using build script default ABIS." >&2
        ;;
    esac
  fi
  echo ">>> build Android APK${ABIS:+ (ABIS=$ABIS)}"
  scripts/build_android_apk.sh
fi

if [ "$INSTALL_APK" = "1" ]; then
  [ -f "$APK_PATH" ] || {
    echo "APK not found: $APK_PATH" >&2
    echo "Set APK_PATH or run with BUILD_APK=1." >&2
    exit 1
  }
  ls -lh "$APK_PATH" > "$OUT_DIR/apk.txt"
  echo ">>> install $APK_PATH"
  if ! "${ADB[@]}" install -r -d "$APK_PATH" > "$OUT_DIR/adb-install.txt" 2>&1; then
    if [ "$RESET_ON_SIGNATURE_MISMATCH" = "1" ] && grep -q "INSTALL_FAILED_UPDATE_INCOMPATIBLE" "$OUT_DIR/adb-install.txt"; then
      echo ">>> signature mismatch; uninstalling $PACKAGE and retrying install"
      "${ADB[@]}" uninstall "$PACKAGE" > "$OUT_DIR/adb-uninstall-before-retry.txt" 2>&1 || true
      if "${ADB[@]}" install -r -d "$APK_PATH" > "$OUT_DIR/adb-install-retry.txt" 2>&1; then
        cat "$OUT_DIR/adb-install-retry.txt" >> "$OUT_DIR/adb-install.txt"
      else
        cat "$OUT_DIR/adb-install.txt" >&2
        cat "$OUT_DIR/adb-install-retry.txt" >&2
        "${ADB[@]}" shell df -h /data > "$OUT_DIR/df-data-after-install-failure.txt" 2>&1 || true
        "${ADB[@]}" shell pm list packages | grep -F "$PACKAGE" > "$OUT_DIR/installed-package.txt" 2>&1 || true
        echo "APK install retry failed. Diagnostics saved in $OUT_DIR" >&2
        exit 1
      fi
    else
      cat "$OUT_DIR/adb-install.txt" >&2
      "${ADB[@]}" shell df -h /data > "$OUT_DIR/df-data-after-install-failure.txt" 2>&1 || true
      "${ADB[@]}" shell pm list packages | grep -F "$PACKAGE" > "$OUT_DIR/installed-package.txt" 2>&1 || true
      echo "APK install failed. Diagnostics saved in $OUT_DIR" >&2
      echo "If the package is already installed and you only need launch/log checks, retry with INSTALL_APK=0." >&2
      exit 1
    fi
  fi
fi

if [ "$FORCE_STOP" = "1" ]; then
  echo ">>> force-stop $PACKAGE"
  "${ADB[@]}" shell am force-stop "$PACKAGE" || true
fi

echo ">>> clear logcat"
"${ADB[@]}" logcat -c

if [ "$LAUNCH_APP" = "1" ]; then
  echo ">>> launch $PACKAGE/$ACTIVITY"
  "${ADB[@]}" shell am start -n "$PACKAGE/$ACTIVITY" > "$OUT_DIR/am-start.txt"
fi

echo ">>> wait ${WAIT_SECS}s"
sleep "$WAIT_SECS"

PID="$("${ADB[@]}" shell pidof "$PACKAGE" | tr -d '\r' || true)"
if [ -z "$PID" ]; then
  "${ADB[@]}" logcat -d > "$OUT_DIR/logcat.txt" || true
  echo "app process is not running after launch: $PACKAGE" >&2
  echo "logcat saved to $OUT_DIR/logcat.txt" >&2
  exit 1
fi
echo "$PID" > "$OUT_DIR/pid.txt"

if [ "$CAPTURE_SCREENSHOT" = "1" ]; then
  echo ">>> capture screenshot"
  "${ADB[@]}" shell screencap -p "$REMOTE_SCREENSHOT"
  "${ADB[@]}" pull "$REMOTE_SCREENSHOT" "$OUT_DIR/launch.png" >/dev/null
  "${ADB[@]}" shell rm -f "$REMOTE_SCREENSHOT" >/dev/null 2>&1 || true
fi

echo ">>> capture logcat"
"${ADB[@]}" logcat -d > "$OUT_DIR/logcat.txt"
grep -iE "panic|fatal|jni|native crash|\bANR\b|exception|error|warn|keystore|safe_area" "$OUT_DIR/logcat.txt" > "$OUT_DIR/log-summary.txt" || true
"${ADB[@]}" shell df -h /data > "$OUT_DIR/df-data-after.txt" 2>&1 || true

# Fatal patterns only. Avoid matching generic "error" because Android logs are
# noisy and many non-fatal framework lines contain that word.
if grep -iE "fatal exception|jni detected error|native crash|signal [0-9]+|ANR in|Application Not Responding|Input dispatching timed out|thread exiting with uncaught exception|panicked at" "$OUT_DIR/logcat.txt"; then
  echo "Android smoke test found fatal log output" >&2
  echo "Artifacts saved in $OUT_DIR" >&2
  exit 1
fi

echo ">>> Android smoke test passed"
echo "Artifacts saved in $OUT_DIR"
