#!/usr/bin/env bash

# ========================================
# Shared Alarm Test Library
# ========================================
# 
# Common helpers for Phase 1, 2, and 3 test scripts
# 
# Usage: source this file in your test script:
#   SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
#   source "${SCRIPT_DIR}/alarm-test-lib.sh"
#
# Configuration can be overridden before sourcing:
#   APP_ID="custom.package" source "${SCRIPT_DIR}/alarm-test-lib.sh"
#
# STRICT MODE NOTE:
# This library does NOT set strict mode itself (set -euo pipefail) because
# it's a library file. Scripts that source this library SHOULD set strict mode:
#   set -euo pipefail
#   IFS=$'\n\t'

# --- Config Defaults (can be overridden before sourcing) ---

: "${APP_ID:=org.timesafari.dailynotification}"
: "${APK_PATH:=./app/build/outputs/apk/debug/app-debug.apk}"
: "${ADB_BIN:=adb}"

# Reactivation log tag (common across phases)
: "${REACTIVATION_TAG:=DNP-REACTIVATION}"
: "${SCENARIO_KEY:=Detected scenario: }"

# Screenshot configuration
: "${SCREENSHOT_ROOT:=screenshots}"
: "${ENABLE_SCREENSHOTS:=1}"

# Run folder configuration (P1)
: "${RUN_ID:=$(date '+%Y%m%d_%H%M%S' 2>/dev/null || echo 'unknown')}"
: "${RUN_DIR:=runs/${RUN_ID}}"

# Release gating configuration (P4)
: "${RELEASE_GATE_PHASE3:=0}"

# Derived config (for backward compatibility with Phase 1)
PACKAGE="${APP_ID}"
ACTIVITY="${APP_ID}/.MainActivity"

# Colors for output (used by Phase 1 print_* functions)
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# ========================================
# PUBLIC API - UI/Log Helpers
# ========================================
# These are the primary functions that all scripts should use.
# Deprecated functions (print_*, wait_for_*) are kept for backward compatibility.

# ========================================
# Event Emission (Phase B - Live Console Updates)
# ========================================

# Initialize run ID for event emission
: "${DNP_RUN_ID:=$(date '+%Y%m%d_%H%M%S' 2>/dev/null || echo 'unknown')}"

