#!/bin/sh
# Kuro Tracking for Linux: the full-screen TV app the kuro-tracking .deb installs as
# /opt/kuro-tracking/kuro-screen-launch (owner 2026-10-14: kiosk and classview "as separate apps" for
# the TVs, "frameless... open nothing else but the one page"). Generated for production
# (https://kuro.solutions) by tools/build/linux/build-screen-deb.sh; the Linux twin of
# tools/build/updater/kuro-screen.ps1 and a trimmed copy of kuro-launch.sh (no Kuro extension).
#
#   kuro-screen-launch            open the app (app menu, desktop icon, and the autostart entry)
#   kuro-screen-launch --update   the hourly run (systemd USER timer kuro-tracking-updater.timer)
#   kuro-screen-launch --forget   removes THIS user's data for the app (profile, sign-in, Helium copy)
#
# One full-screen window (--kiosk --app) on /kp/features/tracking/station.html in ~/.local/share/KuroTracking/
# (Helium/, Profile/, kuro-screen.log). Helium is ONLY the exact release Kuro pins: the RSA-signed
# <base>/ext/kuro-browser-linux.json, or the pin built into this package. Close it with Ctrl+W.
set -u
KURO_BASE='https://kuro.solutions'
KURO_ENV='production'
KURO_NAME='KuroTracking'
KURO_PKG='kuro-tracking'
KURO_TITLE='Kuro Tracking'
KURO_PAGE='/kp/features/tracking/station.html'
PUBKEY='-----BEGIN PUBLIC KEY-----
MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAxWHEId1WHwCgrkKoSQu3
3Q/qLJJ2nIzgEUcaJZfBA/FQvqg2QAGo3CwzEeUihuqdfKWmeEXgJHyWR5DhUKpq
xt3gkTALiZdtdZ1yJaV7Ip8RcgPtOxUWRrie9ldEyu9mFJEUBb6XWkIRtj0RKVx0
d68uxbYxxjhGLE92c0aDCdwx4BGNbLTYOvkMmT3YHwyF+zU0oF8yCegSHYSRCjdJ
HYMYEhxyobAule1SGIzpLDZSzkSJryIVinfmY6rDfrQWE0ipomYdK3XV7x5t0kKO
jh4Sjn4yUfARd1Oq36oMvoL1dCHOOuOo+3m5r4bwHpZtW1K9jJV7fIbm2IAe2crX
z7AmtNklubKHpU7JUKDl8+yVCfqiV1+qCuXilcelYFA0bEJLopIRfZqsE1EZPC+k
eyT+iD3AFJ2zRoy8098NV98rNTqTY4uj3sRZSDx0sNqsoKGWn/TX6UpYkKYEe9WS
VU0qoFCNYXQZF/Qzmpq6JwH7xRFNpR1kmEiEVmg7UePZAgMBAAE=
-----END PUBLIC KEY-----'
PIN_ARCH='arm64'
PIN_VERSION='0.19.2.1'
PIN_URL='https://github.com/imputnet/helium-linux/releases/download/0.19.2.1/helium-0.19.2.1-arm64_linux.tar.xz'
PIN_SIZE='116102016'
PIN_SHA256='6cb0205b8b4fd8eba47fea38b2fcd3bd73e515bc5606abe2aaf7586a57a3e68b'
HELIUM_URL_RE='^https://github\.com/imputnet/helium-linux/releases/download/[0-9.]+/helium-[0-9.]+-(x86_64|arm64)_linux\.tar\.xz$'

ROOT="${XDG_DATA_HOME:-$HOME/.local/share}/$KURO_NAME"
HELIUM_DIR="$ROOT/Helium"
NEXT_DIR="$ROOT/Helium.next"
PROFILE="$ROOT/Profile"
LOG="$ROOT/kuro-screen.log"
MODE="${1:-}"
[ "$(id -u)" = 0 ] && { echo "$KURO_TITLE opens as your own user, not as root." >&2; exit 1; }
mkdir -p "$ROOT" && chmod 700 "$ROOT"
TMP=''
cleanup() { [ -n "$TMP" ] && rm -rf "$TMP"; }
trap cleanup EXIT INT TERM

log() { printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" >>"$LOG" 2>/dev/null; [ -t 2 ] && printf '%s\n' "$1" >&2; return 0; }
have() { command -v "$1" >/dev/null 2>&1; }
gui() { [ -n "${DISPLAY:-}${WAYLAND_DISPLAY:-}" ]; }
kzenity() { GSK_RENDERER="${GSK_RENDERER:-cairo}" zenity "$@"; }
field() { sed -n "s/.*\"$1\": *\"\{0,1\}\([^\",}]*\)\"\{0,1\}.*/\1/p" "$2" 2>/dev/null | head -n 1; }
sha256of() { sha256sum "$1" | cut -d' ' -f1; }
sizeof() { wc -c <"$1" | tr -d ' '; }
tmpdir() { [ -n "$TMP" ] || TMP="$(mktemp -d "$ROOT/.tmp.XXXXXX")"; }
tell() {
  log "$1"
  if gui && have zenity; then kzenity --"${2:-info}" --title="$KURO_TITLE" --text="$1" --width=420 >/dev/null 2>&1 &
  elif gui && have notify-send; then notify-send -a "$KURO_TITLE" -i "$KURO_PKG" "$KURO_TITLE" "$1" >/dev/null 2>&1 || true
  fi
}
running() {
  l="$(readlink "$PROFILE/SingletonLock" 2>/dev/null)" || return 1
  p="${l##*-}"
  [ -n "$p" ] && kill -0 "$p" 2>/dev/null
}
helium_bin() {
  for d in "$HELIUM_DIR"/*/; do [ -x "${d}helium-wrapper" ] && { printf '%s\n' "${d}helium-wrapper"; return 0; }; done
  return 1
}
verify_sig() {
  tmpdir
  printf '%s' "$1" >"$TMP/signed.txt"
  printf '%s\n' "$PUBKEY" >"$TMP/pub.pem"
  printf '%s' "$2" | openssl base64 -d -A >"$TMP/sig.bin" 2>/dev/null || return 1
  openssl dgst -sha256 -verify "$TMP/pub.pem" -signature "$TMP/sig.bin" "$TMP/signed.txt" >/dev/null 2>&1
}

# ---- Helium: the same pin, download and checks as kuro-launch.sh (copied verbatim) ----
resolve_pin() {
  V="$PIN_VERSION"; U="$PIN_URL"; S="$PIN_SIZE"; H="$PIN_SHA256"; SRC='this package'
  tmpdir
  if curl -fsSL --max-time 20 -H 'Cache-Control: no-cache' "$KURO_BASE/ext/kuro-browser-linux.json" -o "$TMP/kb.json" 2>/dev/null; then
    A="$PIN_ARCH"
    v="$(field version "$TMP/kb.json")"; u="$(field "${A}_url" "$TMP/kb.json")"; s="$(field "${A}_size" "$TMP/kb.json")"
    h="$(field "${A}_sha256" "$TMP/kb.json")"; g="$(field "${A}_signature" "$TMP/kb.json")"
    if [ "$(field format "$TMP/kb.json")" = 'kuro-browser-linux' ] && [ "$(field env "$TMP/kb.json")" = "$KURO_ENV" ] \
      && printf '%s' "$u" | grep -Eq "$HELIUM_URL_RE" && verify_sig "$KURO_ENV|linux-$A|$v|$u|$s|$h" "$g"; then
      V="$v"; U="$u"; S="$s"; H="$h"; SRC='kuro-browser-linux.json'
    else
      log 'kuro-browser-linux.json did not verify: using the Helium pin built into this package.'
    fi
  fi
}
# A downloaded and checked Helium waits in Helium.next until this Kuro is closed, then moves in.
install_pending() {
  [ -f "$NEXT_DIR/kuro-helium.json" ] || return 0
  running && return 0
  rm -rf "$HELIUM_DIR" && mv "$NEXT_DIR" "$HELIUM_DIR" && log "Helium $(field version "$HELIUM_DIR/kuro-helium.json") installed in $HELIUM_DIR"
}
# Download with something the person can see: a zenity progress window, else a notification at
# start and end, else (no zenity, no notify-send) a small progress page in the default browser.
download() { # download <url> <out> <expected size> <show:0|1>
  # The progress shows while $TMP/progress.on exists: fetch_helium removes it once Helium is
  # unpacked (and the EXIT trap removes all of $TMP on any failure), so the window closes then.
  : >"$TMP/progress.on"
  curl -fsSL --retry 3 --connect-timeout 20 -o "$2" "$1" &
  cpid=$!
  if [ "$4" = 1 ] && gui; then
    mb=$(( $3 / 1048576 ))
    if have zenity; then
      ( while [ -f "$TMP/progress.on" ]; do
          n="$(sizeof "$2" 2>/dev/null || echo 0)"
          if [ "$n" -ge "$3" ]; then echo 95; echo '# Unpacking the Kuro browser engine...'
          else echo $(( n * 90 / $3 )); echo "# Downloading the Kuro browser engine: $(( n / 1048576 )) of $mb MB"; fi
          sleep 1
        done; echo 100 ) | kzenity --progress --title="Setting up $KURO_TITLE" --percentage=0 --auto-close --no-cancel --width=440 >/dev/null 2>&1 &
    elif have notify-send; then
      notify-send -a "$KURO_TITLE" -i "$KURO_PKG" "Setting up $KURO_TITLE" "Downloading the Kuro browser engine ($mb MB). Kuro opens by itself when it is ready." >/dev/null 2>&1 || true
    elif have xdg-open; then
      page="$ROOT/setup-progress.html"
      ( while [ -f "$TMP/progress.on" ]; do
          n="$(sizeof "$2" 2>/dev/null || echo 0)"
          printf '<!doctype html><meta charset="utf-8"><meta http-equiv="refresh" content="2"><title>Setting up %s</title><body style="font:16px system-ui;margin:3em"><h2>Setting up %s</h2><p>Downloading the Kuro browser engine: %s of %s MB (%s%%).</p><progress max="100" value="%s" style="width:24em"></progress><p>Kuro opens by itself when it is ready; you can close this tab then.</p>' \
            "$KURO_TITLE" "$KURO_TITLE" "$(( n / 1048576 ))" "$mb" "$(( n * 100 / $3 ))" "$(( n * 100 / $3 ))" >"$page.tmp" && mv "$page.tmp" "$page"
          sleep 2
        done; printf '<!doctype html><meta charset="utf-8"><title>%s</title><body style="font:16px system-ui;margin:3em"><h2>%s is ready</h2><p>You can close this tab.</p>' "$KURO_TITLE" "$KURO_TITLE" >"$page" ) &
      sleep 1; xdg-open "$page" >/dev/null 2>&1 &
    fi
  fi
  wait "$cpid"
}
fetch_helium() { # fetch_helium <show progress 0|1>
  resolve_pin
  [ "$(field sha256 "$HELIUM_DIR/kuro-helium.json")" = "$H" ] && helium_bin >/dev/null && return 0
  if [ "$(field sha256 "$NEXT_DIR/kuro-helium.json")" = "$H" ]; then install_pending; return 0; fi
  printf '%s' "$U" | grep -Eq "$HELIUM_URL_RE" || { log "Unexpected Helium address: $U"; return 1; }
  tmpdir
  log "Downloading Helium $V ($U, pinned by $SRC)..."
  download "$U" "$TMP/helium.tar.xz" "$S" "$1" || { log 'Helium download failed.'; return 1; }
  [ "$(sizeof "$TMP/helium.tar.xz")" = "$S" ] || { log 'Helium download: size mismatch, refused.'; return 1; }
  [ "$(sha256of "$TMP/helium.tar.xz")" = "$H" ] || { log 'Helium download: sha256 mismatch, refused.'; return 1; }
  rm -rf "$NEXT_DIR.tmp" && mkdir -p "$NEXT_DIR.tmp"
  tar -xJf "$TMP/helium.tar.xz" -C "$NEXT_DIR.tmp" || { log 'Helium archive could not be unpacked.'; rm -rf "$NEXT_DIR.tmp"; return 1; }
  printf '{\n  "version": "%s",\n  "url": "%s",\n  "sha256": "%s"\n}\n' "$V" "$U" "$H" >"$NEXT_DIR.tmp/kuro-helium.json"
  rm -rf "$NEXT_DIR" && mv "$NEXT_DIR.tmp" "$NEXT_DIR"
  rm -f "$TMP/helium.tar.xz"
  if running; then log "Helium $V is ready but Kuro is open; it installs next time Kuro opens."; else install_pending; fi
  rm -f "$TMP/progress.on"
}


# ---- The profile and the window -------------------------------------------------------------
seed_profile() {
  prefs="$PROFILE/$KURO_NAME/Preferences"
  [ -f "$prefs" ] && return 0
  mkdir -p "$(dirname "$prefs")"
  # No welcome page; for Kuro FaceID also "Kuro's page may use the camera" (this profile, Kuro's
  # own address only - tools/build/screenApps.mjs screenPreferences; nobody is at a TV to click Allow).
  printf '%s' '{"browser":{"has_seen_welcome_page":true}}' >"$prefs"
}
# Always starts on its page: the saved tabs go while the app is closed.
clean_profile() {
  running && return 0
  rm -f "$PROFILE/$KURO_NAME/Sessions/"* 2>/dev/null
  return 0
}
timer_on() {
  have systemctl && systemctl --user show-environment >/dev/null 2>&1 || return 1
  systemctl --user is-enabled "$KURO_PKG-updater.timer" >/dev/null 2>&1 && return 0
  systemctl --user enable --now "$KURO_PKG-updater.timer" >/dev/null 2>&1 || return 1
  log "Hourly updates: systemd user timer $KURO_PKG-updater.timer enabled."
}
desktop_icon() {
  [ -f "$ROOT/desktop-icon.done" ] && return 0
  : >"$ROOT/desktop-icon.done"
  d="$(xdg-user-dir DESKTOP 2>/dev/null || true)"; [ -n "$d" ] || d="$HOME/Desktop"
  [ "$d" != "$HOME" ] && [ -d "$d" ] && [ -f "/usr/share/applications/$KURO_PKG.desktop" ] || return 0
  cp "/usr/share/applications/$KURO_PKG.desktop" "$d/$KURO_PKG.desktop" && chmod 755 "$d/$KURO_PKG.desktop"
  have gio && gio set "$d/$KURO_PKG.desktop" metadata::trusted true >/dev/null 2>&1
  log "Desktop icon placed in $d"
}
# The same window as kuro-screen.ps1 ScreenArgs: full screen, no frame, tabs or address bar, on this
# app's page; ?kuroTv=1 = stay signed in + keep the screen awake (webroot/shared/session-guard.js).
start_helium() {
  b="$(helium_bin)" || return 1
  cd "$HOME" || true
  setsid "$b" "--user-data-dir=$PROFILE" "--profile-directory=$KURO_NAME" \
    --no-first-run --no-default-browser-check --hide-crash-restore-bubble --noerrdialogs \
    --disable-features=OverlayScrollbar,FluentOverlayScrollbar,Translate \
    --overscroll-history-navigation=0 --disable-pinch --autoplay-policy=no-user-gesture-required \
    "--window-name=$KURO_TITLE" "--class=$KURO_NAME" \
    --kiosk "--app=$KURO_BASE${KURO_PAGE}?kuroTv=1" \
    >>"$ROOT/helium.log" 2>&1 </dev/null &
}

case "$MODE" in
  --update)
    have flock && { exec 9>"$ROOT/.update.lock"; flock -n 9 || exit 0; }
    fetch_helium 0
    exit 0 ;;
  --forget)
    running && { tell "Close $KURO_TITLE first (Ctrl+W)." error; exit 1; }
    have systemctl && systemctl --user disable --now "$KURO_PKG-updater.timer" >/dev/null 2>&1
    d="$(xdg-user-dir DESKTOP 2>/dev/null || true)"; [ -n "$d" ] || d="$HOME/Desktop"
    rm -f "$d/$KURO_PKG.desktop"
    trap - EXIT INT TERM; rm -rf "$ROOT"
    echo "$KURO_TITLE: this user's data ($ROOT) removed."
    exit 0 ;;
  ''|--launch) ;;
  *) echo "usage: kuro-screen-launch [--update|--forget]" >&2; exit 2 ;;
esac

# Already open: a TV never gets a second window.
running && exit 0
install_pending
if ! helium_bin >/dev/null; then
  log "First start of $KURO_TITLE for $(id -un): setting up in $ROOT"
  fetch_helium 1 || { tell "$KURO_TITLE could not download the browser engine. Check the internet connection and open it again (details: $LOG)." error; exit 1; }
  first=1
else
  first=0
fi
seed_profile
clean_profile
start_helium || { tell "The $KURO_TITLE browser engine is missing; open it again to download it." error; exit 1; }
desktop_icon
if ! timer_on; then
  [ -f "$ROOT/update-on-launch" ] || log 'No systemd user session: checking for updates each time the app opens instead of hourly.'
  : >"$ROOT/update-on-launch"
  [ "$first" = 1 ] || (setsid "$0" --update >/dev/null 2>&1 </dev/null &)
else
  rm -f "$ROOT/update-on-launch"
fi
exit 0
