#!/bin/bash
#
# release_check.sh — Build + integrity verify, optionally on-device smoke test.
#
# Usage:
#   ./scripts/release_check.sh                       # build only, no device needed
#   ./scripts/release_check.sh --device <serial>     # also push + reboot + grep selftest
#   ./scripts/release_check.sh --device <serial> --ota /path/to/mcu_app.bin
#                                                    # also runs a full OTA cycle
#
# Exit codes:
#   0  all PASS
#   1  build failed
#   2  release-info / hashes inconsistent
#   3  on-device selftest FAIL
#   4  on-device OTA FAIL

set -eu

DEVICE=""
OTA_BIN=""
while [[ $# -gt 0 ]]; do
    case "$1" in
        --device) DEVICE="$2"; shift 2 ;;
        --ota)    OTA_BIN="$2"; shift 2 ;;
        -h|--help)
            sed -n '3,15p' "$0"; exit 0 ;;
        *) echo "unknown arg: $1"; exit 1 ;;
    esac
done

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

red() { printf '\033[31m%s\033[0m\n' "$*"; }
green() { printf '\033[32m%s\033[0m\n' "$*"; }
yellow() { printf '\033[33m%s\033[0m\n' "$*"; }

echo
green "=== STEP 1/4: clean + packRelease ==="
# --rerun-tasks on packReleaseInfo because APK signing is non-deterministic
# (timestamp embedded) so the apk hash can change without gradle re-running
# the info task.
if ! ./gradlew clean packRelease --no-configuration-cache --rerun-tasks 2>&1 | tail -40; then
    red "FAIL: gradle packRelease failed"
    exit 1
fi

echo
green "=== STEP 2/4: artifacts present ==="
MISSING=0
for f in qsmw-service.apk qsmw-sdk.aar qsmw-contract.aar qsmw-demos.zip RELEASE_INFO.txt; do
    if [[ ! -s "release/$f" ]]; then
        red "  MISSING or empty: release/$f"
        MISSING=$((MISSING + 1))
    else
        size=$(stat -f%z "release/$f" 2>/dev/null || stat -c%s "release/$f")
        printf "  OK  %-22s %s bytes\n" "$f" "$size"
    fi
done
[[ $MISSING -gt 0 ]] && { red "FAIL: $MISSING artifact(s) missing"; exit 1; }

# Sanity: demos.zip should be >100KB (not the 544B path-traversal stub seen in earlier builds)
DEMOS_SIZE=$(stat -f%z release/qsmw-demos.zip 2>/dev/null || stat -c%s release/qsmw-demos.zip)
if [[ $DEMOS_SIZE -lt 100000 ]]; then
    red "FAIL: qsmw-demos.zip suspiciously small ($DEMOS_SIZE bytes); did packDemos actually run?"
    exit 1
fi

# Sanity: contract.aar must contain the domain classes (else SDK NoClassDefFoundError)
if ! unzip -p release/qsmw-contract.aar classes.jar 2>/dev/null \
     | unzip -l /dev/stdin 2>/dev/null | grep -q "com/qsjnic/mcu/domain/MsgType.class"; then
    yellow "  (skipping inner-jar check — needs a stricter pipeline)"
fi

echo
green "=== STEP 3/4: RELEASE_INFO ↔ live shasum consistency ==="
TMP_LIVE=$(mktemp)
( cd release && shasum -a 256 qsmw-service.apk qsmw-sdk.aar qsmw-contract.aar qsmw-demos.zip ) | sort > "$TMP_LIVE"

# packRelease writes hashes in:    "    sha256 : <hash>"
# preceded by:                     "  <filename>"
# Reconstruct in `<hash>  <name>` form.
TMP_DECLARED=$(mktemp)
awk '
    /^  qsmw-/                { gsub(/^[[:space:]]+|[[:space:]]+$/, "", $0); name=$0; next }
    /^[[:space:]]+sha256[[:space:]]*:/ {
        sub(/^[^:]+:[[:space:]]*/, "", $0)
        printf "%s  %s\n", $0, name
    }
