#!/bin/bash
set -euo pipefail
umask 077

ROOT=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
cd "$ROOT"

ENV_FILE="$ROOT/.env"
APK="$ROOT/android/dist-staging/who-need-help-staging.apk"
TEST_APK="$ROOT/android/dist-staging/who-need-help-staging-androidTest.apk"
run_id="$(date -u +%Y%m%d%H%M%S)-$$"
output_dir="$ROOT/output/android-browser-staging-e2e/$run_id"
tools_image="who-need-help:android-e2e-tools-$run_id"
browser_image="who-need-help-e2e-tests:android-$run_id"
android_image="who-need-help-android:cross-client-$run_id"
container="who-need-help-android-cross-client-$run_id"
avd_volume="who-need-help-android-avd-cross-client-$run_id"
package=org.whoneedhelp.mobile.staging
service_class=org.whoneedhelp.mobile.TrackingService
fixture_password="$(openssl rand -hex 24)"
android_message="android-$run_id"
browser_reply="browser-$run_id"
prepared=0
android_runner_pid=
android_runner_output=

if [[ ! -e /dev/kvm ]]; then
  echo "/dev/kvm is required for the containerized Android emulator." >&2
  exit 1
fi

if [[ ! -f "$ENV_FILE" ]]; then
  echo "Missing $ENV_FILE." >&2
  exit 1
fi

if [[ ! -s "$APK" ]]; then
  echo "Missing staging APK: $APK. Run scripts/android-staging-build.sh first." >&2
  exit 1
fi

if [[ ! -s "$TEST_APK" ]]; then
  echo "Missing staging test APK: $TEST_APK. Run scripts/android-staging-build.sh first." >&2
  exit 1
fi

set -a
# shellcheck source=/dev/null
. "$ENV_FILE"
set +a

if [[ "${DATABASE_MODE:-container}" != container ]]; then
  echo "This rollback-based Android/browser drill requires DATABASE_MODE=container." >&2
  exit 1
fi

: "${POSTGRES_DB:?POSTGRES_DB is missing from .env}"
: "${WNH_BASE_URL:?WNH_BASE_URL is missing from .env}"
: "${WNH_TRACKING_MIN_TIME_MS:?WNH_TRACKING_MIN_TIME_MS is missing from .env}"
: "${WNH_TRACKING_HTTP_TIMEOUT_MS:?WNH_TRACKING_HTTP_TIMEOUT_MS is missing from .env}"

case "$WNH_BASE_URL" in
  https://*/* | https://*) ;;
  *)
    echo "WNH_BASE_URL must be an HTTPS origin." >&2
    exit 1
    ;;
esac