# Emit test event to console via ADB broadcast
emit_event() {
  # Usage: emit_event TYPE LEVEL [PHASE] [TEST] [STEP] [MESSAGE] [EXTRA_JSON]
  # Example: emit_event "step_start" "INFO" "phase1" "phase1_test0" "p1_t0_s1" "Starting step"
  local event_type="$1"
  local level="${2:-INFO}"
  local phase_id="${3:-}"
  local test_id="${4:-}"
  local step_id="${5:-}"
  local message="${6:-}"
  local extra_json="${7:-}"

  # Only emit if UI events enabled
  if [ "${DNP_UI_EVENTS:-0}" != "1" ]; then
    return 0
  fi

  # Build JSON payload using Python (safer than shell JSON escaping)
  local payload
  payload=$(python3 -c "
import json
import sys
from datetime import datetime

event = {
    'version': 'testevent.v1',
    'ts': datetime.now().isoformat(),
    'runId': '${DNP_RUN_ID}',
    'type': '${event_type}',
    'level': '${level}',
    'phaseId': '${phase_id}',
    'testId': '${test_id}',
    'stepId': '${step_id}',
    'message': '${message}'
}

# Add extra JSON fields if provided
if '${extra_json}':
    try:
        extra = json.loads('${extra_json}')
        event.update(extra)
    except:
        pass

print(json.dumps(event))
" 2>/dev/null)

  if [ -z "$payload" ]; then
    # Fallback: simple JSON without Python
    payload="{\"version\":\"testevent.v1\",\"ts\":\"$(date -Iseconds 2>/dev/null || date '+%Y-%m-%dT%H:%M:%S')\",\"runId\":\"${DNP_RUN_ID}\",\"type\":\"${event_type}\",\"level\":\"${level}\",\"phaseId\":\"${phase_id}\",\"testId\":\"${test_id}\",\"stepId\":\"${step_id}\",\"message\":\"${message}\"}"
  fi

  # Send via ADB broadcast
  adb_broadcast_event "$payload"
}

# Send event via ADB broadcast
adb_broadcast_event() {
  local payload="$1"
  local action="org.timesafari.dailynotification.TEST_EVENT"

  # Escape payload for shell (single quotes are safest)
  # Replace single quotes with '\'' (end quote, escaped quote, start quote)
  local escaped_payload
  escaped_payload=$(echo "$payload" | sed "s/'/'\\\\''/g")

  # Send broadcast
  $ADB_BIN shell am broadcast \
    -a "$action" \
    --es payload "$escaped_payload" \
    >/dev/null 2>&1 || true
}

# Set test context (phase/test/step) for event emission
set_test_context() {
  # Usage: set_test_context PHASE_ID TEST_ID STEP_ID
  export DNP_PHASE="${1:-}"
  export DNP_TEST="${2:-}"
  export DNP_STEP="${3:-}"
}

# Emit step start event
step_start() {
  # Usage: step_start STEP_ID [MESSAGE]
  local step_id="${1:-${DNP_STEP}}"
  local message="${2:-Starting step}"
  
  if [ "${DNP_UI_EVENTS:-0}" = "1" ]; then
    emit_event "step_start" "INFO" "${DNP_PHASE:-}" "${DNP_TEST:-}" "$step_id" "$message"
  fi
}

# Emit step pass event
step_pass() {
  # Usage: step_pass STEP_ID [MESSAGE]
  local step_id="${1:-${DNP_STEP}}"
  local message="${2:-Step completed}"
  
  if [ "${DNP_UI_EVENTS:-0}" = "1" ]; then
    emit_event "step_pass" "INFO" "${DNP_PHASE:-}" "${DNP_TEST:-}" "$step_id" "$message"
  fi
}

# Emit step warn event
step_warn() {
  # Usage: step_warn STEP_ID [MESSAGE]
  local step_id="${1:-${DNP_STEP}}"
  local message="${2:-Step warning}"
  
  if [ "${DNP_UI_EVENTS:-0}" = "1" ]; then
    emit_event "step_warn" "WARN" "${DNP_PHASE:-}" "${DNP_TEST:-}" "$step_id" "$message"
  fi
}

# Emit step fail event
step_fail() {
  # Usage: step_fail STEP_ID [MESSAGE]
  local step_id="${1:-${DNP_STEP}}"
  local message="${2:-Step failed}"
  
  if [ "${DNP_UI_EVENTS:-0}" = "1" ]; then
    emit_event "step_fail" "ERROR" "${DNP_PHASE:-}" "${DNP_TEST:-}" "$step_id" "$message"
  fi
}

section() {
  echo
  echo "========================================"
  echo "$1"
  echo "========================================"
  echo
}

substep() {
  echo "→ $1"
}

info() {
  echo -e "ℹ️  $1"
}

ok() {
  echo -e "✅ $1"
}

warn() {
  echo -e "⚠️  $1"
}

error() {
  echo -e "❌ $1"
}

pause() {
  echo
  read -rp "Press Enter when ready to continue..."
  echo
}

ui_prompt() {
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "👆 UI ACTION REQUIRED"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo -e "$1"
  echo
  
  # Emit operator_required event if UI events enabled
  if [ "${DNP_UI_EVENTS:-0}" = "1" ]; then
    emit_event "operator_required" "WAIT" "${DNP_PHASE:-}" "${DNP_TEST:-}" "${DNP_STEP:-}" "$1"
  fi
  
  read -rp "Press Enter after completing the action above..."
  echo
}

# ========================================
# PUBLIC API - Command Execution Helpers
# ========================================

run_cmd() {
  # Execute a command and capture output
  # Usage: run_cmd "description" command [args...]
  # Returns: exit code of command
  local desc="$1"
  shift
  local cmd=("$@")
  
  info "Running: $desc"
  if "${cmd[@]}"; then
    ok "$desc completed"
    return 0
  else
    local exit_code=$?
    error "$desc failed (exit code: $exit_code)"
    return $exit_code
  fi
}

require_cmd() {
  # Execute a command and exit on failure
  # Usage: require_cmd "description" command [args...]
  # Exits script if command fails
  local desc="$1"
  shift
  local cmd=("$@")
  
  info "Required: $desc"
  if ! "${cmd[@]}"; then
    local exit_code=$?
    error "$desc failed (exit code: $exit_code)"
    exit $exit_code
  fi
  ok "$desc completed"
}

# ========================================
# DEPRECATED - Phase 1 Compatibility Aliases
# ========================================
# These functions are kept for backward compatibility but should not be used
# in new code. Use the public API functions above instead.

print_header() {
  # DEPRECATED: Use section() instead
  echo ""
  echo -e "${BLUE}========================================${NC}"
  echo -e "${BLUE}$1${NC}"
  echo -e "${BLUE}========================================${NC}"
  echo ""
}

print_step() {
  # DEPRECATED: Use substep() instead
  echo -e "${GREEN}→ Step $1:${NC} $2"
}

print_wait() {
  # DEPRECATED: Use info() or warn() instead
  echo -e "${YELLOW}⏳ $1${NC}"
}

print_success() {
  # DEPRECATED: Use ok() instead
  echo -e "${GREEN}✅ $1${NC}"
}

print_error() {
  # DEPRECATED: Use error() instead
  echo -e "${RED}❌ $1${NC}"
}

print_info() {
  # DEPRECATED: Use info() instead
  echo -e "${BLUE}ℹ️  $1${NC}"
}

print_warn() {
  # DEPRECATED: Use warn() instead
  echo -e "${YELLOW}⚠️  $1${NC}"
}

wait_for_user() {
  # DEPRECATED: Use pause() instead
  echo ""
  read -p "Press Enter when ready to continue..."
  echo ""
}

wait_for_ui_action() {
  # DEPRECATED: Use ui_prompt() instead
  ui_prompt "$1"
}

# --- ADB/Build Helpers ---

require_adb_device() {
  section "Pre-Flight Checks"

  if ! $ADB_BIN devices | awk 'NR>1 && $2=="device"{found=1} END{exit !found}'; then
    error "No emulator/device in 'device' state. Start your emulator first."
    exit 1
  fi
  ok "ADB device connected"

  info "Checking emulator status..."
  if ! $ADB_BIN shell getprop sys.boot_completed 2>/dev/null | grep -q "1"; then
    info "Waiting for emulator to boot..."
    $ADB_BIN wait-for-device
    while [ "$($ADB_BIN shell getprop sys.boot_completed 2>/dev/null)" != "1" ]; do
      sleep 2
    done
  fi
  ok "Emulator is ready"
}

# Phase 1 compatibility: check_adb_connection + check_emulator_ready
check_adb_connection() {
  if ! $ADB_BIN devices | grep -q "device$"; then
    print_error "No Android device/emulator connected"
    echo "Please connect a device or start an emulator, then run:"
    echo "  adb devices"
    exit 1
  fi
  print_success "ADB device connected"
}

check_emulator_ready() {
  print_info "Checking emulator status..."
  if ! $ADB_BIN shell getprop sys.boot_completed 2>/dev/null | grep -q "1"; then
    print_wait "Waiting for emulator to boot..."
    $ADB_BIN wait-for-device
    while [ "$($ADB_BIN shell getprop sys.boot_completed 2>/dev/null)" != "1" ]; do
      sleep 2
    done
  fi
  print_success "Emulator is ready"
}

build_app() {
  section "Building Test App"

  substep "Step 1: Building debug APK..."
  if ./gradlew :app:assembleDebug; then
    ok "Build successful"
  else
    error "Build failed"
    exit 1
  fi

  if [[ -f "$APK_PATH" ]]; then
    ok "APK ready: $APK_PATH"
  else
    error "APK not found at $APK_PATH"
    exit 1
  fi
}

install_app() {
  section "Installing App"

  substep "Step 1: Uninstalling existing app (if present)..."
  set +e
  uninstall_output="$($ADB_BIN uninstall "$APP_ID" 2>&1)"
  uninstall_status=$?
  set -e

  if [[ $uninstall_status -ne 0 ]]; then
    if grep -q "DELETE_FAILED_INTERNAL_ERROR" <<<"$uninstall_output"; then
      info "No existing app to uninstall (continuing)"
    else
      warn "Uninstall returned non-zero status: $uninstall_output (continuing anyway)"
    fi
  else
    ok "Previous app uninstall succeeded"
  fi

  substep "Step 2: Installing new APK..."
  if $ADB_BIN install -r "$APK_PATH"; then
    ok "App installed successfully"
  else
    error "App installation failed"
    exit 1
  fi

  substep "Step 3: Verifying installation..."
  if $ADB_BIN shell pm list packages | grep -q "$APP_ID"; then
    ok "App verified in package list"
  else
    error "App not found in package list"
    exit 1
  fi

  info "Clearing logcat buffer..."
  $ADB_BIN logcat -c
  ok "Logcat cleared"
}

launch_app() {
  # Check if we're in Phase 1 context (has print_info function)
  if type print_info >/dev/null 2>&1; then
    print_info "Launching app..."
    $ADB_BIN shell am start -n "${ACTIVITY}"
    sleep 3  # Give app time to load and check status
    print_success "App launched"
  else
    substep "Launching app main activity..."
    $ADB_BIN shell am start -n "${ACTIVITY}" >/dev/null 2>&1
    sleep 3  # Give app time to load
    ok "App launched"
  fi
}

clear_logs() {
  info "Clearing logcat buffer..."
  $ADB_BIN logcat -c
  ok "Logs cleared"
}

show_alarms() {
  info "Checking AlarmManager status..."
  echo
  $ADB_BIN shell dumpsys alarm | grep -A3 "$APP_ID" || true
  echo
}

# Plugin-specific alarm action (must match AndroidManifest.xml)
PLUGIN_ALARM_ACTION="org.timesafari.daily.NOTIFICATION"

get_plugin_alarm_count() {
  # Returns count of ONLY the plugin's NOTIFICATION alarms (not prefetch - that uses WorkManager)
  # Expected: 1 notification alarm per daily schedule
  # 
  # This function counts ALARM_CLOCK wake alarms (RTC_WAKEUP) tagged as:
  #   tag=*walarm*:org.timesafari.daily.NOTIFICATION
  # 
  # Uses deduplicating parser to avoid double-counting the same alarm that appears in both:
  # - Main alarm list
  # - "Next wake from idle" section (ignored - only counts RTC_WAKEUP blocks)
  # - Alarm Stats section (ignored - only counts actual alarm blocks)
  # 
  # Tracks unique Alarm handles to ensure each alarm is counted only once.
  # Checks for app package AND action string anywhere in the block (they appear on different lines).
  local count app_id action
  app_id="$APP_ID"
  action="org.timesafari.daily.NOTIFICATION"
  count="$($ADB_BIN shell dumpsys alarm 2>/dev/null | awk -v app="$app_id" -v action="$action" '
    BEGIN {
      in_block = 0
      alarmId = ""
      hasAppLine = 0
      hasActionLine = 0
    }
    # Start of a new RTC_WAKEUP alarm block
    /^[[:space:]]*RTC_WAKEUP/ {
      # Flush previous block if it was a plugin notification
      if (in_block == 1 && hasAppLine == 1 && hasActionLine == 1 && alarmId != "") {
        seen[alarmId] = 1
      }
      in_block = 1
      alarmId = ""
      hasAppLine = 0
      hasActionLine = 0
      # Extract alarmId from "Alarm{11245c ..."
      if (match($0, /Alarm\{[0-9a-f]+/)) {
        # match is like "Alarm{11245c", extract just the hex part
        alarmId = substr($0, RSTART + 6, RLENGTH - 6)
      }
    }
    # Blank line = end of block
    /^[[:space:]]*$/ {
      if (in_block == 1 && hasAppLine == 1 && hasActionLine == 1 && alarmId != "") {
        seen[alarmId] = 1
      }
      in_block = 0
      alarmId = ""
      hasAppLine = 0
      hasActionLine = 0
    }
    # Lines inside an alarm block
    in_block == 1 {
      if ($0 ~ app) {
        hasAppLine = 1
      }
      if ($0 ~ action) {
        hasActionLine = 1
      }
    }
    END {
      # Flush final block if it was a plugin notification
      if (in_block == 1 && hasAppLine == 1 && hasActionLine == 1 && alarmId != "") {
        seen[alarmId] = 1
      }
      count = 0
      for (id in seen) {
        count++
      }
      print count + 0
    }
  ' 2>/dev/null || echo "0")"
  echo "$count" | tr -d '\n\r' | head -1
}

# Alias for backward compatibility
get_notification_alarm_count() {
  get_plugin_alarm_count
}

get_prefetch_work_count() {
  # Returns count of prefetch WorkManager jobs (not AlarmManager alarms)
  # Note: Prefetch uses WorkManager, not AlarmManager, so it won't appear in dumpsys alarm
  # This is a placeholder - actual WorkManager job counting would require different approach
  local count
  count="$($ADB_BIN shell dumpsys jobscheduler | grep -c "prefetch" 2>/dev/null || echo "0")"
  echo "$count" | tr -d '\n\r' | head -1
}

get_system_alarm_count() {
  # Returns total RTC_WAKEUP alarms on system (for debugging/context)
  local count
  count="$($ADB_BIN shell dumpsys alarm 2>/dev/null | grep -c "RTC_WAKEUP" || echo "0")"
  echo "$count" | tr -d '\n\r' | head -1
}

count_alarms() {
  # Legacy function: now returns plugin-specific alarm count
  # Use get_plugin_alarm_count() for clarity, or get_system_alarm_count() for total
  get_plugin_alarm_count
}

show_plugin_alarms_compact() {
  # Prints only the plugin's alarm block(s) for debugging
  # Shows complete RTC_WAKEUP alarm blocks that contain the app ID
  # This makes it visually obvious why the AWK matcher should pick up alarms
  # (app ID on header line, action on tag line within the same block)
  $ADB_BIN shell dumpsys alarm 2>/dev/null \
    | awk -v app="$APP_ID" '
        BEGIN {
          in_block = 0
          block = ""
          found_app = 0
        }
        /^[[:space:]]*RTC_WAKEUP/ {
          # Print previous block if it contained app ID
          if (in_block && found_app) {
            print block ORS
          }
          # Start new block
          in_block = 1
          block = $0 ORS
          found_app = ($0 ~ app) ? 1 : 0
          next
        }
        /^[[:space:]]*$/ {
          # End of block
          if (in_block && found_app) {
            print block ORS
          }
          in_block = 0
          block = ""
          found_app = 0
          next
        }
        {
          if (in_block) {
            block = block $0 ORS
            if ($0 ~ app) {
              found_app = 1
            }
          }
        }
        END {
          if (in_block && found_app) {
            print block
          }
        }
      ' \
    | sed -n '1,80p' || true
}

wait_for_stable_plugin_alarm_count() {
  # Polls for plugin alarm count to stabilize (reduces race condition false negatives)
  # Usage: wait_for_stable_plugin_alarm_count [attempts] [delay_seconds]
  # Default: 5 attempts, 2 second delay (total ~10 seconds)
  # Returns: alarm count (0 if none found after all attempts)
  local attempts=${1:-5}
  local delay=${2:-2}
  local count=0
  local i
  
  for i in $(seq 1 "$attempts"); do
    count="$(get_plugin_alarm_count)"
    if [ "$count" -ge 1 ] 2>/dev/null; then
      echo "$count"
      return 0
    fi
    if [ "$i" -lt "$attempts" ]; then
      sleep "$delay"
    fi
  done
  echo "$count"
}

# --- Screenshot Helpers ---

take_screenshot() {
  # Captures a device screenshot and saves it with test name, step, and timestamp
  # Usage: take_screenshot "test_name" "step_name"
  # Example: take_screenshot "phase1_test0_daily_rollover" "before_scheduling"
  local test_name="$1"
  local step_name="$2"

  # Do nothing if screenshots are disabled
  if [ "$ENABLE_SCREENSHOTS" != "1" ]; then
    return 0
  fi

  if [ -z "$ADB_BIN" ]; then
    echo "⚠️  ADB_BIN is not set; cannot take screenshot." >&2
    return 0
  fi

  # Timestamp for uniqueness
  local ts
  ts="$(date '+%Y%m%d-%H%M%S' 2>/dev/null)" || ts="unknown"

  # Directory: screenshots/<test_name>/
  # Use absolute path relative to script directory if SCREENSHOT_ROOT is relative
  local dir
  if [ -n "$SCRIPT_DIR" ] && [ -d "$SCRIPT_DIR" ]; then
    dir="${SCRIPT_DIR}/${SCREENSHOT_ROOT}/${test_name}"
  elif [ -n "${BASH_SOURCE[0]}" ]; then
    # Fallback: derive from this script's location
    local lib_dir
    lib_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd 2>/dev/null)" || lib_dir=""
    if [ -n "$lib_dir" ]; then
      dir="${lib_dir}/${SCREENSHOT_ROOT}/${test_name}"
    else
      dir="${SCREENSHOT_ROOT}/${test_name}"
    fi
  else
    dir="${SCREENSHOT_ROOT}/${test_name}"
  fi
  mkdir -p "$dir" 2>/dev/null || {
    echo "⚠️  Failed to create screenshot directory: $dir" >&2
    return 0
  }

  # File name: <test>_<step>_<timestamp>.png, with spaces converted to dashes
  local safe_step="${step_name// /-}"
  local file="${dir}/${test_name}_${safe_step}_${ts}.png"

  echo "📸 Capturing screenshot: ${file}" >&2
  # Use exec-out to avoid newline mangling
  if ! "$ADB_BIN" exec-out screencap -p > "$file" 2>/dev/null; then
    echo "⚠️  Failed to capture screenshot via adb." >&2
    # Clean up empty file if created
    [ -s "$file" ] || rm -f "$file" 2>/dev/null || true
    return 0
  fi

  # Verify file was created and has content
  if [ ! -s "$file" ]; then
    echo "⚠️  Screenshot file is empty or missing: $file" >&2
    rm -f "$file" 2>/dev/null || true
    return 0
  fi
}

take_failure_screenshot() {
  # Convenience wrapper for failure cases
  # Usage: take_failure_screenshot "test_name" "reason"
  # Example: take_failure_screenshot "phase1_test0_daily_rollover" "no_alarm_after_rollover"
  local test_name="$1"
  local reason="$2"
  take_screenshot "$test_name" "FAIL_${reason}"
}

force_stop_app() {
  info "Forcing stop of app process..."
  $ADB_BIN shell am force-stop "$APP_ID" || true
  sleep 2
  ok "Force stop issued"
}

# Phase 1 compatibility alias
kill_app() {
  print_info "Killing app process..."
  $ADB_BIN shell am kill "$APP_ID"
  sleep 2
  
  # Verify process is killed
  if $ADB_BIN shell ps | grep -q "$APP_ID"; then
    print_wait "Process still running, using force-stop..."
    $ADB_BIN shell am force-stop "$APP_ID"
    sleep 1
  fi
  
  if ! $ADB_BIN shell ps | grep -q "$APP_ID"; then
    print_success "App process terminated"
  else
    print_error "App process still running"
    return 1
  fi
}

reboot_emulator() {
  info "Rebooting emulator..."
  $ADB_BIN reboot
  ok "Reboot initiated"
  
  info "Waiting for emulator to come back online..."
  $ADB_BIN wait-for-device

  info "Waiting for system to fully boot..."
  while [ "$($ADB_BIN shell getprop sys.boot_completed 2>/dev/null)" != "1" ]; do
    sleep 2
  done
  sleep 5  # Additional buffer for system services
  ok "Emulator back online"
}

# --- Log Parsing Helpers ---

get_recovery_logs() {
  # Collect only the relevant reactivation logs
  $ADB_BIN logcat -d | grep "$REACTIVATION_TAG" || true
}

extract_field_from_logs() {
  # Usage: extract_field_from_logs "$logs" "rescheduled"
  local logs="$1"
  local key="$2"
  echo "$logs" | grep -E "$key=" | sed -E "s/.*$key=([0-9]+).*/\1/" | tail -n1 || echo "0"
}

extract_scenario_from_logs() {
  local logs="$1"
  echo "$logs" | grep -E "$SCENARIO_KEY" | sed -E "s/.*$SCENARIO_KEY([A-Z_]+).*/\1/" | tail -n1 || echo ""
}

# Phase 1 compatibility: check_recovery_logs
check_recovery_logs() {
  print_info "Checking recovery logs..."
  echo ""
  $ADB_BIN logcat -d | grep -E "$REACTIVATION_TAG" | tail -10
  echo ""
}

# Phase 1 compatibility: check_alarm_status
check_alarm_status() {
  print_info "Checking AlarmManager status..."
  echo ""
  $ADB_BIN shell dumpsys alarm | grep -i "$APP_ID" | head -5
  echo ""
}

# --- Test Selection Helper ---

should_run_test() {
  # Usage: should_run_test "1" SELECTED_TESTS
  # Returns 0 (success) if test should run, 1 (failure) if not
  local id="$1"
  local -n selected_tests_ref="$2"
  
  # If no tests are specified, run all tests
  if [[ "${#selected_tests_ref[@]}" -eq 0 ]]; then
    return 0
  fi
  
  for t in "${selected_tests_ref[@]}"; do
    if [[ "$t" == "$id" ]]; then
      return 0
    fi
  done
  return 1
}

# ========================================
# PUBLIC API - Run Folder & Evidence Helpers (P1)
# ========================================

ensure_run_dir() {
  # Create run directory structure if it doesn't exist
  # Creates: RUN_DIR/logs, RUN_DIR/alarms, RUN_DIR/screens, RUN_DIR/notes
  # Returns: 0 on success, 1 on failure
  local base_dir
  if [ -n "$SCRIPT_DIR" ] && [ -d "$SCRIPT_DIR" ]; then
    base_dir="${SCRIPT_DIR}/${RUN_DIR}"
  elif [ -n "${BASH_SOURCE[0]}" ]; then
    local lib_dir
    lib_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd 2>/dev/null)" || lib_dir=""
    if [ -n "$lib_dir" ]; then
      base_dir="${lib_dir}/${RUN_DIR}"
    else
      base_dir="${RUN_DIR}"
    fi
  else
    base_dir="${RUN_DIR}"
  fi
  
  mkdir -p "${base_dir}/logs" "${base_dir}/alarms" "${base_dir}/screens" "${base_dir}/notes" 2>/dev/null || {
    error "Failed to create run directory: ${base_dir}"
    return 1
  }
  
  # Export for use by capture functions
  export RUN_DIR_ABS="${base_dir}"
  return 0
}

