#!/usr/bin/env bash
# DEPRECATED — CSM/mlx-audio sidecar (legacy fallback).
#
# This script provisions mlx-audio serving CSM-1b. It works but produces
# lower-quality cloned speech than Chatterbox and is no longer the recommended
# engine for Apple Silicon.
#
# Use setup-chatterbox-tts-macos.sh instead (Chatterbox, MIT, ~7s/utterance,
# Windows-quality voice cloning). This script is retained only for users who
# need the faster-but-lower-quality CSM path or want to A/B test.
#
# Provision the native-host MLX TTS sidecar for DPF on Apple Silicon.
#
# Docker Desktop on macOS has no Metal GPU passthrough, so cloning-grade neural
# TTS must run host-native. This script stands up an `mlx-audio` OpenAI-compatible
# server (POST /v1/audio/speech) under a managed uv venv, supervises it with a
# launchd LaunchAgent (RunAtLoad + KeepAlive), and prints the .env values the
# portal container needs to reach it via host.docker.internal.
#
# Idempotent: safe to re-run. macOS + Apple Silicon only.
# Spec: docs/superpowers/specs/2026-05-28-tts-apple-silicon-local-design.md
#
# Usage:
#   scripts/tts/setup-mlx-tts-macos.sh [--port 8770] [--data-root <host uploads dir>]
#                                      [--model mlx-community/csm-1b] [--no-launchd]
set -euo pipefail

PORT="8770"
MODEL="mlx-community/csm-1b"
INSTALL_LAUNCHD=1
DATA_ROOT=""

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
TTS_HOME="${HOME}/.dpf/tts"
VENV_DIR="${TTS_HOME}/venv"
LOG_DIR="${TTS_HOME}/logs"
LAUNCH_SCRIPT="${TTS_HOME}/run-mlx-tts.sh"
PLIST_TMPL="${SCRIPT_DIR}/com.dpf.mlx-tts.plist.tmpl"
PLIST_LABEL="local.dpf-mlx-tts"
PLIST_DEST="${HOME}/Library/LaunchAgents/${PLIST_LABEL}.plist"

while [ $# -gt 0 ]; do
  case "$1" in
    --port) PORT="$2"; shift 2 ;;
    --model) MODEL="$2"; shift 2 ;;
    --data-root) DATA_ROOT="$2"; shift 2 ;;
    --no-launchd) INSTALL_LAUNCHD=0; shift ;;
    *) echo "Unknown arg: $1" >&2; exit 2 ;;
  esac
done

log() { printf '[mlx-tts setup] %s\n' "$1"; }
die() { printf '[mlx-tts setup] ERROR: %s\n' "$1" >&2; exit 1; }

# --- Preconditions -----------------------------------------------------------
[ "$(uname -s)" = "Darwin" ] || die "macOS only (uname=$(uname -s))."
[ "$(uname -m)" = "arm64" ] || die "Apple Silicon (arm64) only; this host is $(uname -m)."

UV="$(command -v uv || true)"
[ -n "$UV" ] || UV="/opt/homebrew/bin/uv"
[ -x "$UV" ] || die "uv not found. Install with: brew install uv"

# ffmpeg is required by mlx-audio to decode non-WAV reference clips (the portal
# stores reference audio as .webm). Auto-install via Homebrew when missing.
if ! command -v ffmpeg >/dev/null 2>&1 && [ ! -x /opt/homebrew/bin/ffmpeg ]; then
  if command -v brew >/dev/null 2>&1; then
    log "ffmpeg not found — installing via Homebrew"
    brew install ffmpeg >/dev/null || die "ffmpeg install failed. Install manually: brew install ffmpeg"
  else
    die "ffmpeg not found and Homebrew unavailable. Install ffmpeg: brew install ffmpeg"
  fi
fi

# Default host uploads dir: <repo>/data/uploads (the host side of the portal's
# UPLOAD_STORAGE_PATH bind mount). Override with --data-root for installed layouts
# (e.g. ~/.dpf/data/uploads).
if [ -z "$DATA_ROOT" ]; then DATA_ROOT="${REPO_ROOT}/data/uploads"; fi

