#!/usr/bin/env bash

# Phase 1 Testing Script - Interactive Test Runner
# Guides through all Phase 1 tests with clear prompts for UI interaction

# Strict mode (P0.2)
set -euo pipefail
IFS=$'\n\t'

# Source shared library
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/alarm-test-lib.sh"

# Initialize run directory (P1)
ensure_run_dir || {
  error "Failed to initialize run directory"
  exit 1
}

# Phase 1 specific configuration
VERIFY_FIRE=${VERIFY_FIRE:-false}  # Set VERIFY_FIRE=true to enable alarm fire verification
APP_DIR="${SCRIPT_DIR}"
PROJECT_ROOT="$(cd "${APP_DIR}/../.." && pwd)"

# Allow selecting specific tests on the command line (e.g. ./test-phase1.sh 1 2)
SELECTED_TESTS=()

check_plugin_configured() {
    print_info "Checking if plugin is already configured..."
    
    # Wait a moment for app to fully load
    sleep 3
    
    # Check if database exists (indicates plugin has been used)
    DB_EXISTS=$($ADB_BIN shell run-as "${APP_ID}" ls databases/ 2>/dev/null | grep -c "daily_notification" || echo "0")
    
    # Check if SharedPreferences has configuration (more reliable)
    # The plugin stores config in SharedPreferences
    PREFS_EXISTS=$($ADB_BIN shell run-as "${APP_ID}" ls shared_prefs/ 2>/dev/null | grep -c "DailyNotification" || echo "0")
    
    # Check recent logs for configuration activity
    RECENT_CONFIG=$($ADB_BIN logcat -d -t 50 | grep -E "Plugin configured|configurePlugin|Configuration" | tail -3)
    
    if [ "${DB_EXISTS}" -gt "0" ] || [ "${PREFS_EXISTS}" -gt "0" ]; then
        print_success "Plugin appears to be configured (database or preferences exist)"
        
        # Show user what to check in UI
        print_info "Please verify in the app UI that you see:"
        echo "  ⚙️ Plugin Settings: ✅ Configured"
        echo "  🔌 Native Fetcher: ✅ Configured"
        echo ""
        echo "If both show ✅, the plugin is configured and you can skip configuration."
        echo "If either shows ❌ or 'Not configured', you'll need to click 'Configure Plugin'."
        echo ""
        
        return 0
    else
        print_info "Plugin not configured (no database or preferences found)"
        print_info "You will need to click 'Configure Plugin' in the app UI"
        return 1
    fi
}

check_permissions() {
    print_info "Checking notification permissions..."
    
    # Check if POST_NOTIFICATIONS permission is granted (Android 13+)
    # Strip whitespace/newlines to ensure we get a single integer
    PERM_CHECK=$($ADB_BIN shell dumpsys package "${APP_ID}" | grep -A 5 "granted=true" | grep -c "android.permission.POST_NOTIFICATIONS" 2>/dev/null | tr -d '\n\r ' || echo "0")
    
    # Also check via app's permission status if available
    PERM_GRANTED=false
    # Ensure PERM_CHECK is a valid integer (strip any remaining non-digits)
    PERM_CHECK=$(echo "${PERM_CHECK}" | tr -d '\n\r ' | grep -oE '^[0-9]+$' || echo "0")
    if [ "${PERM_CHECK}" -gt "0" ] 2>/dev/null; then
        PERM_GRANTED=true
    else
        # Check if we're on Android 12 or below (permission not required)
        SDK_VERSION=$($ADB_BIN shell getprop ro.build.version.sdk 2>/dev/null | tr -d '\n\r ' || echo "0")
        SDK_VERSION=$(echo "${SDK_VERSION}" | grep -oE '^[0-9]+$' || echo "0")
        if [ "${SDK_VERSION}" -lt "33" ] 2>/dev/null; then
            PERM_GRANTED=true  # Pre-Android 13 doesn't need runtime permission
        fi
    fi
    
    if [ "${PERM_GRANTED}" = true ]; then
        print_success "Notification permissions granted"
        return 0
    else
        print_warn "Notification permissions may not be granted"
        return 1
    fi
}

# Comprehensive prerequisite verification (aligned with PHASE1_TEST0_GOLDEN.md)
# Verifies all 5 items specified in the golden run document:
# 1. Plugin Settings: Configured
# 2. Native Fetcher: Configured
# 3. Notifications: Granted
# 4. Exact Alarms: Granted
# 5. Channel: Enabled (High)
#
# Usage: verify_all_prerequisites "phase1" "phase1_test0" "p1_t0_s2"
# This will verify all prerequisites and emit step events for each check
verify_all_prerequisites() {
    local phase_id="${1:-}"
    local test_id="${2:-}"
    local base_step_id="${3:-}"
    
    info "Verifying all prerequisites (aligned with golden run specification)..."
    
    local all_ok=true
    local missing_items=()
    
    # 1. Check Plugin Settings: Configured
    local step_id="${base_step_id}_plugin"
    set_test_context "${phase_id}" "${test_id}" "${step_id}"
    step_start "${step_id}" "Verifying Plugin Settings: Configured"
    if check_plugin_configured >/dev/null 2>&1; then
        ok "Plugin Settings: ✅ Configured"
        step_pass "${step_id}" "Plugin Settings configured"
    else
        warn "Plugin Settings: ❌ Not configured"
        all_ok=false
        missing_items+=("Plugin Settings")
        step_fail "${step_id}" "Plugin Settings not configured"
    fi
    
    # 2. Check Native Fetcher: Configured
    # (Plugin config check also verifies native fetcher via SharedPreferences)
    # For explicit verification, we check logs for fetcher configuration
    step_id="${base_step_id}_fetcher"
    set_test_context "${phase_id}" "${test_id}" "${step_id}"
    step_start "${step_id}" "Verifying Native Fetcher: Configured"
    local fetcher_configured=false
    local fetcher_logs=$($ADB_BIN logcat -d -t 100 | grep -E "Native fetcher.*registered|FETCHER.*CONFIGURE|configureNativeFetcher" | tail -5 || true)
    if [ -n "${fetcher_logs}" ] || check_plugin_configured >/dev/null 2>&1; then
        ok "Native Fetcher: ✅ Configured"
        step_pass "${step_id}" "Native Fetcher configured"
        fetcher_configured=true
    else
        warn "Native Fetcher: ⚠️ Status unclear (check UI)"
        step_warn "${step_id}" "Native Fetcher status unclear - verify in UI"
        # Don't fail on this - let UI verification catch it
    fi
    
    # 3. Check Notifications: Granted
    step_id="${base_step_id}_notifications"
    set_test_context "${phase_id}" "${test_id}" "${step_id}"
    step_start "${step_id}" "Verifying Notifications: Granted"
    if check_permissions >/dev/null 2>&1; then
        ok "Notifications: ✅ Granted"
        step_pass "${step_id}" "Notifications permission granted"
    else
        error "Notifications: ❌ Not granted"
        all_ok=false
        missing_items+=("Notifications permission")
        step_fail "${step_id}" "Notifications permission not granted"
    fi
    
    # 4. Check Exact Alarms: Granted
    # Best way: Check if app has alarms with exactAllowReason=policy_permission
    # This indicates the permission is granted and working
    step_id="${base_step_id}_exact_alarms"
    set_test_context "${phase_id}" "${test_id}" "${step_id}"
    step_start "${step_id}" "Verifying Exact Alarms: Granted"
    local exact_alarm_granted=false
    local sdk_version=$($ADB_BIN shell getprop ro.build.version.sdk 2>/dev/null | tr -d '\n\r ' | grep -oE '^[0-9]+$' || echo '0')
    
    if [ "${sdk_version}" -ge "31" ] 2>/dev/null; then
        # Android 12+ requires SCHEDULE_EXACT_ALARM permission
        # Check if app has any alarms with exactAllowReason=policy_permission
        # This is the most reliable indicator that exact alarms are working
        local alarm_dump=$($ADB_BIN shell dumpsys alarm 2>/dev/null | grep -A 10 "${APP_ID}" | grep -c "exactAllowReason=policy_permission" || echo "0")
        alarm_dump=$(echo "${alarm_dump}" | tr -d '\n\r ' | grep -oE '^[0-9]+$' || echo "0")
        
        if [ "${alarm_dump}" -gt "0" ] 2>/dev/null; then
            # Found alarms with policy_permission - exact alarms are working
            exact_alarm_granted=true
        else
            # No alarms yet, but check if permission is declared in manifest
            # (We can't easily check runtime permission status via ADB, so we'll rely on UI)
            local has_perm=$($ADB_BIN shell dumpsys package "${APP_ID}" | grep -c "android.permission.SCHEDULE_EXACT_ALARM" 2>/dev/null | tr -d '\n\r ' || echo "0")
            has_perm=$(echo "${has_perm}" | grep -oE '^[0-9]+$' || echo "0")
            if [ "${has_perm}" -gt "0" ] 2>/dev/null; then
                # Permission is declared, assume it's granted (will be verified in UI)
                exact_alarm_granted=true
            fi
        fi
    else
        # Pre-Android 12 doesn't require this permission
        exact_alarm_granted=true
    fi
    
    if [ "${exact_alarm_granted}" = true ]; then
        ok "Exact Alarms: ✅ Granted"
        step_pass "${step_id}" "Exact Alarms permission granted"
    else
        warn "Exact Alarms: ⚠️ May not be granted (check UI)"
        step_warn "${step_id}" "Exact Alarms permission unclear - verify in UI"
        # Don't fail - let UI verification catch it
    fi
    
    # 5. Check Channel: Enabled (High)
    # Channel status is best verified via UI, but we can check logs
    step_id="${base_step_id}_channel"
    set_test_context "${phase_id}" "${test_id}" "${step_id}"
    step_start "${step_id}" "Verifying Channel: Enabled (High)"
    local channel_logs=$($ADB_BIN logcat -d -t 100 | grep -E "Channel.*enabled|channel.*importance|NotificationChannel" | tail -5 || true)
    if [ -n "${channel_logs}" ]; then
        ok "Channel: ✅ Status found in logs (verify 'Enabled (High)' in UI)"
        step_pass "${step_id}" "Channel status verified"
    else
        info "Channel: ⚠️ Verify 'Enabled (High)' status in UI"
        step_warn "${step_id}" "Channel status unclear - verify 'Enabled (High)' in UI"
        # Don't fail - channel is created automatically
    fi
    
    # Final UI verification prompt (aligned with golden run step 4)
    if [ "${MODE}" != "ci" ]; then
        echo ""
        info "=== Final UI Verification (Golden Run Step 4) ==="
        info "Please confirm in the app UI that ALL of the following show ✅:"
        echo "  ⚙️ Plugin Settings: ✅ Configured"
        echo "  🔌 Native Fetcher: ✅ Configured"
        echo "  🔔 Notifications: ✅ Granted"
        echo "  ⏰ Exact Alarms: ✅ Granted"
        echo "  📢 Channel: ✅ Enabled (High)"
        echo ""
        
        if [ "${all_ok}" = false ]; then
            error "Some prerequisites are missing: ${missing_items[*]}"
            ui_prompt "Please fix the missing prerequisites in the app UI, then press Enter to continue."
        else
            ui_prompt "If all 5 items show ✅ in the UI, press Enter to continue. Otherwise, fix any issues first."
        fi
    fi
    
    if [ "${all_ok}" = false ] && [ "${MODE}" = "ci" ]; then
        error "Prerequisites not met: ${missing_items[*]}"
        return 1
    fi
    
    return 0
}

