#!/bin/bash
set -euo pipefail

usage() {
  echo "Usage: $0 chromium OUTPUT_DIR"
  echo "       $0 stock OUTPUT_DIR STOCK_WEBAPK_MAP"
  exit 2
}

[ $# -ge 2 ] || usage
MODE=$1
OUT=$2
MAP=${3:-}
: "${ADB_SERVER_SOCKET:=tcp:127.0.0.1:15037}"
export ADB_SERVER_SOCKET
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
CDP="$SCRIPT_DIR/cdp_adb.py"
mkdir -p "$OUT"

shot() { adb exec-out screencap -p > "$OUT/$1.png"; }
select_devtools_socket() {
  local package=$1 pid
  pid=$(adb shell pidof "$package" | tr -d '\r' | awk '{print $1}')
  if [ -n "$pid" ] && adb shell cat /proc/net/unix | grep -q "@chrome_devtools_remote_$pid$"; then
    export CHROME_DEVTOOLS_SOCKET="chrome_devtools_remote_$pid"
  else
    export CHROME_DEVTOOLS_SOCKET=chrome_devtools_remote
  fi
}
eval_json() { "$CDP" --url-contains "$1" --eval "$2" > "$OUT/$3.json"; }
window_metrics='({mode:["fullscreen","standalone","minimal-ui","browser"].find(m=>matchMedia(`(display-mode: ${m})`).matches),innerHeight,visualViewportHeight:visualViewport&&visualViewport.height,screenHeight:screen.height,top:getComputedStyle(document.body).paddingTop,bottom:getComputedStyle(document.body).paddingBottom,result:document.querySelector("#result")?.textContent})'
fit_metrics='(()=>{const p=document.querySelector("#probe"),s=getComputedStyle(p),r=document.documentElement.getBoundingClientRect();return {fit:document.querySelector("button.active")?.dataset.fit,mode:["fullscreen","standalone","minimal-ui","browser"].find(m=>matchMedia(`(display-mode: ${m})`).matches),orientation:screen.orientation?.type,innerWidth,innerHeight,screenWidth:screen.width,screenHeight:screen.height,visualViewportWidth:visualViewport?.width,visualViewportHeight:visualViewport?.height,root:{left:r.left,top:r.top,width:r.width,height:r.height},safe:{top:s.paddingTop,right:s.paddingRight,bottom:s.paddingBottom,left:s.paddingLeft}}})()'

if [ "$MODE" = chromium ]; then
  adb shell am force-stop com.android.chrome || true
  launch() {
    "$SCRIPT_DIR/launch_shortcut.py" "$1"
    select_devtools_socket org.chromium.chrome
  }
  standalone_fragment=demo-standalone.html
  fullscreen_fragment=demo-fullscreen.html
  backswipe_fragment=demo-backswipe.html
  nav_fragment=demo-nav-contrast.html
  password_fragment=manualpassword.html
  fit_fragment=${VIEWPORT_FIT_FRAGMENT:-viewport-fit}
  browser_package=org.chromium.chrome
  browser_activity=org.chromium.chrome/org.chromium.chrome.browser.ChromeTabbedActivity
elif [ "$MODE" = stock ]; then
  [ -n "$MAP" ] || usage
  # shellcheck disable=SC1090
  source "$MAP"
  adb shell am force-stop org.chromium.chrome || true
  launch() {
    local key=$1 package
    case "$key" in
      "E2E Standalone") package=$standalone ;;
      "E2E Fullscreen") package=$fullscreen ;;
      "E2E BackSwipe") package=$backswipe ;;
      "Nav Contrast") package=$nav_contrast ;;
      "Password Test") package=$password ;;
      "Fit Switch") package=$viewport_fit ;;
      *) return 2 ;;
    esac
    adb shell am force-stop com.android.chrome
    adb shell monkey -p "$package" -c android.intent.category.LAUNCHER 1 >/dev/null
    sleep 7
    select_devtools_socket com.android.chrome
  }
  standalone_fragment=automation/pages/standalone
  fullscreen_fragment=automation/pages/fullscreen
  backswipe_fragment=automation/pages/backswipe
  nav_fragment=automation/pages/nav-contrast
  password_fragment=automation/pages/password
  fit_fragment=automation/pages/viewport-fit
  browser_package=com.android.chrome
  browser_activity=com.android.chrome/com.google.android.apps.chrome.Main