get_run_dir() {
  # Get absolute path to current run directory
  # Returns: absolute path, or empty string if not initialized
  echo "${RUN_DIR_ABS:-}"
}

capture_alarms() {
  # Capture AlarmManager dump to run folder
  # Usage: capture_alarms "<label>"
  # Saves to: RUN_DIR/alarms/<label>_alarms.txt
  local label="$1"
  local run_dir
  run_dir="$(get_run_dir)"
  
  if [ -z "$run_dir" ]; then
    warn "Run directory not initialized, skipping alarm capture: $label"
    return 0
  fi
  
  local safe_label="${label//[^a-zA-Z0-9_-]/_}"
  local file="${run_dir}/alarms/${safe_label}_alarms.txt"
  
  info "Capturing alarms: $label → $file"
  if $ADB_BIN shell dumpsys alarm > "$file" 2>/dev/null; then
    ok "Alarms captured: $file"
    
    # Emit artifact event if UI events enabled
    if [ "${DNP_UI_EVENTS:-0}" = "1" ]; then
      local artifact_json="{\"kind\":\"alarms\",\"name\":\"${label}_alarms\",\"path\":\"${file}\"}"
      emit_event "artifact" "EVID" "${DNP_PHASE:-}" "${DNP_TEST:-}" "${DNP_STEP:-}" "Alarms captured: $label" "$artifact_json"
    fi
    
    return 0
  else
    warn "Failed to capture alarms: $label"
    return 1
  fi
}