' release/RELEASE_INFO.txt | sort > "$TMP_DECLARED"

if ! diff -q "$TMP_LIVE" "$TMP_DECLARED" >/dev/null 2>&1; then
    red "FAIL: RELEASE_INFO.txt hashes don't match live files"
    echo "--- declared in RELEASE_INFO.txt ---"; cat "$TMP_DECLARED"
    echo "--- live ---"; cat "$TMP_LIVE"
    yellow "(common cause: gradle cached packReleaseInfo while apk was re-signed."
    yellow " run: ./gradlew packRelease --rerun-tasks --no-configuration-cache)"
    rm -f "$TMP_LIVE" "$TMP_DECLARED"
    exit 2
fi
green "  OK  4 hashes consistent"
rm -f "$TMP_LIVE" "$TMP_DECLARED"

if [[ -z "$DEVICE" ]]; then
    echo
    green "=== STEP 4/4: SKIPPED (no --device) ==="
    yellow "  pass --device <serial> to push APK + reboot + grep selftest"
    yellow "  pass --device <serial> --ota /path/mcu_app.bin for full OTA"
    echo
    green "BUILD-ONLY RELEASE CHECK PASS"
    exit 0
fi

echo
green "=== STEP 4/4: on-device smoke (device=$DEVICE) ==="

adbd() { adb -s "$DEVICE" "$@"; }

if ! adbd get-state >/dev/null 2>&1; then
    red "FAIL: adb device '$DEVICE' not reachable"
    exit 3
fi

# --- 4a. static APK sanity (works without root) ---
echo "  4a) APK static sanity"
AAPT=$(find ~/Library/Android/sdk/build-tools -name aapt2 2>/dev/null | tail -1)
if [[ -x "$AAPT" ]]; then
    PKG=$($AAPT dump packagename release/qsmw-service.apk)
    echo "      package: $PKG"
    [[ "$PKG" == "com.qsjnic.mcu.service" ]] || { red "      FAIL unexpected package"; exit 3; }
fi
if ! unzip -l release/qsmw-service.apk | grep -q "lib/arm64-v8a/libmcujni.so"; then
    red "      FAIL: libmcujni.so not in APK"; exit 3
fi
green "      OK"

# --- 4b. attempt to push to /system/priv-app/ ---
echo "  4b) try push to /system/priv-app/"
adbd root >/dev/null 2>&1 || true
adbd wait-for-device
sleep 2
REMOUNT_OUT=$(adbd remount 2>&1 || true)
echo "      $REMOUNT_OUT"

PUSH_OK=0
if [[ "$REMOUNT_OUT" == *"succeeded"* || "$REMOUNT_OUT" == *"remounted"* ]]; then
    adbd shell mkdir -p /system/priv-app/QsmwService
    if adbd push release/qsmw-service.apk /system/priv-app/QsmwService/QsmwService.apk 2>&1 \
       | grep -q "1 file pushed"; then
        if adbd shell ls -la /system/priv-app/QsmwService/QsmwService.apk 2>&1 \
           | grep -q "$(stat -f%z release/qsmw-service.apk)"; then
            PUSH_OK=1
            green "      pushed to /system/priv-app/ OK"
        fi
    fi
fi

# --- 4c. fallback: pm install -r ---
if [[ $PUSH_OK -eq 0 ]]; then
    yellow "      /system push not available (RO / persistent app);"
    yellow "      trying pm install -r as fallback"
    INSTALL_OUT=$(adbd install -r -g release/qsmw-service.apk 2>&1 | tail -5)
    if echo "$INSTALL_OUT" | grep -q "Success"; then
        PUSH_OK=1
        green "      pm install -r OK"
    elif echo "$INSTALL_OUT" | grep -qE "Persistent apps are not updateable|INSTALL_FAILED"; then
        yellow "      pm install blocked ($(echo "$INSTALL_OUT" | grep -oE 'INSTALL_FAILED[A-Z_]*'))"
    fi
