#!/bin/bash
#
# launch.sh — boot the Vision Pro simulator, install and launch HelloVisionOS
# - Niall George Horn; June 2026

# Info (stuff I totally knew before Claude Code helped find what the issue was...): 
# Handles the FrontBoard scene state problem that causes assertion crashes on
# re-launch: shuts down the sim, wipes the app's scene session from the
# FrontBoard sqlite DB, reboots clean. If you skip this and just reinstall,
# SpringBoard will try to restore a scene state that doesn't match the new
# build and crash on startup. Fun times.
#
# Usage:
#   ./deploy/launch.sh
#
# Run build.sh first. setup.sh must have been run this boot (/tmp cleared on restart).
#
# After launch, tap "Enter Immersive Space" in the Simulator window.
# Stream the live CoreIK log from a second terminal:
#   tail -f /tmp/coreik_invocations.log
#

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
REPO_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"

RED='\033[0;31m'; GREEN='\033[0;32m'; CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m'
ok()   { echo -e "${GREEN}  ✅ $*${RESET}"; }
fail() { echo -e "${RED}  ❌ $*${RESET}"; exit 1; }
info() { echo -e "${CYAN}  ℹ  $*${RESET}"; }

echo ""
echo -e "${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo -e "${BOLD}  Persona_CoreIKDemo — Launch${RESET}"
echo -e "${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo ""

# =========== Find built .app ===========

APP_PATH=$(find "${REPO_DIR}/build" -name "HelloVisionOS.app" -path "*xrsimulator*" 2>/dev/null | head -1)
[ -n "$APP_PATH" ] || fail "No built app found — run ./deploy/build.sh first"
info "App: ${APP_PATH}"

# =========== Find Vision Pro simulator UDID ===========
# The binary has minos 26.2 — it will not install on the visionOS 1.x simulator
# Side note -  I ran 26.2 in the Tribunal demo, 26.4/5 were not installed. 
# Anyway 26.2 appears first in `simctl list`. Filter to 26+ sections only, and prefer
# the version that matches the staged CoreIK BUILD tag.
#
# Selection order:
#   1. Booted sim from the BUILD-matched OS version
#   2. Any booted 26+ sim
#   3. Shutdown sim from the BUILD-matched OS version
#   4. Any shutdown 26+ sim (tail = highest version)

SIMCTL_OUT=$(xcrun simctl list devices 2>/dev/null)

# Map BUILD tag → preferred visionOS section string (matches simctl header)
PREFERRED_OS=""
if [ -f "/tmp/CoreIK.framework/BUILD" ]; then
    case "$(cat /tmp/CoreIK.framework/BUILD)" in
        23N301)   PREFERRED_OS="26.2" ;;
        23O249a)  PREFERRED_OS="26.4" ;;
        23O470)   PREFERRED_OS="26.5" ;;
    esac
fi