ensure_permissions() {
    if check_permissions; then
        print_success "Permissions already granted"
        return 0
    else
        print_info "Notification permissions needed"
        wait_for_ui_action "In the app UI, click the 'Request Permissions' button.
        
        This will show a system permission dialog.
        
        Steps:
        1. Click 'Request Permissions' button
        2. In the system dialog, tap 'Allow' to grant notification permission
        3. Return to the app and verify the status shows:
           - 🔔 Notifications: ✅ Granted (or similar)
        
        Once permission is granted, press Enter to continue."
        
        # Re-check permissions
        sleep 2
        if check_permissions; then
            print_success "Permissions granted"
            return 0
        else
            print_warn "Permissions may still not be granted - continuing anyway"
            print_info "If notifications fail, you may need to grant permissions manually"
            return 1
        fi
    fi
}

ensure_plugin_configured() {
    if check_plugin_configured; then
        # Plugin might be configured, but let user verify
        wait_for_ui_action "Please check the Plugin Status section at the top of the app.
        
        If you see:
        - ⚙️ Plugin Settings: ✅ Configured
        - 🔌 Native Fetcher: ✅ Configured
        - 🔔 Notifications: ✅ Granted (or similar)
        
        Then the plugin is already configured - just press Enter to continue.
        
        If any show ❌ or 'Not configured':
        - Click 'Request Permissions' if notifications are not granted
        - Click 'Configure Plugin' if settings/fetcher are not configured
        - Wait for all to show ✅, then press Enter."
        
        # Give a moment for any configuration that just happened
        sleep 2
        print_success "Continuing with tests (plugin configuration verified or skipped)"
        return 0
    else
        # Plugin definitely needs configuration
        print_info "Plugin needs configuration"
        
        # First ensure permissions
        ensure_permissions
        
        wait_for_ui_action "Click the 'Configure Plugin' button in the app UI.
        
        Wait for the status to update:
        - ⚙️ Plugin Settings: Should change to ✅ Configured
        - 🔌 Native Fetcher: Should change to ✅ Configured
        
        Once both show ✅, press Enter to continue."
        
        # Verify configuration completed
        sleep 2
        print_success "Plugin configuration completed (or verified)"
    fi
}

# kill_app is now provided by the library, but Phase 1 uses it with PACKAGE variable
# The library version uses APP_ID, which is set to PACKAGE, so it should work
# But we keep this as a compatibility wrapper if needed

# Phase 1 specific helper: get_current_time (used for fire verification)
get_current_time() {
    $ADB_BIN shell date +%s
}

# ========================================
# CLI Mode Parsing (P2.1)
# ========================================

parse_cli_args() {
  local mode="all"
  SELECTED_TESTS=()
  
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --setup)
        mode="setup"
        shift
        ;;
      --run)
        mode="run"
        shift
        ;;
      --smoke)
        mode="smoke"
        shift
        ;;
      --all)
        mode="all"
        shift
        ;;
      --ci)
        mode="ci"
        shift
        ;;
      -h|--help)
        show_help
        exit 0
        ;;
      [0-9]*)
        SELECTED_TESTS+=("$1")
        shift
        ;;
      *)
        error "Unknown option: $1"
        show_help
        exit 1
        ;;
    esac
  done
  
  echo "$mode"
}

show_help() {
  cat <<EOF
Usage: $0 [MODE] [TEST_IDS...]

Modes:
  --setup    Run setup only (permissions, app install, plugin config checks)
  --run      Run actual Phase 1 tests (requires setup first)
  --smoke    Minimal test: schedule one notification + verify pending
  --all      Run setup + all tests (default)
  --ci       Non-interactive: automated checks only, fail fast

Test Selection:
  [TEST_IDS] Optional test numbers to run (e.g., 0 1 2)
             If omitted, all tests run

Examples:
  $0                    # Run all (setup + tests)
  $0 --setup            # Setup only
  $0 --run              # Run tests only
  $0 --smoke            # Quick smoke test
  $0 --ci               # CI mode (non-interactive)
  $0 --run 1 2          # Run only tests 1 and 2
  $0 --smoke 0          # Run smoke test for test 0

EOF
}

# ========================================
# Setup Mode (P2.1)
# ========================================

run_setup() {
  section "Phase 1 Setup"
  
  info "This mode performs pre-flight checks and setup:"
  echo "  • ADB device connection"
  echo "  • Emulator readiness"
  echo "  • App build and installation"
  echo "  • Permission checks"
  echo "  • Plugin configuration checks"
  echo ""
  
  if [ "${MODE}" != "ci" ]; then
    pause
  fi
  
  # Pre-flight checks
  section "Pre-Flight Checks"
  require_adb_device
  
  # Build and install
  build_app
  install_app
  
  # Clear logs
  clear_logs
  
  # Launch app for setup checks
  launch_app
  sleep 2
  
  # Check permissions and plugin configuration together
  section "Permissions & Plugin Configuration"
  info "Checking both permissions and plugin/fetcher configuration..."
  echo ""
  
  # Check both silently first
  local perms_ok=false
  local plugin_ok=false
  
  if check_permissions >/dev/null 2>&1; then
    perms_ok=true
  fi
  
  if check_plugin_configured >/dev/null 2>&1; then
    plugin_ok=true
  fi
  
  # Show status
  if [ "${perms_ok}" = "true" ]; then
    ok "Notification permissions: ✅ Granted"
  else
    warn "Notification permissions: ❌ Not granted"
  fi
  
  if [ "${plugin_ok}" = "true" ]; then
    ok "Plugin configuration: ✅ Configured"
  else
    warn "Plugin configuration: ❌ Not configured"
  fi
  
  # If either needs attention, show single combined prompt
  if [ "${MODE}" = "ci" ]; then
    if [ "${perms_ok}" != "true" ]; then
      error "Permissions not granted - cannot proceed in CI mode"
      exit 1
    fi
    if [ "${plugin_ok}" != "true" ]; then
      error "Plugin not configured - cannot proceed in CI mode"
      exit 1
    fi
  else
    # Show single combined prompt for both
    if [ "${perms_ok}" != "true" ] || [ "${plugin_ok}" != "true" ]; then
      ui_prompt "Please configure in the app UI:
      
      REQUIRED STATUS (all must show ✅):
      1) 🔔 Notifications: ✅ Granted
      2) ⚙️ Plugin Settings: ✅ Configured
      3) 🔌 Native Fetcher: ✅ Configured
      
      ACTIONS:
      - If Notifications shows ❌: Click 'Request Permissions' button
      - If Plugin Settings or Native Fetcher show ❌: Click 'Configure Plugin' button
      - Wait for all three to show ✅
      
      Once all three show ✅, press Enter to continue."
      
      # Re-check both after user action
      sleep 2
      echo ""
      info "Re-checking after configuration..."
      perms_ok=false
      plugin_ok=false
      if check_permissions >/dev/null 2>&1; then
        perms_ok=true
        ok "Notification permissions: ✅ Verified"
      else
        warn "Notification permissions: ⚠️ Still not granted"
      fi
      if check_plugin_configured >/dev/null 2>&1; then
        plugin_ok=true
        ok "Plugin configuration: ✅ Verified"
      else
        warn "Plugin configuration: ⚠️ Still not configured"
      fi
      
      echo ""
      if [ "${perms_ok}" = "true" ] && [ "${plugin_ok}" = "true" ]; then
        ok "All checks passed - ready to proceed"
      else
        warn "Some items may still need configuration - continuing anyway"
        info "If tests fail, verify all three items show ✅ in the app UI"
      fi
    else
      # Both are OK, but still prompt for final verification
      ui_prompt "Please verify in the app UI that you see:
      
      1) 🔔 Notifications: ✅ Granted
      2) ⚙️ Plugin Settings: ✅ Configured
      3) 🔌 Native Fetcher: ✅ Configured
      
      If all show ✅, press Enter to continue.
      If any show ❌, configure them first, then press Enter."
    fi
  fi
  
  ok "Setup complete!"
  
  # Capture initial state
  capture_alarms "setup_initial"
  capture_logcat "setup_initial" "DNP" 100
  
  evidence_block "phase1_setup"
}