capture_logcat() {
  # Capture logcat output to run folder
  # Usage: capture_logcat "<label>" "<grep_pattern>" "<lines>"
  # Saves to: RUN_DIR/logs/<label>_logcat.txt
  # If grep_pattern is empty, captures all recent logs
  local label="$1"
  local pattern="${2:-}"
  local lines="${3:-250}"
  local run_dir
  run_dir="$(get_run_dir)"
  
  if [ -z "$run_dir" ]; then
    warn "Run directory not initialized, skipping logcat capture: $label"
    return 0
  fi
  
  local safe_label="${label//[^a-zA-Z0-9_-]/_}"
  local file="${run_dir}/logs/${safe_label}_logcat.txt"
  
  info "Capturing logcat: $label → $file"
  
  if [ -n "$pattern" ]; then
    if $ADB_BIN logcat -d -t "$lines" | grep -E "$pattern" > "$file" 2>/dev/null; then
      ok "Logcat captured (filtered): $file"
      
      # Emit artifact event if UI events enabled
      if [ "${DNP_UI_EVENTS:-0}" = "1" ]; then
        local artifact_json="{\"kind\":\"logs\",\"name\":\"${label}_logcat\",\"path\":\"${file}\"}"
        emit_event "artifact" "EVID" "${DNP_PHASE:-}" "${DNP_TEST:-}" "${DNP_STEP:-}" "Logcat captured: $label" "$artifact_json"
      fi
      
      return 0
    else
      # Even if grep finds nothing, create empty file to indicate attempt
      touch "$file"
      warn "No logcat matches for pattern: $pattern"
      return 0
    fi
  else
    if $ADB_BIN logcat -d -t "$lines" > "$file" 2>/dev/null; then
      ok "Logcat captured: $file"
      
      # Emit artifact event if UI events enabled
      if [ "${DNP_UI_EVENTS:-0}" = "1" ]; then
        local artifact_json="{\"kind\":\"logs\",\"name\":\"${label}_logcat\",\"path\":\"${file}\"}"
        emit_event "artifact" "EVID" "${DNP_PHASE:-}" "${DNP_TEST:-}" "${DNP_STEP:-}" "Logcat captured: $label" "$artifact_json"
      fi
      
      return 0
    else
      warn "Failed to capture logcat: $label"
      return 1
    fi
  fi
}