# Extract all lines from visionOS 26+ sections (avoids 1.x sim)
MODERN_SIMS=$(printf '%s\n' "$SIMCTL_OUT" | awk '
    /-- visionOS / { in_s = ($0 ~ /2[6-9]\.|[3-9][0-9]\./) }
    /^--/ && !/-- visionOS / { in_s = 0 }
    in_s && /Apple Vision Pro/ { print }
')

# Extract lines from the preferred version section only
PREFERRED_SIMS=""
if [ -n "$PREFERRED_OS" ]; then
    PREFERRED_SIMS=$(printf '%s\n' "$SIMCTL_OUT" | awk -v os="$PREFERRED_OS" '
        /-- visionOS / { in_s = (index($0, os) > 0) }
        /^--/ && !/-- visionOS / { in_s = 0 }
        in_s && /Apple Vision Pro/ { print }
    ')
fi

UDID=""
# 1. Booted sim matching BUILD version
[ -z "$UDID" ] && [ -n "$PREFERRED_SIMS" ] && \
    UDID=$(printf '%s\n' "$PREFERRED_SIMS" | grep "Booted" | head -1 | sed -E 's/.*\(([A-F0-9-]{36})\).*/\1/') || true
# 2. Any booted 26+ sim
[ -z "$UDID" ] && \
    UDID=$(printf '%s\n' "$MODERN_SIMS" | grep "Booted" | head -1 | sed -E 's/.*\(([A-F0-9-]{36})\).*/\1/') || true
# 3. Shutdown sim matching BUILD version
[ -z "$UDID" ] && [ -n "$PREFERRED_SIMS" ] && \
    UDID=$(printf '%s\n' "$PREFERRED_SIMS" | head -1 | sed -E 's/.*\(([A-F0-9-]{36})\).*/\1/') || true
# 4. Any shutdown 26+ sim — tail = highest version in list
[ -z "$UDID" ] && \
    UDID=$(printf '%s\n' "$MODERN_SIMS" | tail -1 | sed -E 's/.*\(([A-F0-9-]{36})\).*/\1/') || true

[ -n "$UDID" ] || fail "No Apple Vision Pro simulator (visionOS 26+) found. Install a runtime: Xcode → Settings → Platforms"
info "Vision Pro sim UDID: ${UDID}"

# =========== Clear FrontBoard scene state ===========
# FrontBoard keeps open scene sessions (WindowGroup, ImmersiveSpace) in an
# sqlite DB. If these don't match the current build's scene manifest,
# AppSceneDelegate crashes on launch. Must kill Simulator.app before touching
# the DB — FrontBoard holds state in memory and will overwrite changes if running.
# I had no idea wtf FrontBoard was untill recently either! 

info "Shutting down simulator and clearing FrontBoard scene state..."

SIM_STATE=$(xcrun simctl list devices 2>/dev/null | grep "$UDID" | grep -o "Booted" | head -1 || true)
if [ "$SIM_STATE" = "Booted" ]; then
    xcrun simctl shutdown "$UDID" 2>/dev/null || true
fi
osascript -e 'tell application "Simulator" to quit' 2>/dev/null || true
killall -9 "Simulator" 2>/dev/null || true
sleep 1

FB_DB="${HOME}/Library/Developer/CoreSimulator/Devices/${UDID}/data/Library/FrontBoard/applicationState.db"
if [ -f "$FB_DB" ]; then
    sqlite3 "$FB_DB" "
DELETE FROM kvs WHERE application_identifier IN (SELECT rowid FROM application_identifier_tab WHERE application_identifier = 'RottenOS.HelloVisionOS');
DELETE FROM application_identifier_tab WHERE application_identifier = 'RottenOS.HelloVisionOS';
" 2>/dev/null || true
    info "FrontBoard scene state cleared"
fi

# =========== Boot, install, launch ===========

info "Booting simulator..."
xcrun simctl boot "$UDID" 2>/dev/null || true

WAITED=0
until xcrun simctl list devices 2>/dev/null | grep "$UDID" | grep -q "Booted"; do
    sleep 2; WAITED=$((WAITED + 2))
    [ $WAITED -ge 60 ] && fail "Simulator did not boot within 60 seconds"
done
ok "Simulator booted"

open -a Simulator 2>/dev/null || true

info "Uninstalling previous build..."
xcrun simctl uninstall "$UDID" RottenOS.HelloVisionOS 2>/dev/null || true

info "Installing app..."
xcrun simctl install "$UDID" "$APP_PATH"
ok "App installed"

info "Launching RottenOS.HelloVisionOS..."
xcrun simctl launch "$UDID" RottenOS.HelloVisionOS > /dev/null 2>&1 || true

echo ""
ok "App launched"
echo ""
echo -e "  Tap ${BOLD}Enter Immersive Space${RESET} in the Simulator window."
echo ""
echo -e "  Stream the live CoreIK log (second terminal):"
echo -e "    ${CYAN}tail -f /tmp/coreik_invocations.log${RESET}"
echo ""
echo -e "  Or run: ${CYAN}./deploy/logs.sh${RESET}"
echo ""
echo -e "${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo ""