else
  usage
fi

ORIGINAL_NAV_MODE=$(adb shell settings get secure navigation_mode | tr -d '\r')
ORIGINAL_ACCELEROMETER_ROTATION=$(adb shell settings get system accelerometer_rotation | tr -d '\r')
ORIGINAL_USER_ROTATION=$(adb shell settings get system user_rotation | tr -d '\r')
ORIGINAL_WINDOW_ROTATION=$(adb shell cmd window user-rotation | tr -d '\r')
ORIGINAL_FIXED_TO_USER_ROTATION=$(adb shell cmd window fixed-to-user-rotation | tr -d '\r')

restore_navigation() {
  if [ "$ORIGINAL_NAV_MODE" = 2 ]; then
    adb shell cmd overlay enable-exclusive --category com.android.internal.systemui.navbar.gestural || true
  else
    adb shell cmd overlay disable com.android.internal.systemui.navbar.gestural || true
  fi
  adb shell settings put secure navigation_mode "$ORIGINAL_NAV_MODE" || true
}

restore_rotation() {
  adb shell cmd window fixed-to-user-rotation "$ORIGINAL_FIXED_TO_USER_ROTATION" || true
  if [ "$ORIGINAL_WINDOW_ROTATION" = free ]; then
    adb shell cmd window user-rotation free || true
  else
    adb shell cmd window user-rotation lock "${ORIGINAL_WINDOW_ROTATION##* }" || true
  fi
  adb shell settings put system accelerometer_rotation "$ORIGINAL_ACCELEROMETER_ROTATION" || true
  adb shell settings put system user_rotation "$ORIGINAL_USER_ROTATION" || true
}

restore_device_state() {
  restore_navigation
  restore_rotation
}

enable_gesture_navigation() {
  adb shell cmd overlay enable-exclusive --category com.android.internal.systemui.navbar.gestural
  adb shell settings put secure navigation_mode 2
  sleep 4
}

trap restore_device_state EXIT

# Keep portrait cases deterministic regardless of how the physical device is
# resting. The landscape matrix below temporarily changes user_rotation.
adb shell cmd window fixed-to-user-rotation enabled
adb shell cmd window user-rotation lock 0
sleep 4