capture_screenshot() {
  # Capture device screenshot to run folder
  # Usage: capture_screenshot "<label>"
  # Saves to: RUN_DIR/screens/<label>_screenshot.png
  # Falls back to existing take_screenshot() if screenshots enabled
  local label="$1"
  local run_dir
  run_dir="$(get_run_dir)"
  
  if [ -z "$run_dir" ]; then
    warn "Run directory not initialized, skipping screenshot: $label"
    return 0
  fi
  
  if [ "$ENABLE_SCREENSHOTS" != "1" ]; then
    warn "Screenshots disabled, skipping: $label"
    return 0
  fi
  
  local safe_label="${label//[^a-zA-Z0-9_-]/_}"
  local file="${run_dir}/screens/${safe_label}_screenshot.png"
  
  info "Capturing screenshot: $label → $file"
  if "$ADB_BIN" exec-out screencap -p > "$file" 2>/dev/null; then
    if [ -s "$file" ]; then
      ok "Screenshot captured: $file"
      
      # Emit artifact event if UI events enabled
      if [ "${DNP_UI_EVENTS:-0}" = "1" ]; then
        local artifact_json="{\"kind\":\"screen\",\"name\":\"${label}_screenshot\",\"path\":\"${file}\"}"
        emit_event "artifact" "EVID" "${DNP_PHASE:-}" "${DNP_TEST:-}" "${DNP_STEP:-}" "Screenshot captured: $label" "$artifact_json"
      fi
      
      return 0
    else
      warn "Screenshot file is empty: $file"
      rm -f "$file" 2>/dev/null || true
      return 1
    fi
  else
    warn "Failed to capture screenshot: $label"
    return 1
  fi
}

