#!/bin/bash
# Shuo installer for macOS (Apple silicon first; Intel also works).
#   curl -fsSL https://shuo.sinogenomics.com/install.sh | bash
# What it does: checks macOS 13+, makes sure the Swift compiler from Xcode Command Line Tools
# is present, downloads the source, builds Shuo.app on your Mac, puts it in /Applications and opens it.
set -euo pipefail
SITE="${SHUO_SITE:-https://shuo.sinogenomics.com}"
say(){ printf '\033[1;36m>>\033[0m %s\n' "$*"; }
die(){ printf '\033[1;31m!!\033[0m %s\n' "$*" >&2; exit 1; }

[ "$(uname -s)" = "Darwin" ] || die "This installer is for macOS."
ARCH="$(uname -m)"
OSV="$(sw_vers -productVersion)"
MAJOR="${OSV%%.*}"
[ "$MAJOR" -ge 13 ] || die "Shuo needs macOS 13 (Ventura) or newer; this Mac runs $OSV."
say "macOS $OSV on $ARCH"

if ! xcode-select -p >/dev/null 2>&1 || ! xcrun -f swiftc >/dev/null 2>&1; then
  say "Xcode Command Line Tools are needed to build Shuo (a one-time, free download from Apple)."
  say "A dialog will appear — click Install, wait for it to finish, then run this command again."
  xcode-select --install 2>/dev/null || true
  exit 3
fi

TMP="$(mktemp -d /tmp/shuo-build.XXXXXX)"
trap 'rm -rf "$TMP"' EXIT
say "downloading source…"
curl -fsSL "$SITE/dl/shuo-src.tar.gz" -o "$TMP/src.tgz" || die "download failed"
tar -xzf "$TMP/src.tgz" -C "$TMP"
SRC="$(find "$TMP" -maxdepth 2 -name build.sh -print -quit)"
[ -n "$SRC" ] || die "source archive looks wrong"
SRC="$(dirname "$SRC")"

# One-time: a local code-signing certificate in the login keychain, so macOS recognises every future
# build as the same app and keeps the Accessibility / Microphone grants. Self-signed, only on this Mac.
ensure_signing_identity() {
  security find-identity -v -p codesigning 2>/dev/null | grep -q '"Shuo Local Signing"' && return 0
  say "creating a local signing certificate (one time) so permissions survive updates…"
  local KC="$HOME/Library/Keychains/login.keychain-db"
  cat > "$TMP/cert.cnf" <<CNF
[req]
distinguished_name = dn
x509_extensions = ext
prompt = no
[dn]
CN = Shuo Local Signing
O = Shuo (local build)
[ext]
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, codeSigning
basicConstraints = critical, CA:false
subjectKeyIdentifier = hash
CNF
  openssl req -x509 -newkey rsa:2048 -nodes -days 3650 -config "$TMP/cert.cnf" \
    -keyout "$TMP/key.pem" -out "$TMP/cert.pem" >/dev/null 2>&1 || { say "  (could not create certificate; using ad-hoc signing)"; return 0; }
  openssl pkcs12 -export -inkey "$TMP/key.pem" -in "$TMP/cert.pem" -out "$TMP/id.p12" -passout pass:shuo >/dev/null 2>&1 || { say "  (could not package certificate; using ad-hoc signing)"; return 0; }
  security import "$TMP/id.p12" -k "$KC" -P shuo -T /usr/bin/codesign -T /usr/bin/security >/dev/null 2>&1 || { say "  (keychain import failed; using ad-hoc signing)"; return 0; }
  say "  macOS may ask for your login password to trust this certificate for code signing — that is expected."
  security add-trusted-cert -r trustRoot -p codeSign -k "$KC" "$TMP/cert.pem" 2>/dev/null || say "  (certificate not marked trusted; using ad-hoc signing)"
  rm -f "$TMP/key.pem" "$TMP/id.p12"
  if security find-identity -v -p codesigning 2>/dev/null | grep -q '"Shuo Local Signing"'; then say "  signing certificate ready"; fi
}
ensure_signing_identity

# On-device speech recognition: SenseVoice (the same model family the server uses) via sherpa-onnx.
# Engine libs 9 MB + model 159 MB, downloaded once; the app falls back to the server if anything is missing.
SV_LIBS="sherpa-onnx-macos-arm64-1.13.8.tar.gz"
SV_MODEL_TGZ="sensevoice-int8-2025-09-09.tar.gz"
SV_MODEL_SIZE=237115547
MODEL_DIR="$HOME/Library/Application Support/Shuo/models"
SV_DIR="$MODEL_DIR/sensevoice"
if [ "$ARCH" != "arm64" ]; then
  say "on-device SenseVoice is Apple-silicon only; this Intel Mac will transcribe on the server."