# ========================================
# Smoke Test Mode (P2.1)
# ========================================

run_smoke_test() {
  section "Phase 1 Smoke Test"
  
  info "Minimal test: schedule one notification + verify pending"
  echo "Expected time: 2-4 minutes"
  echo ""
  
  if [ "${MODE}" != "ci" ]; then
    pause
  fi
  
  # Ensure setup is done
  if ! check_permissions >/dev/null 2>&1; then
    warn "Setup not complete - running setup first..."
    run_setup
  fi
  
  # Capture before state
  capture_alarms "smoke_before"
  
  # Schedule notification
  section "Smoke Test: Schedule Notification"
  launch_app
  
  if [ "${MODE}" != "ci" ]; then
    ui_prompt "1) In the app UI, click 'Test Notification' to schedule a notification for 4 minutes in the future."
  else
    info "CI mode: Assuming notification is scheduled manually or via automation"
    sleep 3
  fi
  
  # Wait for alarm to be registered
  sleep 3
  
  # Capture after state
  capture_alarms "smoke_after"
  capture_logcat "smoke_after" "DNP-SCHEDULE|DNP-NOTIFY" 50
  
  # Verify alarm exists
  local alarm_count
  alarm_count="$(get_plugin_alarm_count)"
  
  if [ "${alarm_count}" -eq "1" ]; then
    verdict_pass "phase1_smoke" "Smoke test passed: 1 alarm scheduled"
  elif [ "${alarm_count}" -gt "1" ]; then
    verdict_fail "phase1_smoke" "Smoke test failed: ${alarm_count} alarms found (expected: 1)"
  else
    verdict_warn "phase1_smoke" "Smoke test inconclusive: ${alarm_count} alarms found (expected: 1, may be race condition)"
  fi
  
  evidence_block "phase1_smoke"
}

# ========================================
# Main Test Execution
# ========================================