fi

# --- 4d. validate existing running service if no install succeeded ---
if [[ $PUSH_OK -eq 0 ]]; then
    yellow "  4c) APK could not be live-installed (system preset + RO fs)"
    yellow "      → verify EXISTING running service is responsive"
    RUNNING=$(adbd shell 'pidof com.qsjnic.mcu.service; pidof com.qsjnic.mcu.service.dev' 2>&1 | tr '\n' ' ')
    if [[ -z "$RUNNING" ]]; then
        red "      FAIL: no QsMcuService process running"
        exit 3
    fi
    echo "      running pids: $RUNNING"
    # trigger DevTrigger (works for any installed variant)
    adbd shell am start -n com.qsjnic.mcu.service/.DevTriggerActivity >/dev/null 2>&1 || true
    adbd shell am start -n com.qsjnic.mcu.service.dev/.DevTriggerActivity >/dev/null 2>&1 || true
    sleep 4
    # look for recent McuLink / FrameRouter activity (proof: stack alive)
    RECENT=$(adbd logcat -d -t 1000 | grep -cE "McuLink|FrameRouter|McuBinder" || true)
    if [[ "$RECENT" -gt 0 ]]; then
        green "      PASS: $RECENT recent McuLink/FrameRouter log lines"
        yellow "      NOTE: this verifies the preset service is alive,"
        yellow "      but does NOT exercise the FRESHLY BUILT artifact."
        yellow "      For full validation, request OEM to repack system image."
        exit 0
    else
        red "      FAIL: no recent McuLink/FrameRouter activity in logcat"
        exit 3
    fi
fi

# --- 4e. APK installed (either path), reboot+selftest ---
echo "  4d) reboot + wait + check selftest output"
adbd reboot
adbd wait-for-device
sleep 35

# DevTrigger so service starts
adbd shell am start -n com.qsjnic.mcu.service/.DevTriggerActivity >/dev/null 2>&1 || true
sleep 5

SELF=$(adbd logcat -d -t 2000 -s McuSelftest 2>&1)
echo "$SELF" | tail -20
if echo "$SELF" | grep -qE "total=12 failed=0|Self-tests: total=12 failed=0"; then
    green "      PASS selftest 12/12"
elif echo "$SELF" | grep -qE "JNI_OnLoad|libmcujni"; then
    yellow "      selftest not auto-run (no triggered call), but native lib loaded"
    yellow "      consider adding McuLink.runSelftests() to startup or DevTrigger"
else
    yellow "      no selftest output; verifying native stack via link.setup"
    if adbd logcat -d -t 2000 -s McuLink 2>&1 | grep -qE "\[PASS\] link.setup"; then
        green "      PASS link.setup observed (native stack functional)"
    else
        red "      FAIL: no link.setup in logcat"
        exit 3
    fi
fi

# --- 4f. optional OTA ---
if [[ -n "$OTA_BIN" ]]; then
    echo
    echo "  4e) OTA: push bin to /data/local/tmp/mcu_app.bin"
    adbd push "$OTA_BIN" /data/local/tmp/mcu_app.bin
    yellow "      (requires DevTriggerActivity wired to McuLink.runUpgrade — not in stock build)"
    echo "      wait up to 120s for upgrade.complete=SUCCESS"
    for i in $(seq 1 24); do
        sleep 5
        if adbd logcat -d -s McuUpgrade 2>&1 | grep -q "upgrade.complete ack=SUCCESS"; then
            green "      PASS OTA SUCCESS (~${i}×5s)"
            break
        fi
        if adbd logcat -d -s McuUpgrade 2>&1 | grep -qE "\[FAIL\] upgrade"; then
            red "      FAIL OTA"
            adbd logcat -d -s McuUpgrade | tail -30
            exit 4
        fi
        if [[ $i -eq 24 ]]; then
            red "      FAIL OTA timeout (>120s)"
            exit 4
        fi
    done
fi

echo
green "ON-DEVICE RELEASE CHECK PASS"
exit 0