origin_without_scheme=${WNH_BASE_URL#https://}
case "$origin_without_scheme" in
  "" | */* | *\?* | *\#* | *@*)
    echo "WNH_BASE_URL must be a credential-free HTTPS origin without a path." >&2
    exit 1
    ;;
esac
expected_host=${origin_without_scheme%%:*}

mkdir -p "$output_dir"
chmod 700 "$ROOT/output" "$ROOT/output/android-browser-staging-e2e" "$output_dir"

db_container=$(docker compose ps -q db)
if [[ -z "$db_container" ]]; then
  echo "The ordinary Compose database container is not running." >&2
  exit 1
fi

internal_network_id=$(
  docker inspect "$db_container" |
    jq -r '.[0].NetworkSettings.Networks | to_entries[] | select(.key | endswith("_internal")) | .value.NetworkID' |
    head -n 1
)

if [[ -z "$internal_network_id" ]]; then
  echo "Could not identify the ordinary Compose internal network." >&2
  exit 1
fi

snapshot_database() {
  local destination=$1

  docker compose exec -T db sh -c \
    'psql --no-psqlrc --set ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB"' \
    >"$destination" <<'SQL'
BEGIN READ ONLY;
SELECT 'users' AS table_name, count(*) AS row_count FROM users
UNION ALL SELECT 'users_tokens', count(*) FROM users_tokens
UNION ALL SELECT 'help_requests', count(*) FROM help_requests
UNION ALL SELECT 'messages', count(*) FROM messages
UNION ALL SELECT 'help_assignments', count(*) FROM help_assignments
UNION ALL SELECT 'reports', count(*) FROM reports
UNION ALL SELECT 'reviews', count(*) FROM reviews
UNION ALL SELECT 'audit_events', count(*) FROM audit_events
UNION ALL SELECT 'abuse_signals', count(*) FROM abuse_signals
UNION ALL SELECT 'tracking_sessions', count(*) FROM tracking_sessions
UNION ALL SELECT 'tracking_positions', count(*) FROM tracking_positions
UNION ALL SELECT 'activities', count(*) FROM activities
UNION ALL SELECT 'activity_participants', count(*) FROM activity_participants
UNION ALL SELECT 'activity_messages', count(*) FROM activity_messages
UNION ALL SELECT 'category_proposals', count(*) FROM category_proposals
UNION ALL SELECT 'category_votes', count(*) FROM category_votes
UNION ALL SELECT 'blocks', count(*) FROM blocks
UNION ALL SELECT 'social_identities', count(*) FROM social_identities
UNION ALL SELECT 'rate_limit_buckets', count(*) FROM rate_limit_buckets
ORDER BY table_name;
COMMIT;
SQL
}

run_fixture_tool() {
  local action=$1

  docker run --rm \
    --network "$internal_network_id" \
    --env-file "$ENV_FILE" \
    --env APP_ROLE=migrate \
    --env "WNH_ANDROID_E2E_EXPECTED_DATABASE=$POSTGRES_DB" \
    --env WNH_ANDROID_E2E_CONFIRM=public-staging-android-e2e \
    --env "WNH_ANDROID_E2E_RUN_ID=$run_id" \
    --env "WNH_ANDROID_E2E_PASSWORD=$fixture_password" \
    --env "WNH_ANDROID_E2E_ANDROID_MESSAGE=$android_message" \
    --env "WNH_ANDROID_E2E_BROWSER_REPLY=$browser_reply" \
    --env WNH_ANDROID_E2E_MANIFEST_PATH=/output/fixture.json \
    --volume "$output_dir:/output" \
    "$tools_image" \
    mix wnh.staging_android_e2e "$action"
}

adb() {
  docker exec "$container" adb "$@"
}

run_browser_phase() {
  local phase=$1
  local phase_output="$output_dir/browser-$phase"

  mkdir -p "$phase_output"
  chmod 700 "$phase_output"

  docker run --rm \
    --network host \
    --user "$(id -u):$(id -g)" \
    --env "BASE_URL=$WNH_BASE_URL" \
    --env MAILPIT_URL=http://127.0.0.1:8027 \
    --env "CROSS_CLIENT_PHASE=$phase" \
    --env "CROSS_CLIENT_REQUESTER_EMAIL=$requester_email" \
    --env "CROSS_CLIENT_PASSWORD=$fixture_password" \
    --env "CROSS_CLIENT_REQUEST_PATH=$request_path" \
    --env "CROSS_CLIENT_ANDROID_MESSAGE=$android_message" \
    --env "CROSS_CLIENT_BROWSER_REPLY=$browser_reply" \
    --env HOME=/tmp \
    --volume "$phase_output:/work/output" \
    "$browser_image" \
    npx playwright test \
    --config=playwright.cross-client.config.ts \
    --project=chromium
}

start_android_phase() {
  local method=$1
  android_runner_output="$output_dir/android-$method.txt"

  adb shell am instrument -w -r \
    -e class \
    "org.whoneedhelp.mobile.CrossClientStagingInstrumentedTest#$method" \
    -e login_path "$helper_login_path" \
    -e request_path "$request_path" \
    -e android_message "$android_message" \
    -e browser_reply "$browser_reply" \
    org.whoneedhelp.mobile.staging.test/androidx.test.runner.AndroidJUnitRunner \
    >"$android_runner_output" 2>&1 &
  android_runner_pid=$!
}

wait_android_phase() {
  local status

  set +e
  wait "$android_runner_pid"
  status=$?
  set -e
  android_runner_pid=

  if [[ "$status" -ne 0 ]] ||
    ! grep -Eq '^OK \(1 test\)$' "$android_runner_output" ||
    grep -Eq 'FAILURES!!!|INSTRUMENTATION_FAILED|Process crashed' \
      "$android_runner_output"; then
    cat "$android_runner_output" >&2
    echo "Android cross-client instrumentation phase failed." >&2
    return 1
  fi
}

cleanup() {
  local status=$?
  trap - EXIT HUP INT TERM

  if docker inspect "$container" >/dev/null 2>&1; then
    adb logcat -d >"$output_dir/logcat.txt" 2>&1 || true
    adb shell dumpsys notification --noredact >"$output_dir/notifications.txt" 2>&1 || true
    adb shell dumpsys activity services "$package" >"$output_dir/services.txt" 2>&1 || true
    adb exec-out screencap -p >"$output_dir/final.png" 2>/dev/null || true
    docker logs "$container" >"$output_dir/emulator.log" 2>&1 || true
  fi

  if [[ "$prepared" -eq 1 ]]; then
    if ! run_fixture_tool cleanup >"$output_dir/fixture-cleanup.log" 2>&1; then
      echo "Exact Android/browser staging cleanup failed; inspect $output_dir." >&2
      status=1
    else
      snapshot_database "$output_dir/database-after.txt"
      if ! diff -u \
        "$output_dir/database-before.txt" \
        "$output_dir/database-after.txt" \
        >"$output_dir/database-cleanup.diff"; then
        echo "Android/browser cleanup did not restore application table counts." >&2
        status=1
      fi
    fi
  fi

  docker rm -f "$container" >/dev/null 2>&1 || true
  docker volume rm -f "$avd_volume" >/dev/null 2>&1 || true
  docker image rm "$android_image" "$browser_image" "$tools_image" >/dev/null 2>&1 || true

  {
    printf 'container_absent='
    if docker inspect "$container" >/dev/null 2>&1; then
      printf 'false\n'
    else
      printf 'true\n'
    fi
    printf 'volume_absent='
    if docker volume inspect "$avd_volume" >/dev/null 2>&1; then
      printf 'false\n'
    else
      printf 'true\n'
    fi
    printf 'fixture_prefix_absent='
    if [[ -f "$output_dir/database-cleanup.diff" ]] &&
      [[ ! -s "$output_dir/database-cleanup.diff" ]]; then
      printf 'true\n'
    else
      printf 'unknown\n'
    fi
  } >"$output_dir/cleanup.txt"

  unset fixture_password
  exit "$status"
}
trap cleanup EXIT HUP INT TERM

curl --fail --silent --show-error "$WNH_BASE_URL/healthz/ready" \
  >"$output_dir/public-ready.txt"
snapshot_database "$output_dir/database-before.txt"
sha256sum "$APK" >"$output_dir/apk.sha256"
sha256sum "$TEST_APK" >"$output_dir/test-apk.sha256"

docker build --target load_tools --tag "$tools_image" . \
  >"$output_dir/tools-build.log"
docker build --tag "$browser_image" e2e \
  >"$output_dir/browser-build.log"
docker build \
  --build-arg "WNH_DEBUG_BASE_URL=$WNH_BASE_URL" \
  --build-arg "WNH_TRACKING_MIN_TIME_MS=$WNH_TRACKING_MIN_TIME_MS" \
  --build-arg "WNH_TRACKING_HTTP_TIMEOUT_MS=$WNH_TRACKING_HTTP_TIMEOUT_MS" \
  --build-arg \
    "ANDROID_EMULATOR_SYSTEM_IMAGE=system-images/android-37.0/google_apis_ps16k/x86_64" \
  --target emulator \
  --tag "$android_image" \
  android >"$output_dir/android-image-build.log"

docker volume create "$avd_volume" >"$output_dir/avd-volume.txt"

docker run -d \
  --name "$container" \
  --device /dev/kvm \
  --mount \
    "type=volume,source=$avd_volume,target=/home/gradle/.android/avd/who_need_help_test.avd" \
  "$android_image" >"$output_dir/container-id.txt"

if [[ "$(docker inspect --format '{{.State.Running}}' "$container")" != true ]]; then
  docker logs "$container" >"$output_dir/emulator.log" 2>&1 || true
  echo "Android cross-client emulator exited before ADB became available; inspect $output_dir/emulator.log." >&2
  exit 1
fi

adb_ready=0
for _attempt in $(seq 1 90); do
  if [[ "$(docker inspect --format '{{.State.Running}}' "$container")" != true ]]; then
    break
  fi

  if [[ "$(adb get-state 2>/dev/null || true)" == device ]]; then
    adb_ready=1
    break
  fi

  sleep 2
done

if [[ "$adb_ready" -ne 1 ]]; then
  docker logs "$container" >"$output_dir/emulator.log" 2>&1 || true
  echo "Android cross-client emulator did not expose a ready ADB device." >&2
  exit 1
fi

booted=
for _attempt in $(seq 1 90); do
  booted=$(adb shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')
  [[ "$booted" == 1 ]] && break
  sleep 2
done

if [[ "$booted" != 1 ]]; then
  echo "Android cross-client emulator did not finish booting." >&2
  exit 1
fi

adb shell input keyevent 82
adb shell settings put global window_animation_scale 0
adb shell settings put global transition_animation_scale 0
adb shell settings put global animator_duration_scale 0
adb shell cmd location set-location-enabled true
docker cp "$APK" "$container:/tmp/who-need-help-staging.apk"
docker cp "$TEST_APK" "$container:/tmp/who-need-help-staging-androidTest.apk"
adb install -r /tmp/who-need-help-staging.apk >"$output_dir/install.txt"
adb install -r /tmp/who-need-help-staging-androidTest.apk \
  >"$output_dir/test-install.txt"
adb shell pm list instrumentation >"$output_dir/instrumentation.txt"
adb shell pm grant "$package" android.permission.ACCESS_FINE_LOCATION
adb shell pm grant "$package" android.permission.POST_NOTIFICATIONS
adb emu geo fix 30.5237 50.4504

network_ready=0
for _attempt in $(seq 1 45); do
  if adb shell ping -c 1 -W 1 "$expected_host" \
    >"$output_dir/network-current.txt" 2>&1; then
    network_ready=1
    break
  fi
  sleep 1
done

if [[ "$network_ready" -ne 1 ]]; then
  echo "The Android emulator could not resolve and reach the staging host." >&2
  exit 1
fi

run_fixture_tool prepare >"$output_dir/fixture-prepare.log"
prepared=1
docker run --rm \
  --volume "$output_dir:/output" \
  --entrypoint sh \
  "$tools_image" \
  -euc "chown $(id -u):$(id -g) /output/fixture.json; chmod 600 /output/fixture.json"

request_path=$(jq -r '.request.path' "$output_dir/fixture.json")
helper_login_path=$(jq -r '.helper_login_path' "$output_dir/fixture.json")
requester_email=$(jq -r '.users.requester.email' "$output_dir/fixture.json")

adb logcat -c
adb emu geo fix 30.5237 50.4504
start_android_phase exchangeMessageAndTrackingWithBrowser

tracking_started=0
for _attempt in $(seq 1 45); do
  if ! kill -0 "$android_runner_pid" 2>/dev/null; then
    wait_android_phase
    echo "Android instrumentation exited before foreground tracking started." >&2
    exit 1
  fi

  adb shell dumpsys notification --noredact >"$output_dir/notifications-current.txt"
  adb shell dumpsys activity services "$package" >"$output_dir/services-current.txt"
  if grep -Fq "Sharing live location" "$output_dir/notifications-current.txt" &&
    grep -Fq "$service_class" "$output_dir/services-current.txt"; then
    tracking_started=1
    break
  fi
  adb emu geo fix 30.5237 50.4504 >/dev/null
  sleep 1
done

if [[ "$tracking_started" -ne 1 ]]; then
  echo "Android native foreground tracking did not start." >&2
  exit 1
fi

run_browser_phase observe_android
wait_android_phase

tracking_stopped=0
for _attempt in $(seq 1 45); do
  adb shell dumpsys activity services "$package" >"$output_dir/services-after-stop.txt"
  if ! grep -Fq "$service_class" "$output_dir/services-after-stop.txt"; then
    tracking_stopped=1
    break
  fi
  sleep 1
done

if [[ "$tracking_stopped" -ne 1 ]]; then
  echo "Android native foreground tracking did not stop." >&2
  exit 1
fi

run_browser_phase observe_stopped
run_fixture_tool verify >"$output_dir/fixture-verify.log"
docker run --rm \
  --volume "$output_dir:/output" \
  --entrypoint sh \
  "$tools_image" \
  -euc "chown $(id -u):$(id -g) /output/fixture-verification.json; chmod 600 /output/fixture-verification.json"

adb logcat -d -s WhoNeedHelpWebView:D AndroidRuntime:E '*:S' \
  >"$output_dir/logcat-before-cleanup.txt"
if grep -Eqi \
  'Main-frame load failed|net::ERR_|ERR_CERT|SSL handshake failed|FATAL EXCEPTION' \
  "$output_dir/logcat-before-cleanup.txt"; then
  echo "Android cross-client logcat contains a load, TLS, browser, or fatal failure." >&2
  exit 1
fi

{
  printf 'run_id=%s\n' "$run_id"
  printf 'android_api=37.0\n'
  printf 'package=%s\n' "$package"
  printf 'public_origin=%s\n' "$WNH_BASE_URL"
  printf 'magic_link_login=true\n'
  printf 'android_chat_message_visible_in_browser=true\n'
  printf 'browser_chat_reply_visible_in_android=true\n'
  printf 'android_foreground_tracking_visible_in_browser=true\n'
  printf 'android_tracking_stop_visible_in_browser=true\n'
  printf 'retained_raw_position_after_stop=0\n'
  printf 'load_tls_browser_or_fatal_errors=0\n'
} >"$output_dir/summary.txt"

echo "Android/browser public staging E2E passed."
echo "Evidence: $output_dir"