else
  say "downloading the on-device speech engine (sherpa-onnx, 9 MB)…"
  mkdir -p "$SRC/frameworks"
  if curl -fsSL "$SITE/dl/$SV_LIBS" | tar -xz -C "$SRC/frameworks"; then
    mkdir -p "$SV_DIR"
    if [ -s "$SV_DIR/model.int8.onnx" ] && [ "$(stat -f %z "$SV_DIR/model.int8.onnx")" = "$SV_MODEL_SIZE" ] && [ -s "$SV_DIR/tokens.txt" ]; then
      say "SenseVoice model already installed"
    else
      say "downloading the SenseVoice model (159 MB, one time)…"
      if curl -fL --progress-bar -o "$TMP/sv.tgz" "$SITE/dl/models/$SV_MODEL_TGZ" && tar -xzf "$TMP/sv.tgz" -C "$TMP"; then
        cp "$TMP/sensevoice/model.int8.onnx" "$TMP/sensevoice/tokens.txt" "$SV_DIR/"
      elif curl -fL --progress-bar -o "$TMP/sv.tbz" "https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/sherpa-onnx-sense-voice-zh-en-ja-ko-yue-int8-2025-09-09.tar.bz2" && tar -xjf "$TMP/sv.tbz" -C "$TMP"; then
        M="$(find "$TMP" -name model.int8.onnx -print -quit)"; cp "$M" "$(dirname "$M")/tokens.txt" "$SV_DIR/"
      fi
      if [ -s "$SV_DIR/model.int8.onnx" ] && [ "$(stat -f %z "$SV_DIR/model.int8.onnx")" = "$SV_MODEL_SIZE" ]; then
        say "SenseVoice model installed"
        # The earlier Whisper model (574 MB) is no longer needed.
        [ -f "$MODEL_DIR/ggml-large-v3-turbo-q5_0.bin" ] && rm -f "$MODEL_DIR/ggml-large-v3-turbo-q5_0.bin" && say "removed the old Whisper model (574 MB freed)"
      else
        say "⚠️ SenseVoice model incomplete — Shuo will transcribe on the server; run this installer again to retry."
      fi
    fi
  else
    say "⚠️ could not fetch the speech engine — Shuo will transcribe on the server."
    rm -rf "$SRC/frameworks/sherpa-onnx"
  fi
fi

say "building Shuo.app (takes 10–30 seconds)…"
if ! bash "$SRC/build.sh"; then
  die "build failed. Please copy the lines above and send them to tech@sinogenomics.com — thanks."
fi

DEST="/Applications"
[ -w "$DEST" ] || { DEST="$HOME/Applications"; mkdir -p "$DEST"; }
if pgrep -x Shuo >/dev/null 2>&1; then say "stopping the running copy…"; osascript -e 'quit app "Shuo"' >/dev/null 2>&1 || pkill -x Shuo || true; sleep 1; fi
rm -rf "$DEST/Shuo.app"
cp -R "$SRC/dist/Shuo.app" "$DEST/Shuo.app"
# Clear the quarantine flag the download may have left, so Gatekeeper doesn't complain about an unsigned app.
xattr -dr com.apple.quarantine "$DEST/Shuo.app" 2>/dev/null || true
say "installed to $DEST/Shuo.app"
open "$DEST/Shuo.app"

cat <<MSG

  ✅ Shuo is running — look for the waveform icon in the menu bar.

  Permissions (macOS asks once):
    1. Microphone → Allow.
    2. Accessibility → System Settings › Privacy & Security › Accessibility → turn on Shuo.
       (Lets Shuo hear the push-to-talk key and type the English into the app you're using.)
       ⚠️ If Shuo is ALREADY listed there from an earlier install: select it, click "−" to remove it,
       then run  open -a Shuo  and add it again. Old entries point at the previous build and no longer work.

  Use it: put the cursor in any text field, hold the RIGHT ⌥ Option key, speak
  (Chinese, English or both), release. Native English is typed for you.
  Press any key while holding to cancel. Change the key from the menu-bar icon.

  Speech recognition runs on this Mac (SenseVoice via sherpa-onnx — the same model
  family as the server); only the text goes to the server for the English rewrite.

  Help: $SITE
MSG