# --- Provision venv + deps ---------------------------------------------------
mkdir -p "$TTS_HOME" "$LOG_DIR"
if [ ! -d "$VENV_DIR" ]; then
  log "creating uv venv (python 3.12) at ${VENV_DIR}"
  "$UV" venv --python 3.12 "$VENV_DIR" >/dev/null
fi
log "installing mlx-audio + server deps (reuses uv cache)"
# webrtcvad imports pkg_resources, removed in setuptools>=81 -> pin <81.
VIRTUAL_ENV="$VENV_DIR" "$UV" pip install --python "$VENV_DIR/bin/python" \
  mlx-audio uvicorn fastapi python-multipart webrtcvad "setuptools<81" "misaki[en]" >/dev/null
log "deps installed"

# --- Generate launch script --------------------------------------------------
cat > "$LAUNCH_SCRIPT" <<EOF
#!/usr/bin/env bash
# Generated by scripts/tts/setup-mlx-tts-macos.sh — do not edit by hand.
set -euo pipefail
# launchd starts agents with a minimal PATH (/usr/bin:/bin:/usr/sbin:/sbin)
# that excludes Homebrew. mlx-audio shells out to ffmpeg to decode non-WAV
# reference clips (the portal stores .webm), so ffmpeg must be on PATH or
# cloning fails with an empty 200. Prepend the common Homebrew bin dirs.
export PATH="/opt/homebrew/bin:/usr/local/bin:\${PATH}"
# launchd runs with cwd=/ (read-only); the server's default --log-dir is the
# RELATIVE path "logs", so it must be given an absolute dir or it crash-loops.
exec "${VENV_DIR}/bin/python" -m mlx_audio.server --host 127.0.0.1 --port ${PORT} --log-dir "${LOG_DIR}"
EOF
chmod +x "$LAUNCH_SCRIPT"
log "launch script: ${LAUNCH_SCRIPT}"

# --- Install + (re)load LaunchAgent ------------------------------------------
if [ "$INSTALL_LAUNCHD" -eq 1 ]; then
  [ -f "$PLIST_TMPL" ] || die "plist template missing: ${PLIST_TMPL}"
  mkdir -p "$(dirname "$PLIST_DEST")"
  sed -e "s|@@DPF_TTS_LAUNCH_SCRIPT@@|${LAUNCH_SCRIPT}|g" \
      -e "s|@@DPF_TTS_LOG_DIR@@|${LOG_DIR}|g" \
      "$PLIST_TMPL" > "$PLIST_DEST"
  # bootout is best-effort (agent may not be loaded yet); bootstrap (re)loads.
  launchctl bootout "gui/$(id -u)/${PLIST_LABEL}" 2>/dev/null || true
  launchctl bootstrap "gui/$(id -u)" "$PLIST_DEST"
  launchctl kickstart -k "gui/$(id -u)/${PLIST_LABEL}" 2>/dev/null || true
  log "LaunchAgent loaded: ${PLIST_DEST}"
else
  log "skipping launchd (--no-launchd); start manually: bash ${LAUNCH_SCRIPT}"
fi

# --- Health check ------------------------------------------------------------
log "waiting for server on 127.0.0.1:${PORT} ..."
HEALTHY=0
for _ in $(seq 1 30); do
  if curl -fsS -m 3 "http://127.0.0.1:${PORT}/" >/dev/null 2>&1; then HEALTHY=1; break; fi
  sleep 1
done
[ "$HEALTHY" -eq 1 ] && log "server healthy (HTTP 200 on /)" || log "WARN: server not responding yet — check ${LOG_DIR}/mlx-tts.err.log"

# --- Print portal .env values ------------------------------------------------
cat <<EOF

==============================================================================
 MLX TTS sidecar provisioned. Add these to the repo-root .env, then restart
 the portal (docker compose up -d portal):

   TTS_PROVIDER=mlx
   DPF_TTS_URL=http://host.docker.internal:${PORT}
   DPF_TTS_REFERENCE_HOST_ROOT=${DATA_ROOT}
   DPF_TTS_MLX_MODEL=${MODEL}

 Manage the sidecar:
   tail -f ${LOG_DIR}/mlx-tts.err.log          # logs
   launchctl kickstart -k gui/$(id -u)/${PLIST_LABEL}   # restart
   launchctl bootout gui/$(id -u)/${PLIST_LABEL}        # stop + unload
==============================================================================
EOF