close_child_toolbar() {
  adb shell uiautomator dump /sdcard/window.xml >/dev/null
  adb exec-out cat /sdcard/window.xml > "$OUT/window.xml"
  read -r x y < <(XML="$OUT/window.xml" python3 - <<'PY'
import os, re, xml.etree.ElementTree as ET
for node in ET.parse(os.environ["XML"]).iter("node"):
    description = node.attrib.get("content-desc", "")
    resource_id = node.attrib.get("resource-id", "")
    if description in ("Close", "Close tab", "Schließen") or resource_id.endswith(":id/close_button"):
        x1, y1, x2, y2 = [int(v) for v in re.findall(r"\d+", node.attrib["bounds"])]
        print((x1 + x2) // 2, (y1 + y2) // 2)
        break
else:
    raise SystemExit("Close button not found")
PY
)
  adb shell input tap "$x" "$y"
}

# Standalone: cold start, pull-to-refresh, off-origin child toolbar, recovery.
launch "E2E Standalone"
shot standalone-cold
eval_json "$standalone_fragment" "$window_metrics" standalone-cold
adb shell input swipe 500 300 500 1100 700
sleep 6
shot standalone-refresh
eval_json "$standalone_fragment" "$window_metrics" standalone-refresh
"$CDP" --url-contains "$standalone_fragment" --eval 'document.querySelector(`a[href^="https://example.com"]`).click(); true' >/dev/null
sleep 7
shot standalone-offorigin
close_child_toolbar
sleep 7
shot standalone-recovered
eval_json "$standalone_fragment" "$window_metrics" standalone-recovered

# Fullscreen: the same sequence catches failure to reacquire immersive layout.
launch "E2E Fullscreen"
shot fullscreen-cold
eval_json "$fullscreen_fragment" "$window_metrics" fullscreen-cold
adb shell input swipe 500 300 500 1100 700
sleep 6
shot fullscreen-refresh
eval_json "$fullscreen_fragment" "$window_metrics" fullscreen-refresh
"$CDP" --url-contains "$fullscreen_fragment" --eval 'document.querySelector(`a[href^="https://example.com"]`).click(); true' >/dev/null
sleep 7
shot fullscreen-offorigin
close_child_toolbar
sleep 15
shot fullscreen-recovered
eval_json "$fullscreen_fragment" "$window_metrics" fullscreen-recovered

# Three-button navigation contrast and status icon appearance.
launch "Nav Contrast"
"$CDP" --url-contains "$nav_fragment" --eval 'document.querySelector("#white").click(); true' >/dev/null
sleep 2
shot nav-white
capture_appearance() {
  # The focused fullscreen web app can drop the mLastAppearance= line from
  # dumpsys; the status-bar AppearanceRegion still reports LIGHT_STATUS_BARS
  # (dark icons) versus an empty appearance (light icons).
  local out_file=$1 line
  for _ in 1 2 3 4 5; do
    line=$(adb shell dumpsys window | grep -m1 'AppearanceRegion{' || true)
    if [ -n "$line" ]; then
      printf '%s\n' "$line" > "$out_file"
      return
    fi
    sleep 1
  done
  : > "$out_file"
}
capture_appearance "$OUT/nav-white-appearance.txt"
"$CDP" --url-contains "$nav_fragment" --eval 'document.querySelector("#dark").click(); true' >/dev/null
sleep 2
shot nav-black
capture_appearance "$OUT/nav-black-appearance.txt"

# Dynamic viewport-fit transitions in landscape. Repeat cover and auto after
# contain to catch stale padding, cutout paint, and inset ownership.
launch "Fit Switch"
adb shell cmd window fixed-to-user-rotation enabled
adb shell cmd window user-rotation lock 1
sleep 4

capture_fit() {
  local fit=$1 suffix=$2
  "$CDP" --url-contains "$fit_fragment" \
    --eval "document.querySelector('[data-fit=\"$fit\"]').click(); true" >/dev/null
  sleep 2
  shot "viewport-fit-landscape-$suffix"
  eval_json "$fit_fragment" "$fit_metrics" "viewport-fit-landscape-$suffix"
  adb shell dumpsys window windows > "$OUT/viewport-fit-landscape-$suffix-window.txt"
}

capture_fit cover cover-1
capture_fit auto auto-1
capture_fit contain contain
capture_fit cover cover-2
capture_fit auto auto-2
adb shell cmd window user-rotation lock 0
sleep 4

# Keyboard and CSS safe-area behavior. Gesture navigation gives this case a
# non-zero bottom safe area before the resizing IME opens.
enable_gesture_navigation
launch "Password Test"
shot keyboard-closed
eval_json "$password_fragment" "$window_metrics" keyboard-closed
"$CDP" --url-contains "$password_fragment" --eval 'document.querySelector("#finalPassword").focus(); true' >/dev/null
sleep 5
shot keyboard-open
eval_json "$password_fragment" "$window_metrics" keyboard-open
adb shell input keyevent BACK || true
restore_navigation

# requestFullscreen baseline in a normal browser tab.
adb shell am force-stop "$browser_package"
adb shell am start -W -n "$browser_activity" -a android.intent.action.MAIN >/dev/null
sleep 4
select_devtools_socket "$browser_package"
URL='https://static.januschka.com/i-407420295/demo-request-fullscreen.html?automation=1' SCRIPT_DIR="$SCRIPT_DIR" python3 - <<'PY'
import os, sys
sys.path.insert(0, os.environ["SCRIPT_DIR"])
from cdp_adb import CDP, targets
for page in [t for t in targets() if t.get("type") == "page"]:
    cdp = CDP(page)
    try:
        browser = cdp.evaluate("matchMedia('(display-mode: browser)').matches")
    except Exception:
        browser = False
    if browser:
        cdp.command("Page.navigate", {"url": os.environ["URL"]})
        cdp.close()
        break
    cdp.close()
else:
    raise SystemExit("Browser target not found")
PY
sleep 5
"$CDP" --url-contains demo-request-fullscreen --eval 'document.querySelector("#toggle").click(); true' >/dev/null
sleep 5
shot request-fullscreen
eval_json demo-request-fullscreen '({fullscreen:!!document.fullscreenElement,innerHeight,visualViewportHeight:visualViewport.height,screenHeight:screen.height})' request-fullscreen

# Backswipe under temporary gesture navigation. The EXIT trap restores the
# original three-button state if capture fails.
enable_gesture_navigation
launch "E2E BackSwipe"
shot backswipe-initial
eval_json "$backswipe_fragment" "$window_metrics" backswipe-initial
(adb shell input swipe 1 1200 260 1200 2000) & swipe_pid=$!
sleep 0.7
shot backswipe-mid
wait "$swipe_pid"
sleep 2
shot backswipe-after
eval_json "$backswipe_fragment" "$window_metrics" backswipe-after
restore_navigation
restore_rotation
trap - EXIT

# Public Chromium cannot create the production saved-password state on this
# device. Run Chromium's instrumented accessory integration test with injected
# suggestions to exercise the real accessory UI instead.
if [ "$MODE" = chromium ] && [ -n "${CHROMIUM_SRC:-}" ]; then
  CHROMIUM_OUT=${CHROMIUM_OUT:-out/Android}
  # Call test_runner.py directly. The generated wrapper goes through
  # testing/test_env.py, which sets CHROME_HEADLESS=1; with that variable set
  # and build/fuchsia/starview/run_cuttlefish.py present, the environment
  # factory silently targets a Cuttlefish emulator on 127.0.0.1:6525 instead
  # of the attached device.
  (
    cd "$CHROMIUM_SRC"
    vpython3 build/android/test_runner.py instrumentation \
      --output-directory "$CHROMIUM_OUT" \
      --runtime-deps-path "$CHROMIUM_OUT/gen.runtime/chrome/android/chrome_public_test_apk.runtime_deps" \
      --test-apk "$CHROMIUM_OUT/apks/ChromePublicTest.apk" \
      --use-local-devil-tools \
      --additional-apk "$CHROMIUM_OUT/apks/ChromiumNetTestSupport.apk" \
      --additional-apk "$CHROMIUM_OUT/apks/JavatestsWebApk.apk" \
      --additional-apk "$CHROMIUM_OUT/apks/ChromePublicTestSupport.apk" \
      --additional-apk "$CHROMIUM_OUT/apks/MediaRouterTestSupportApk.apk" \
      --approve-app-links org.chromium.chrome.tests.support:www.example.com \
      --device-data-filter='+//chrome/test/data/*' \
      --device-data-filter='+//content/test/data/*' \
      --device-data-filter='+//net/data/ssl/certificates/*' \
      --device-data-filter='-*' \
      ${ANDROID_SERIAL:+-d "$ANDROID_SERIAL"} \
      -f 'org.chromium.chrome.browser.keyboard_accessory.AutofillKeyboardAccessoryIntegrationTest.testTapInputFieldShowsKeyboardAccessory' \
      --num-retries=0
  ) 2>&1 | tee "$OUT/keyboard-accessory-test.txt"
fi

"$SCRIPT_DIR/validate_matrix.py" "$MODE" "$OUT"
echo "Capture and validation complete: $OUT"