evidence_block() {
  # Print evidence location block for a test
  # Usage: evidence_block "<test_id>"
  # Prints formatted block showing where artifacts are saved
  local test_id="$1"
  local run_dir
  run_dir="$(get_run_dir)"
  
  if [ -z "$run_dir" ]; then
    warn "Run directory not initialized, cannot show evidence block"
    return 1
  fi
  
  echo
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "📦 EVIDENCE: $test_id"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "Run ID: $RUN_ID"
  echo "Evidence directory: $run_dir"
  echo
  echo "Artifacts:"
  echo "  • Alarms:    $run_dir/alarms/"
  echo "  • Logs:      $run_dir/logs/"
  echo "  • Screens:   $run_dir/screens/"
  echo "  • Notes:     $run_dir/notes/"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo
}

# ========================================
# PUBLIC API - Verdict Functions (P1)
# ========================================

verdict_pass() {
  # Emit a PASS verdict for a test
  # Usage: verdict_pass "<test_id>" "<message>"
  local test_id="$1"
  local message="$2"
  local run_dir
  run_dir="$(get_run_dir)"
  
  echo
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "✅ VERDICT: PASS"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "Test ID: $test_id"
  echo "Status:  PASS"
  echo "Message: $message"
  if [ -n "$run_dir" ]; then
    echo "Evidence: $run_dir"
  fi
  echo "Next:    Continue to next test"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo
  
  # Emit test_verdict event if UI events enabled
  if [ "${DNP_UI_EVENTS:-0}" = "1" ]; then
    emit_event "test_verdict" "INFO" "${DNP_PHASE:-}" "$test_id" "" "Test passed: $message"
  fi
}