main() {
  # Parse CLI arguments
  MODE="$(parse_cli_args "$@")"
  
  section "Phase 1 Testing Script"
  echo "Mode: ${MODE}"
  echo "Run ID: ${RUN_ID}"
  echo "Evidence directory: $(get_run_dir)"
  echo ""
  
  if [ "${MODE}" = "setup" ]; then
    run_setup
    return 0
  elif [ "${MODE}" = "smoke" ]; then
    run_smoke_test
    return 0
  elif [ "${MODE}" = "ci" ]; then
    info "CI mode: Non-interactive, automated checks only"
    run_setup
    # In CI mode, we might want to run a subset of tests
    # For now, just run smoke test
    run_smoke_test
    return 0
  fi
  
  # For --run and --all modes, continue with full test suite
  if [ "${MODE}" = "all" ]; then
    info "Running full test suite (setup + tests)"
    if [ "${MODE}" != "ci" ]; then
      pause
    fi
    run_setup
  elif [ "${MODE}" = "run" ]; then
    info "Running tests only (assuming setup already done)"
    if [ "${MODE}" != "ci" ]; then
      pause
    fi
  fi
  
  # ============================================
  # TEST 0: Daily Rollover (Core Contract Verification)
  # ============================================
  if should_run_test "0" SELECTED_TESTS; then
    section "TEST 0: Daily Rollover Verification"
    
    # Set test context for event emission
    set_test_context "phase1" "phase1_test0" ""
    
    info "Purpose: Verify that after a notification fires, the next day's"
    info "         schedule is correctly computed and only ONE alarm exists."
    info ""
    info "Expected time: 5-10 minutes"
    info "Automatable: Partial (requires manual clock manipulation or waiting)"
    echo ""
    
    if [ "${MODE}" != "ci" ]; then
      pause
    fi
    
    # Capture initial state
    set_test_context "phase1" "phase1_test0" "p1_t0_s1"
    step_start "p1_t0_s1" "Capturing initial state"
    capture_alarms "test0_initial"
    capture_logcat "test0_initial" "DNP" 50
    step_pass "p1_t0_s1" "Initial state captured"
    
    substep "Step 1: Verify setup and schedule a test notification"
    set_test_context "phase1" "phase1_test0" "p1_t0_s2"
    step_start "p1_t0_s2" "Verifying prerequisites and scheduling test notification"
    launch_app
    
    # Comprehensive prerequisite verification (aligned with PHASE1_TEST0_GOLDEN.md step 4)
    # Verifies all 5 items: Plugin Settings, Native Fetcher, Notifications, Exact Alarms, Channel
    verify_all_prerequisites "phase1" "phase1_test0" "p1_t0_s2"
    
    # If prerequisites failed and we're in CI mode, exit
    if [ $? -ne 0 ] && [ "${MODE}" = "ci" ]; then
        error "Prerequisites not met - cannot proceed with Test 0"
        exit 1
    fi
    
    # Capture pre-schedule state
    capture_alarms "test0_before_schedule"
    capture_screenshot "test0_before_schedule"
    
    local initial_count system_count
    initial_count="$(get_plugin_alarm_count)"
    system_count="$(get_system_alarm_count)"
    info "Current notification alarms: ${initial_count} (expected before scheduling: 0)"
    info "System/other alarms: ${system_count} (for context)"
    
    if [ "${initial_count}" -eq "0" ] 2>/dev/null; then
        ok "No existing notification alarms (clean state)"
        
        if [ "${MODE}" != "ci" ]; then
          ui_prompt "1) In the app UI, schedule a daily notification.
          
          For this test, schedule it for a time very soon (e.g., 1-2 minutes from now)
          to observe the rollover behavior.
          
          This will schedule:
          • 1 notification alarm (AlarmManager) for the specified time
          • 1 prefetch job (WorkManager) for 2 minutes before that time"
        else
          info "CI mode: Assuming notification is scheduled manually"
          sleep 3
        fi
        
        sleep 3  # Give alarm time to be registered
        
        # Capture post-schedule state
        capture_alarms "test0_after_schedule"
        capture_screenshot "test0_after_schedule"
        
        local post_schedule_count
        post_schedule_count="$(get_plugin_alarm_count)"
        
        # Preliminary check (final verdict comes after rollover)
        if [ "${post_schedule_count}" -eq "0" ] 2>/dev/null; then
            warn "Found 0 plugin alarms right after scheduling (may be race condition)"
            info "Alarm may not be visible in dumpsys yet - will verify after rollover"
        elif [ "${post_schedule_count}" -eq "1" ] 2>/dev/null; then
            ok "Found 1 notification alarm (expected: 1) - preliminary check passed"
        else
            warn "Found ${post_schedule_count} notification alarms (expected: 1) - DUPLICATES DETECTED!"
            error "This indicates duplicate NOTIFICATION alarms were created (BUG)"
            show_plugin_alarms_compact
        fi
        initial_count="${post_schedule_count}"
    fi
    
    # Show alarm details if we found exactly 1 alarm
    local initial_alarm_time=""
    if [ "${initial_count}" -eq "1" ] 2>/dev/null; then
        ok "Single notification alarm scheduled (one per day)"
        
        local alarm_details
        alarm_details="$($ADB_BIN shell dumpsys alarm | grep -A 3 "org.timesafari.dailynotification" | grep -A 3 "org.timesafari.daily.NOTIFICATION" | head -10)"
        if [ -n "${alarm_details}" ]; then
            info "Notification alarm details:"
            echo "${alarm_details}" | head -5
            echo ""
            # Extract initial alarm time for rollover verification (normalize seconds to 00)
            initial_alarm_time=$(echo "${alarm_details}" | grep -oE "origWhen=[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}" | head -1 | sed 's/origWhen=//' | sed 's/:[0-9][0-9]$/:00/')
            if [ -n "${initial_alarm_time}" ]; then
                info "Initial alarm time: ${initial_alarm_time} (normalized)"
            fi
        fi
    elif [ "${initial_count}" -gt "1" ] 2>/dev/null; then
        warn "Found ${initial_count} notification alarms (expected: 1) - DUPLICATES DETECTED!"
    fi
    
    step_pass "p1_t0_s2" "Notification scheduled"
    
    substep "Step 2: Wait for notification to fire"
    set_test_context "phase1" "phase1_test0" "p1_t0_s3"
    step_start "p1_t0_s3" "Waiting for notification to fire"
    info "The test will automatically verify rollover after the notification fires."
    echo ""
    echo "What will be verified automatically:"
    echo "  • Alarm count (should be exactly 1)"
    echo "  • Alarm time changed (should be 24 hours later)"
    echo "  • Rollover logs present"
    echo ""
    
    if [ "${MODE}" != "ci" ]; then
      ui_prompt "Please wait for the notification to fire (or advance the emulator clock past the scheduled time).

      The test will automatically verify:
      • Only ONE alarm exists (one per day)
      • Alarm time advanced to tomorrow (24 hours later)
      • Rollover occurred (logs will be checked)
      
      Press Enter after the notification has fired (or after advancing the clock)."
    else
      info "CI mode: Waiting for notification to fire..."
      sleep 5  # Give time for rollover if automated
    fi
    
    step_pass "p1_t0_s3" "Notification fired (or time advanced)"
    
    substep "Step 3: Verify rollover state"
    set_test_context "phase1" "phase1_test0" "p1_t0_s4"
    step_start "p1_t0_s4" "Waiting for rollover to complete"
    info "Waiting for rollover to complete (notification should auto-schedule next day's alarm)..."
    info "Allowing up to ~15 seconds for Android to process rollover and update alarms..."
    
    # Wait longer for rollover to complete (notification fires → rollover schedules next day)
    sleep 5  # Give Android time to process the notification fire and rollover
    
    local post_rollover_count system_final
    post_rollover_count="$(wait_for_stable_plugin_alarm_count 5 2)"
    system_final="$(get_system_alarm_count)"
    
    # Capture post-rollover state
    capture_alarms "test0_after_rollover"
    # Look for rollover logs: DN|RESCHEDULE, DN|DISPLAY, DN|RECEIVE, ROLLOVER_ON_FIRE, etc.
    # Also capture JavaScript console logs (Capacitor/Console) for UI debugging
    capture_logcat "test0_after_rollover" "DN|RESCHEDULE|DN|DISPLAY|DN|RECEIVE|ROLLOVER|DNP-SCHEDULE|Capacitor/Console" 200
    capture_screenshot "test0_after_rollover"
    
    step_pass "p1_t0_s4" "Post-rollover evidence captured"
    
    set_test_context "phase1" "phase1_test0" "p1_t0_s6"
    step_start "p1_t0_s6" "Verifying schedule state"
    
    info "Notification alarms after rollover: ${post_rollover_count} (expected: 1)"
    info "System/other alarms: ${system_final} (for context)"
    
    # Extract post-rollover alarm time and verify it changed
    local post_rollover_alarm_time=""
    local rollover_verified=false
    if [ "${post_rollover_count}" -eq "1" ] 2>/dev/null; then
        local post_alarm_details
        post_alarm_details="$($ADB_BIN shell dumpsys alarm | grep -A 3 "org.timesafari.dailynotification" | grep -A 3 "org.timesafari.daily.NOTIFICATION" | head -10)"
        if [ -n "${post_alarm_details}" ]; then
            # Extract post-rollover alarm time (normalize seconds to 00 for comparison)
            post_rollover_alarm_time=$(echo "${post_alarm_details}" | grep -oE "origWhen=[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}" | head -1 | sed 's/origWhen=//' | sed 's/:[0-9][0-9]$/:00/')
            if [ -n "${post_rollover_alarm_time}" ]; then
                info "Post-rollover alarm time: ${post_rollover_alarm_time} (normalized)"
                
                # Verify alarm time changed (rollover occurred)
                # Compare alarm date to current date - if alarm is scheduled for tomorrow or later, rollover worked
                if [ -n "${post_rollover_alarm_time}" ]; then
                    local current_date=$(date +%Y-%m-%d)
                    local post_date=$(echo "${post_rollover_alarm_time}" | cut -d' ' -f1)
                    
                    # Compare dates: if alarm date is >= current date, it's scheduled for today or future (correct)
                    # If we also have initial_alarm_time, check if it advanced
                    if [ -n "${initial_alarm_time}" ]; then
                        local initial_date=$(echo "${initial_alarm_time}" | cut -d' ' -f1)
                        if [ "${initial_date}" != "${post_date}" ]; then
                            ok "Alarm date changed: ${initial_alarm_time} → ${post_rollover_alarm_time}"
                            rollover_verified=true
                        elif [ "${post_date}" \> "${current_date}" ] || [ "${post_date}" = "${current_date}" ]; then
                            # Alarm is scheduled for today or future - this is correct
                            # If initial was also tomorrow, that's fine - rollover logs will confirm it occurred
                            info "Alarm scheduled for ${post_date} (current: ${current_date}) - date unchanged from initial, checking rollover logs"
                            # Don't set rollover_verified yet - let log check determine it
                        else
                            warn "Alarm date ${post_date} is in the past (current: ${current_date}) - rollover may have failed"
                            rollover_verified=false
                        fi
                    else
                        # No initial alarm time to compare, just check if it's scheduled for future
                        if [ "${post_date}" \> "${current_date}" ] || [ "${post_date}" = "${current_date}" ]; then
                            info "Alarm scheduled for ${post_date} (current: ${current_date}) - checking rollover logs for confirmation"
                            # Don't set rollover_verified yet - let log check determine it
                        else
                            warn "Alarm date ${post_date} is in the past (current: ${current_date}) - rollover may have failed"
                            rollover_verified=false
                        fi
                    fi
                fi
            fi
        fi
        
        # Check for rollover logs (wider search including all possible rollover patterns)
        # Look for: ROLLOVER_ON_FIRE source, RESCHEDULE logs, DNP-SCHEDULE with ROLLOVER, etc.
        # Note: DN|RESCHEDULE uses pipe character, so we need to escape it properly
        local rollover_logs=$($ADB_BIN logcat -d -t 1000 | grep -E "ROLLOVER_ON_FIRE|source=ROLLOVER|DNP-SCHEDULE.*ROLLOVER|DN\|RESCHEDULE|DN\|RESCHEDULE_OK|DN\|RESCHEDULE_START|Scheduling next.*notification|scheduleNextNotification|Next notification scheduled" | tail -15 || true)
        if [ -n "${rollover_logs}" ]; then
            ok "Rollover logs found - notification fired and rollover occurred"
            info "Rollover log excerpt:"
            echo "${rollover_logs}" | head -3 | sed 's/^/  /'
            rollover_verified=true
        else
            warn "No rollover logs found - checking if notification fired at all..."
            # Check for any notification display logs
            local notification_logs=$($ADB_BIN logcat -d -t 500 | grep -E "DN\|DISPLAY_NOTIF|Notification.*displayed|org.timesafari.daily.NOTIFICATION" | tail -5 || true)
            if [ -n "${notification_logs}" ]; then
                warn "Notification display logs found, but no rollover logs - rollover may have failed"
                info "Notification log excerpt:"
                echo "${notification_logs}" | head -2 | sed 's/^/  /'
            else
                warn "No notification logs found - notification may not have fired"
            fi
            if [ "${rollover_verified}" = false ]; then
                warn "⚠️  Rollover verification failed: alarm date unchanged AND no rollover logs"
            fi
        fi
    fi
    
    # Final verdict
    if [ "${post_rollover_count}" -eq "1" ] 2>/dev/null && [ "${rollover_verified}" = true ]; then
        step_pass "p1_t0_s6" "Schedule state verified - exactly 1 alarm, rollover confirmed"
        set_test_context "phase1" "phase1_test0" "p1_t0_s7"
        verdict_pass "test0_daily_rollover" "Daily rollover created exactly one NOTIFICATION alarm for tomorrow"
    elif [ "${post_rollover_count}" -eq "1" ] 2>/dev/null && [ "${rollover_verified}" = false ]; then
        step_warn "p1_t0_s6" "Alarm count correct but rollover not verified"
        warn "Found 1 alarm but time did not change - notification may not have fired"
        set_test_context "phase1" "phase1_test0" "p1_t0_s7"
        verdict_warn "test0_daily_rollover" "Alarm count correct (1) but rollover not verified - alarm time unchanged, no rollover logs"
    elif [ "${post_rollover_count}" -gt "1" ] 2>/dev/null; then
        step_fail "p1_t0_s6" "Duplicate alarms detected"
        error "Daily rollover created ${post_rollover_count} NOTIFICATION alarms (duplicates)"
        show_plugin_alarms_compact
        set_test_context "phase1" "phase1_test0" "p1_t0_s7"
        verdict_fail "test0_daily_rollover" "Duplicate alarms detected after rollover (expected: 1, got: ${post_rollover_count})"
    else
        step_warn "p1_t0_s6" "No alarm found after rollover"
        warn "No NOTIFICATION alarm found after rollover (expected: 1, got: 0)"
        show_plugin_alarms_compact
        set_test_context "phase1" "phase1_test0" "p1_t0_s7"
        verdict_warn "test0_daily_rollover" "Rollover may have failed - no alarm found (expected: 1, got: 0)"
    fi
    
    evidence_block "test0_daily_rollover"
    
    if [ "${MODE}" != "ci" ]; then
      pause
    fi
  fi  # End of "if should_run_test 0" block
  
  # ============================================
  # TEST 1: Force-Stop Recovery - Database Restoration
  # ============================================
  if should_run_test "1" SELECTED_TESTS; then
    section "TEST 1: Force-Stop Recovery - Database Restoration"
    
    # Set test context
    set_test_context "phase1" "phase1_test1" ""
    
    info "Purpose: Verify that after force-stop (which clears alarms), recovery"
    info "        uses the database to rebuild alarms on app relaunch."
    info ""
    info "Expected time: 5-8 minutes"
    info "Automatable: Partial (requires manual force-stop verification)"
    echo ""
    
    if [ "${MODE}" != "ci" ]; then
      pause
    fi
    
    # Capture initial state
    set_test_context "phase1" "phase1_test1" "p1_t1_s1"
    step_start "p1_t1_s1" "Clean start - verify no lingering alarms"
    capture_alarms "test1_initial"
    capture_logcat "test1_initial" "DNP" 50
    local lingering_count system_count
    lingering_count="$(get_plugin_alarm_count)"
    system_count="$(get_system_alarm_count)"
    info "Current plugin notification alarms: ${lingering_count}"
    info "System/other alarms: ${system_count} (for context)"
    
    local goto_test1_end=false
    
    if [ "${lingering_count}" -gt "0" ] 2>/dev/null; then
        warn "Found ${lingering_count} lingering plugin alarm(s) - these will interfere with TEST 1"
        info "TEST 1 needs a clean state (no existing plugin alarms)"
        info "Resetting app state via uninstall + reinstall..."
        
        # Uninstall existing app
        info "Uninstalling existing app..."
        set +e
        local uninstall_output uninstall_status
        uninstall_output="$($ADB_BIN uninstall "$APP_ID" 2>&1)"
        uninstall_status=$?
        set -e
        
        if [ $uninstall_status -eq 0 ]; then
            ok "App uninstall succeeded (clean slate)"
        else
            if grep -q "DELETE_FAILED_INTERNAL_ERROR" <<<"$uninstall_output"; then
                info "No existing app to uninstall (continuing)"
            else
                warn "App uninstall reported an error: $uninstall_output (continuing anyway)"
            fi
        fi
        
        # Reinstall APK
        info "Reinstalling APK..."
        if $ADB_BIN install -r "$APK_PATH" >/dev/null 2>&1; then
            ok "App reinstall succeeded"
        else
            error "App reinstall FAILED - cannot proceed with TEST 1"
            warn "Marking TEST 1 as INCONCLUSIVE due to dirty starting state"
            capture_screenshot "test1_dirty_state_reinstall_failed"
            if [ "${MODE}" != "ci" ]; then
              pause
            fi
            goto_test1_end=true
        fi
        
        if [ "${goto_test1_end}" != "true" ]; then
            # Verify install
            if ! $ADB_BIN shell pm list packages | grep -q "$APP_ID"; then
                error "App not found in package list after reinstall - aborting TEST 1"
                capture_screenshot "test1_dirty_state_verify_failed"
                if [ "${MODE}" != "ci" ]; then
                  pause
                fi
                goto_test1_end=true
            fi
        fi
        
        if [ "${goto_test1_end}" != "true" ]; then
            # Clear logs
            info "Clearing logcat buffer after reinstall..."
            $ADB_BIN logcat -c || true
            
            # Re-check plugin alarms
            info "Rechecking plugin alarms after reset..."
            sleep 2
            lingering_count="$(get_plugin_alarm_count)"
            info "Plugin alarms after reset: ${lingering_count} (expected: 0)"
            
            if [ "${lingering_count}" -ne "0" ] 2>/dev/null; then
                warn "TEST 1 starting with non-zero alarm count even after reset; treating as INCONCLUSIVE"
                info "This may indicate device-specific behavior where alarms persist across uninstall"
                capture_screenshot "test1_unexpected_alarms_after_reset"
                if [ "${MODE}" != "ci" ]; then
                  pause
                fi
                goto_test1_end=true
            fi
        fi
        
        if [ "${goto_test1_end}" = "true" ]; then
            warn "TEST 1 skipped due to inability to achieve clean starting state"
            if [ "${MODE}" != "ci" ]; then
              pause
            fi
        else
            ok "App state reset complete. TEST 1 starting from clean state"
        fi
    else
        ok "No lingering plugin alarms found (clean state)"
    fi
    
    step_pass "p1_t1_s1" "Clean state verified"
    
    # Skip remaining TEST 1 steps if we couldn't achieve clean state
    if [ "${goto_test1_end}" != "true" ]; then
    
    substep "Step 2: Schedule a known future alarm"
    set_test_context "phase1" "phase1_test1" "p1_t1_s2"
    step_start "p1_t1_s2" "Scheduling notification"
    launch_app
    ensure_plugin_configured
    
    if [ "${MODE}" != "ci" ]; then
      ui_prompt "1) In the app UI, click the 'Test Notification' button.
      
      This will schedule ONE notification for 4 minutes in the future.
      (App supports one alarm per day)
      
      The alarm will be stored in the database AND scheduled in AlarmManager."
    else
      info "CI mode: Assuming notification is scheduled manually"
      sleep 3
    fi
    
    sleep 2
    
    # Capture before force-stop state
    capture_alarms "test1_before_force_stop"
    capture_logcat "test1_before_force_stop" "DNP-SCHEDULE|DNP-NOTIFY" 50
    
    step_pass "p1_t1_s2" "Notification scheduled"
    
    substep "Step 3: Verify alarm exists in AlarmManager (BEFORE force-stop)"
    set_test_context "phase1" "phase1_test1" "p1_t1_s3"
    step_start "p1_t1_s3" "Verifying alarm before force-stop"
    local alarm_count_before system_count_before
    alarm_count_before="$(get_plugin_alarm_count)"
    system_count_before="$(get_system_alarm_count)"
    info "Plugin alarms: ${alarm_count_before} (expected: 1)"
    info "System/other alarms: ${system_count_before} (for context)"
    
    if [ "${alarm_count_before}" -eq "0" ] 2>/dev/null; then
        error "No plugin alarms found in AlarmManager - cannot test force-stop recovery"
        info "Make sure you clicked 'Test Notification' and wait a moment"
        if [ "${MODE}" != "ci" ]; then
          pause
        fi
        # Re-check
        alarm_count_before="$(get_plugin_alarm_count)"
        if [ "${alarm_count_before}" -eq "0" ] 2>/dev/null; then
            error "Still no alarms found - aborting test"
            verdict_fail "test1_force_stop_recovery" "No alarms found before force-stop - cannot test recovery"
            evidence_block "test1_force_stop_recovery"
            goto_test1_end=true
        fi
    fi
    
    if [ "${alarm_count_before}" -eq "1" ] 2>/dev/null; then
        ok "Single plugin alarm confirmed in AlarmManager (one per day)"
    else
        warn "Found ${alarm_count_before} plugin alarms (expected: 1) - continuing anyway"
    fi
    
    # Extract trigger time for later verification
    local alarm_details_before alarm_trigger_ms alarm_trigger_sec alarm_readable
    alarm_details_before="$($ADB_BIN shell dumpsys alarm | grep -A 3 "$APP_ID" | head -10)"
    if [ -n "${alarm_details_before}" ]; then
        info "Alarm details:"
        echo "${alarm_details_before}" | head -5
        echo ""
    fi
    
    alarm_trigger_ms=$(echo "${alarm_details_before}" | grep -oE "origWhen [0-9]+" | head -1 | awk '{print $2}')
    if [ -n "${alarm_trigger_ms}" ]; then
        alarm_trigger_sec=$((alarm_trigger_ms / 1000))
        alarm_readable=$(date -d "@${alarm_trigger_sec}" 2>/dev/null || echo "${alarm_trigger_sec}")
        info "Alarm scheduled for: ${alarm_readable} (${alarm_trigger_ms} ms)"
    fi
    
    if [ "${MODE}" != "ci" ]; then
      pause
    fi
    
    substep "Step 4: Force-stop the app (clears alarms)"
    set_test_context "phase1" "phase1_test1" "p1_t1_s3"
    step_start "p1_t1_s3" "Force-stopping app"
    warn "Force-stop will clear ALL alarms from AlarmManager"
    info "Executing: $ADB_BIN shell am force-stop ${APP_ID}"
    force_stop_app
    sleep 2
    
    # Capture after force-stop state
    capture_alarms "test1_after_force_stop"
    
    substep "Step 5: Verify alarms are MISSING (cleared by OS)"
    set_test_context "phase1" "phase1_test1" "p1_t1_s4"
    step_start "p1_t1_s4" "Verifying alarms cleared"
    sleep 1
    local alarm_count_after system_count_after
    alarm_count_after="$(get_plugin_alarm_count)"
    system_count_after="$(get_system_alarm_count)"
    info "Plugin alarms after force-stop: ${alarm_count_after} (expected: 0)"
    info "System/other alarms: ${system_count_after} (for context)"
    
    if [ "${alarm_count_after}" -eq "0" ] 2>/dev/null; then
        ok "Plugin alarms cleared by force-stop (count: ${alarm_count_after})"
        info "This confirms: Force-stop cleared alarms from AlarmManager"
        step_pass "p1_t1_s4" "Alarms cleared by force-stop"
    else
        warn "Plugin alarms still present after force-stop (count: ${alarm_count_after})"
        info "Some devices/OS versions may not clear alarms on force-stop"
        info "Continuing test anyway - recovery should still work"
        step_warn "p1_t1_s4" "Alarms not cleared (device-specific behavior)"
    fi
    
    if [ "${MODE}" != "ci" ]; then
      pause
    fi
    
    substep "Step 6: Relaunch app (triggers recovery from database)"
    set_test_context "phase1" "phase1_test1" "p1_t1_s4"
    step_start "p1_t1_s4" "Relaunching app to trigger recovery"
    clear_logs
    launch_app
    sleep 4  # Give recovery time to run
    
    substep "Step 7: Verify recovery rebuilt alarms from database"
    set_test_context "phase1" "phase1_test1" "p1_t1_s4"
    step_start "p1_t1_s4" "Verifying recovery rebuilt alarms"
    sleep 2
    
    # Capture after recovery state
    capture_alarms "test1_after_recovery"
    capture_logcat "test1_after_recovery" "DNP-REACTIVATION" 100
    capture_screenshot "test1_after_recovery"
    
    local alarm_count_recovered system_count_recovered
    alarm_count_recovered="$(get_plugin_alarm_count)"
    system_count_recovered="$(get_system_alarm_count)"
    info "Plugin alarms after recovery: ${alarm_count_recovered} (expected: 1)"
    info "System/other alarms: ${system_count_recovered} (for context)"
    
    info "Checking recovery logs..."
    check_recovery_logs
    
    info "Expected log output:"
    echo "  DNP-REACTIVATION: Starting app launch recovery"
    echo "  DNP-REACTIVATION: Rescheduled alarm: <id> for <time>"
    echo "  DNP-REACTIVATION: Cold start recovery complete: ... rescheduled>=1, ..."
    echo ""
    
    # Check recovery logs for rescheduling and recovery source
    local recovery_result rescheduled_count verified_count recovery_source
    recovery_result="$($ADB_BIN logcat -d | grep "Cold start recovery complete\|Boot recovery complete" | tail -1)"
    rescheduled_count=$(echo "${recovery_result}" | grep -oE "rescheduled=[0-9]+" | grep -oE "[0-9]+" || echo "0")
    verified_count=$(echo "${recovery_result}" | grep -oE "verified=[0-9]+" | grep -oE "[0-9]+" || echo "0")
    recovery_source="$($ADB_BIN logcat -d | grep -E "recovery source|from database|DATABASE" | tail -1 || echo "")"
    
    # Pass/fail criteria
    local test1_passed=false
    
    if [ "${alarm_count_recovered}" -gt "0" ] 2>/dev/null; then
        ok "Alarms restored in AlarmManager (count: ${alarm_count_recovered})"
        if [ "${rescheduled_count}" -gt "0" ] 2>/dev/null; then
            ok "Recovery logs confirm rescheduling (rescheduled=${rescheduled_count})"
            test1_passed=true
        else
            warn "Alarms restored but logs show rescheduled=0"
            info "This might be okay if alarms were verified instead of rescheduled"
            # Still pass if alarms are restored, even if rescheduled=0
            test1_passed=true
        fi
    else
        error "No alarms restored (count: ${alarm_count_recovered})"
        info "Recovery may have failed or alarms were not in database"
    fi
    
    # Final verdict
    if [ "${test1_passed}" = "true" ]; then
        step_pass "p1_t1_s4" "Recovery successful"
        set_test_context "phase1" "phase1_test1" "p1_t1_s5"
        verdict_pass "test1_force_stop_recovery" "Recovery successfully rebuilt alarms from database (before: ${alarm_count_before}, after stop: ${alarm_count_after}, after recovery: ${alarm_count_recovered}, rescheduled: ${rescheduled_count})"
    else
        step_fail "p1_t1_s4" "Recovery failed"
        set_test_context "phase1" "phase1_test1" "p1_t1_s5"
        verdict_fail "test1_force_stop_recovery" "Recovery did not rebuild alarms correctly (before: ${alarm_count_before}, after stop: ${alarm_count_after}, after recovery: ${alarm_count_recovered})"
    fi
    
    evidence_block "test1_force_stop_recovery"
    
    # Verify alarm fires if it's scheduled within reasonable time window (< 5 minutes)
    # This ensures restored alarms actually work, not just that they were restored
    if [ -n "${alarm_trigger_ms}" ] && [ "${goto_test1_end}" != "true" ]; then
        local current_time_sec current_time_ms wait_ms wait_sec
        current_time_sec=$(get_current_time)
        current_time_ms=$((current_time_sec * 1000))
        wait_ms=$((alarm_trigger_ms - current_time_ms))
        
        # Auto-enable fire verification if alarm is within 5 minutes (300 seconds)
        # Or if VERIFY_FIRE is explicitly set to true
        local should_verify_fire=false
        if [ "${VERIFY_FIRE}" = "true" ]; then
            should_verify_fire=true
        elif [ "${wait_ms}" -gt 0 ] && [ "${wait_ms}" -lt 300000 ]; then
            # Alarm is in the future and within 5 minutes - auto-verify
            should_verify_fire=true
        fi
        
        if [ "${should_verify_fire}" = "true" ]; then
            substep "Step 8: Verify restored alarm fires at scheduled time"
            set_test_context "phase1" "phase1_test1" "p1_t1_s6"
            
            if [ "${wait_ms}" -lt 0 ]; then
                step_warn "p1_t1_s6" "Alarm time already passed"
                warn "Alarm time already passed (${wait_ms} ms ago); cannot verify fire"
            else
                wait_sec=$((wait_ms / 1000))
                
                # Clamp upper bound to prevent accidentally waiting too long
                if [ "${wait_sec}" -gt 600 ]; then
                    step_warn "p1_t1_s6" "Alarm too far in future"
                    warn "Alarm is >10 minutes away (${wait_sec}s); skipping fire verification"
                    info "To test fire verification, schedule alarm closer to current time"
                else
                    step_start "p1_t1_s6" "Waiting for restored alarm to fire"
                    info "Restored alarm scheduled for: ${alarm_readable}"
                    info "Current time: $(date -d "@${current_time_sec}" 2>/dev/null || echo "${current_time_sec}")"
                    info "Waiting ~${wait_sec} seconds for restored alarm to fire..."
                    
                    clear_logs
                    sleep ${wait_sec}
                    sleep 2
                    
                    info "Checking logs for fired alarm..."
                    local alarm_fired
                    alarm_fired="$($ADB_BIN logcat -d | grep -E "DNP-RECEIVE|DNP-NOTIFY|DNP-WORK|Alarm fired|Notification displayed" | tail -10)"
                    
                    if [ -n "${alarm_fired}" ]; then
                        step_pass "p1_t1_s6" "Restored alarm fired successfully"
                        ok "✅ Restored alarm fired! Logs:"
                        echo "${alarm_fired}"
                    else
                        step_fail "p1_t1_s6" "Restored alarm did not fire"
                        warn "⚠️ No alarm fire logs found - restored alarm may not have fired"
                        info "Check notification tray manually or review recent logs"
                    fi
                fi
            fi
        fi
    fi
    
    fi  # End of "if goto_test1_end != true" block
    
    if [ "${MODE}" != "ci" ]; then
      pause
    fi
  fi  # End of "if should_run_test 1" block
  
  # ============================================
  # TEST 2: Schedule Update (One-Per-Day Semantics)
  # ============================================
  if should_run_test "2" SELECTED_TESTS; then
    section "TEST 2: Schedule Update Verification"
    
    # Set test context
    set_test_context "phase1" "phase1_test2" ""
    
    info "Purpose: Verify that updating the schedule time maintains 'one per day' semantics"
    info ""
    info "Expected time: 4-6 minutes"
    info "Automatable: Partial (requires manual schedule update)"
    echo ""
    
    if [ "${MODE}" != "ci" ]; then
      pause
    fi
    
    # Capture initial state
    set_test_context "phase1" "phase1_test2" "p1_t2_s1"
    step_start "p1_t2_s1" "Schedule initial notification"
    capture_alarms "test2_initial"
    capture_logcat "test2_initial" "DNP" 50
    
    substep "Step 1: Launch app and verify initial schedule"
    launch_app
    ensure_plugin_configured
    
    # Get initial alarm count
    local initial_alarm_count system_alarm_count
    initial_alarm_count="$(get_plugin_alarm_count)"
    system_alarm_count="$(get_system_alarm_count)"
    info "Plugin alarms: ${initial_alarm_count} (expected: 1)"
    info "System/other alarms: ${system_alarm_count} (for context)"
    
    if [ "${initial_alarm_count}" -eq "1" ] 2>/dev/null; then
        ok "Initial alarm confirmed (one per day)"
        step_pass "p1_t2_s1" "Initial notification scheduled"
    elif [ "${initial_alarm_count}" -eq "0" ] 2>/dev/null; then
        warn "No initial alarm found - scheduling one first..."
        if [ "${MODE}" != "ci" ]; then
          ui_prompt "1) In the app UI, schedule a daily notification (e.g., click 'Test Notification')."
        else
          info "CI mode: Assuming notification is scheduled manually"
          sleep 2
        fi
        sleep 2
        initial_alarm_count="$(get_plugin_alarm_count)"
        if [ "${initial_alarm_count}" -eq "1" ] 2>/dev/null; then
            ok "Alarm scheduled"
            step_pass "p1_t2_s1" "Initial notification scheduled"
        else
            error "Failed to schedule initial alarm"
            step_fail "p1_t2_s1" "Failed to schedule"
            if [ "${MODE}" != "ci" ]; then
              pause
            fi
        fi
    else
        warn "Found ${initial_alarm_count} plugin alarms (expected: 1) - continuing anyway"
        step_warn "p1_t2_s1" "Unexpected alarm count"
    fi
    
    # Capture before update state
    capture_alarms "test2_before_update"
    
    substep "Step 2: Update schedule time"
    set_test_context "phase1" "phase1_test2" "p1_t2_s2"
    step_start "p1_t2_s2" "Update schedule"
    if [ "${MODE}" != "ci" ]; then
      ui_prompt "2) In the app UI, change the schedule time (e.g., from 06:50 to 07:10)
      and apply the schedule.
      
      This should cancel the old alarm and schedule a new one at the new time.
      You should still have exactly 1 alarm (one per day)."
    else
      info "CI mode: Assuming schedule is updated manually"
      sleep 3
    fi
    
    sleep 3
    
    # Capture after update state
    capture_alarms "test2_after_update"
    capture_logcat "test2_after_update" "DNP-SCHEDULE|DNP-NOTIFY" 50
    
    local updated_alarm_count
    updated_alarm_count="$(get_plugin_alarm_count)"
    info "Plugin alarms after update: ${updated_alarm_count} (expected: 1)"
    
    local test2_failed=false
    
    if [ "${updated_alarm_count}" -eq "1" ] 2>/dev/null; then
        ok "Single alarm confirmed after schedule update (one per day maintained)"
        step_pass "p1_t2_s2" "Schedule updated"
    elif [ "${updated_alarm_count}" -gt "1" ] 2>/dev/null; then
        error "TEST 2 FAILED: Found ${updated_alarm_count} plugin alarms after update (expected: 1)"
        error "Old alarm was NOT canceled - violates 'one per day' semantics"
        info "This indicates the plugin did not clean up existing schedules before creating new one"
        test2_failed=true
        step_fail "p1_t2_s2" "Multiple alarms detected"
    else
        warn "Found ${updated_alarm_count} plugin alarms (expected: 1) - no alarm scheduled after update"
        step_warn "p1_t2_s2" "No alarm after update"
    fi
    
    substep "Step 3: Kill app and relaunch (triggers recovery)"
    set_test_context "phase1" "phase1_test2" "p1_t2_s3"
    step_start "p1_t2_s3" "Verify one-per-day semantics"
    kill_app
    clear_logs
    launch_app
    
    substep "Step 4: Check recovery logs and verify alarm count"
    sleep 3
    
    # Capture after recovery state
    capture_alarms "test2_after_recovery"
    capture_logcat "test2_after_recovery" "DNP-REACTIVATION" 100
    
    check_recovery_logs
    
    info "Expected log output (either):"
    echo "  DNP-REACTIVATION: Verified scheduled alarm: <id> at <time>"
    echo "    OR"
    echo "  DNP-REACTIVATION: Rescheduled missing alarm: <id> at <time>"
    echo "  DNP-REACTIVATION: Cold start recovery complete: ..., verified=1 or rescheduled=1, ..."
    echo ""
    
    local recovery_result rescheduled_count verified_count alarm_count_after_recovery
    recovery_result="$($ADB_BIN logcat -d | grep "Cold start recovery complete" | tail -1)"
    rescheduled_count=$(echo "${recovery_result}" | grep -oE "rescheduled=[0-9]+" | grep -oE "[0-9]+" || echo "0")
    verified_count=$(echo "${recovery_result}" | grep -oE "verified=[0-9]+" | grep -oE "[0-9]+" || echo "0")
    alarm_count_after_recovery="$(get_plugin_alarm_count)"
    info "Plugin alarms after recovery: ${alarm_count_after_recovery} (expected: 1)"
    
    # CRITICAL: Test fails if alarm count > 1 after recovery (violates "one per day")
    if [ "${alarm_count_after_recovery}" -gt "1" ] 2>/dev/null; then
        error "TEST 2 FAILED: Found ${alarm_count_after_recovery} plugin alarms after recovery (expected: 1)"
        error "Multiple schedules in database caused recovery to reschedule duplicates"
        error "This violates 'one per day' semantics - only one notification per day should exist"
        test2_failed=true
        step_fail "p1_t2_s3" "Multiple alarms after recovery"
    elif [ "${alarm_count_after_recovery}" -eq "1" ] 2>/dev/null; then
        step_pass "p1_t2_s3" "One-per-day semantics verified"
    else
        step_warn "p1_t2_s3" "Inconclusive alarm count"
    fi
    
    # Final verdict
    set_test_context "phase1" "phase1_test2" "p1_t2_s4"
    if [ "${test2_failed}" = "true" ]; then
        verdict_fail "test2_schedule_update" "Multiple alarms detected - violates 'one per day' semantics (after update: ${updated_alarm_count}, after recovery: ${alarm_count_after_recovery})"
    elif [ "${alarm_count_after_recovery}" -eq "1" ] 2>/dev/null; then
        if [ "${rescheduled_count}" -gt "0" ] 2>/dev/null; then
            verdict_pass "test2_schedule_update" "Schedule update maintained one per day semantics (rescheduled: ${rescheduled_count})"
        elif [ "${verified_count}" -gt "0" ] 2>/dev/null; then
            verdict_pass "test2_schedule_update" "Schedule update maintained one per day semantics (verified: ${verified_count})"
        else
            verdict_pass "test2_schedule_update" "Schedule update maintained one per day semantics (no recovery needed)"
        fi
    else
        verdict_warn "test2_schedule_update" "Schedule update result inconclusive (after update: ${updated_alarm_count}, after recovery: ${alarm_count_after_recovery})"
    fi
    
    evidence_block "test2_schedule_update"
    
    if [ "${MODE}" != "ci" ]; then
      pause
    fi
  fi  # End of "if should_run_test 2" block
  
  # ============================================
  # TEST 3: Recovery Timeout
  # ============================================
  if should_run_test "3" SELECTED_TESTS; then
    section "TEST 3: Recovery Timeout"
    
    # Set test context
    set_test_context "phase1" "phase1_test3" ""
    
    info "Purpose: Verify recovery times out gracefully"
    info ""
    info "Expected time: 2-3 minutes"
    info "Automatable: Yes (code verification only)"
    info "Note: Full test (100+ schedules) requires manual execution"
    echo ""
    
    if [ "${MODE}" != "ci" ]; then
      pause
    fi
    
    # Capture initial state
    capture_alarms "test3_initial"
    
    substep "Step 1: Check recovery timeout implementation"
    set_test_context "phase1" "phase1_test3" "p1_t3_s1"
    step_start "p1_t3_s1" "Check recovery timeout implementation"
    local timeout_found timeout_protection_found
    timeout_found=false
    timeout_protection_found=false
    
    if grep -q "RECOVERY_TIMEOUT_SECONDS.*2L" "${PROJECT_ROOT}/android/src/main/java/org/timesafari/dailynotification/ReactivationManager.kt" 2>/dev/null; then
        ok "Timeout is set to 2 seconds"
        timeout_found=true
    else
        error "Timeout not found in code"
    fi
    
    if grep -q "withTimeout" "${PROJECT_ROOT}/android/src/main/java/org/timesafari/dailynotification/ReactivationManager.kt" 2>/dev/null; then
        ok "Timeout protection is implemented"
        timeout_protection_found=true
    else
        error "Timeout protection not found"
    fi
    
    # Capture evidence
    capture_logcat "test3_timeout_check" "DNP-REACTIVATION" 50
    
    if [ "${timeout_found}" = "true" ] && [ "${timeout_protection_found}" = "true" ]; then
        step_pass "p1_t3_s1" "Timeout mechanism verified"
    else
        step_fail "p1_t3_s1" "Timeout mechanism not found"
    fi
    
    # Final verdict
    set_test_context "phase1" "phase1_test3" "p1_t3_s2"
    if [ "${timeout_found}" = "true" ] && [ "${timeout_protection_found}" = "true" ]; then
        verdict_pass "test3_recovery_timeout" "Timeout mechanism verified in code (2 seconds, withTimeout protection)"
    else
        verdict_fail "test3_recovery_timeout" "Timeout mechanism not properly implemented (timeout found: ${timeout_found}, protection found: ${timeout_protection_found})"
    fi
    
    evidence_block "test3_recovery_timeout"
    
    if [ "${MODE}" != "ci" ]; then
      pause
    fi
  fi  # End of "if should_run_test 3" block
  
  # ============================================
  # TEST 4: Invalid Data Handling
  # ============================================
  if should_run_test "4" SELECTED_TESTS; then
    section "TEST 4: Invalid Data Handling"
    
    # Set test context
    set_test_context "phase1" "phase1_test4" ""
    
    info "Purpose: Verify invalid data doesn't crash recovery"
    info ""
    info "Expected time: 5-8 minutes"
    info "Automatable: Partial (requires debuggable app or manual injection)"
    info "Note: This test injects invalid data (empty IDs, null nextRunAt) and"
    info "      verifies that recovery handles it gracefully without crashing"
    echo ""
    
    if [ "${MODE}" != "ci" ]; then
      pause
    fi
    
    # Capture initial state
    set_test_context "phase1" "phase1_test4" "p1_t4_s1"
    step_start "p1_t4_s1" "Inject invalid test data"
    capture_alarms "test4_initial"
    capture_logcat "test4_initial" "DNP" 50
    
    substep "Step 1: Inject invalid test data"
    
    # Clear logs before test
    clear_logs
    
    # Check if app is debuggable (look for DEBUGGABLE flag)
    local is_debuggable=false
    if $ADB_BIN shell dumpsys package "${APP_ID}" | grep -qi "DEBUGGABLE"; then
        is_debuggable=true
    fi
    
    if [ "${is_debuggable}" = "true" ]; then
        ok "App is debuggable - can inject data via database"
        
        substep "Step 2: Inject invalid data via direct database access"
        
        # Launch app first to ensure database is initialized
        info "Launching app to initialize database..."
        $ADB_BIN shell am start -n "${APP_ID}/.MainActivity" > /dev/null 2>&1
        sleep 2
        
        # Stop app before database injection (prevents locking issues)
        info "Stopping app before database injection..."
        $ADB_BIN shell am force-stop "${APP_ID}"
        sleep 1
        
        # Inject invalid data via direct database access
        info "Injecting invalid test data into database..."
        
        # Calculate next run time (24 hours from now in milliseconds)
        local next_run past_time now_time
        next_run=$(($(date +%s) * 1000 + 86400000))
        past_time=$(($(date +%s) * 1000 - 3600000))
        now_time=$(($(date +%s) * 1000))
        
        # Inject schedule with empty ID
        info "  - Injecting schedule with empty ID..."
        $ADB_BIN shell "run-as ${APP_ID} sqlite3 databases/daily_notification_plugin.db \"INSERT OR REPLACE INTO schedules (id, kind, cron, clockTime, enabled, nextRunAt, jitterMs, backoffPolicy) VALUES ('', 'notify', '0 9 * * *', '09:00', 1, ${next_run}, 0, 'exp');\"" 2>&1 || true
        
        # Inject schedule with null nextRunAt
        info "  - Injecting schedule with null nextRunAt..."
        $ADB_BIN shell "run-as ${APP_ID} sqlite3 databases/daily_notification_plugin.db \"INSERT OR REPLACE INTO schedules (id, kind, cron, clockTime, enabled, nextRunAt, jitterMs, backoffPolicy) VALUES ('test_null_nextrunat', 'notify', '0 9 * * *', '09:00', 1, NULL, 0, 'exp');\"" 2>&1 || true
        
        # Checkpoint WAL file to ensure changes are visible
        info "  - Checkpointing WAL file..."
        $ADB_BIN shell "run-as ${APP_ID} sqlite3 databases/daily_notification_plugin.db \"PRAGMA wal_checkpoint(FULL);\"" 2>&1 || true
        
        # Verify data was inserted
        info "Verifying data injection..."
        local schedule_count
        schedule_count="$($ADB_BIN shell "run-as ${APP_ID} sqlite3 databases/daily_notification_plugin.db \"SELECT COUNT(*) FROM schedules;\"" 2>&1 | tr -d '\r\n')"
        
        info "  - Schedules in database: ${schedule_count}"
        
        if [ "${schedule_count}" -gt "0" ] 2>/dev/null; then
            ok "Invalid test data injected successfully"
            step_pass "p1_t4_s1" "Invalid data injected"
        else
            warn "No schedules found after injection - data may not have been inserted"
            step_warn "p1_t4_s1" "Data injection may have failed"
        fi
        
        substep "Step 3: Trigger recovery with invalid data"
        set_test_context "phase1" "phase1_test4" "p1_t4_s2"
        step_start "p1_t4_s2" "Trigger recovery with invalid data"
        $ADB_BIN shell am start -n "${APP_ID}/.MainActivity" > /dev/null 2>&1
        sleep 3  # Give recovery time to run
        
        info "Waiting for recovery to complete..."
        sleep 2
        
        # Capture after recovery state
        capture_alarms "test4_after_recovery"
        capture_logcat "test4_after_recovery" "DNP-REACTIVATION|Skipping invalid" 100
        capture_screenshot "test4_after_recovery"
        step_pass "p1_t4_s2" "Recovery triggered"
        
        substep "Step 4: Check recovery logs for invalid data handling"
        set_test_context "phase1" "phase1_test4" "p1_t4_s3"
        step_start "p1_t4_s3" "Check recovery logs"
        
        # Check logs
        local recovery_logs
        recovery_logs="$($ADB_BIN logcat -d | grep -E "DNP-REACTIVATION|Skipping invalid|TEST:" | tail -30)"
        
        echo ""
        info "Recovery logs:"
        echo "${recovery_logs}"
        echo ""
        
        # Check for invalid data handling
        local test4_passed=false
        local test4_failed=false
        
        if echo "${recovery_logs}" | grep -q "Skipping invalid"; then
            ok "Invalid data was detected and skipped"
            echo "${recovery_logs}" | grep "Skipping invalid"
            test4_passed=true
        else
            warn "No 'Skipping invalid' logs found"
            info "This could mean:"
            echo "  - Invalid data wasn't injected (database constraints prevented it)"
            echo "  - Recovery didn't encounter invalid data"
            echo "  - Logs were cleared"
        fi
        
        if echo "${recovery_logs}" | grep -q "recovery complete\|Recovery completed"; then
            ok "Recovery completed successfully"
            if [ "${test4_passed}" = "false" ]; then
                test4_passed=true  # Recovery completed = passed (even if no invalid data found)
            fi
        else
            warn "Recovery completion message not found in logs"
        fi
        
        if echo "${recovery_logs}" | grep -qiE "crash|fatal|exception.*recovery|Failed.*recovery"; then
            error "TEST 4 FAILED: Recovery crashed or threw fatal exception"
            test4_failed=true
            step_fail "p1_t4_s3" "Recovery crashed"
        else
            step_pass "p1_t4_s3" "Recovery handled invalid data gracefully"
        fi
        
        # Final verdict
        set_test_context "phase1" "phase1_test4" "p1_t4_s4"
        if [ "${test4_failed}" = "true" ]; then
            verdict_fail "test4_invalid_data_handling" "Recovery crashed or threw fatal exception when handling invalid data"
        elif [ "${test4_passed}" = "true" ]; then
            verdict_pass "test4_invalid_data_handling" "Recovery handled invalid data gracefully (or no invalid data found)"
        else
            verdict_warn "test4_invalid_data_handling" "Could not verify invalid data handling"
        fi
        
    else
        info "App is not debuggable - cannot inject data via database"
        info "TEST 4: Code review confirms invalid data handling exists"
        info "  - ReactivationManager.kt checks for empty IDs"
        info "  - Errors are logged but don't crash recovery"
        verdict_warn "test4_invalid_data_handling" "App not debuggable - cannot inject invalid data for testing"
    fi
    
    evidence_block "test4_invalid_data_handling"
    
    if [ "${MODE}" != "ci" ]; then
      pause
    fi
  fi  # End of "if should_run_test 4" block
  
  # ============================================
  # Summary
  # ============================================
  section "Testing Complete"
  
  info "Test Results Summary:"
  echo ""
  echo "All test verdicts are shown above with evidence locations."
  echo "Review evidence in: $(get_run_dir)"
  echo ""
  
  info "All recovery logs:"
  echo ""
  $ADB_BIN logcat -d | grep "$REACTIVATION_TAG" | tail -20
  echo ""
  
  ok "Phase 1 testing script complete!"
  echo ""
  echo "Next steps:"
  echo "  - Review evidence in: $(get_run_dir)"
  echo "  - Verify all test verdicts above"
  echo "  - Check database if needed (debuggable app)"
  echo "  - Update documentation with test results"
}

# Run main function
main "$@"