verdict_warn() {
  # Emit a WARN verdict for a test
  # Usage: verdict_warn "<test_id>" "<message>"
  local test_id="$1"
  local message="$2"
  local run_dir
  run_dir="$(get_run_dir)"
  
  echo
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "⚠️  VERDICT: WARN"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "Test ID: $test_id"
  echo "Status:  WARN"
  echo "Message: $message"
  if [ -n "$run_dir" ]; then
    echo "Evidence: $run_dir"
  fi
  echo "Next:    Review evidence and continue"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo
  
  # Emit test_verdict event if UI events enabled
  if [ "${DNP_UI_EVENTS:-0}" = "1" ]; then
    emit_event "test_verdict" "WARN" "${DNP_PHASE:-}" "$test_id" "" "Test warning: $message"
  fi
}

verdict_fail() {
  # Emit a FAIL verdict for a test
  # Usage: verdict_fail "<test_id>" "<message>"
  # If RELEASE_GATE_PHASE3=1, this will cause script to exit with non-zero
  local test_id="$1"
  local message="$2"
  local run_dir
  run_dir="$(get_run_dir)"
  
  echo
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "❌ VERDICT: FAIL"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo "Test ID: $test_id"
  echo "Status:  FAIL"
  echo "Message: $message"
  if [ -n "$run_dir" ]; then
    echo "Evidence: $run_dir"
  fi
  echo "Next:    Review evidence and investigate"
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  echo
  
  # Emit test_verdict event if UI events enabled
  if [ "${DNP_UI_EVENTS:-0}" = "1" ]; then
    emit_event "test_verdict" "ERROR" "${DNP_PHASE:-}" "$test_id" "" "Test failed: $message"
  fi
  
  # If release gating is enabled, exit with failure
  if [ "${RELEASE_GATE_PHASE3:-0}" = "1" ]; then
    error "Release gating enabled: exiting due to test failure"
    exit 1
  fi
}

