#!/bin/bash
set -euo pipefail
umask 077
base="$HOME/Library/Application Support/DayVoiceAgent"
support="$base/语音助手.app"
legacy_support="$base/生活助手后台.app"
label=com.dayvoice.mac.agent
domain="gui/$(id -u)"
plist="$HOME/Library/LaunchAgents/$label.plist"
scriptdir="$(cd "$(dirname "$0")" && pwd)"
data="$base/status.json"

server_origin=https://123422345.top
agent_hash=a6f188bcbdac8ef3a84077338258509599faca417f04b02d65f949592661ed38
say(){ printf '%s\n' "$*"; }
stop_agent(){
  if launchctl print "$domain/$label" >/dev/null 2>&1; then
    touch "$base/shutdown"
    for ((i=0;i<10;i++)); do
      test -f "$data" || break
      sleep 0.3
    done
    launchctl bootout "$domain/$label" >/dev/null 2>&1 || true
  fi
  rm -f "$base/shutdown"
}
run_agent(){
  rm -f "$base/shutdown"
  launchctl enable "$domain/$label" || return 1
  if ! launchctl print "$domain/$label" >/dev/null 2>&1; then
    launchctl bootstrap "$domain" "$plist" || return 1
  fi
  launchctl kickstart "$domain/$label" >/dev/null 2>&1 || true
}
status_field(){ plutil -extract "$1" raw -o - "$data" 2>/dev/null || true; }
wait_permission(){
  say '如果 macOS 弹出麦克风权限提示，请点「允许」。'
  local auth
  for ((i=0;i<100;i++)); do
    auth="$(status_field microphone)"
    case "$auth" in
      granted) say '麦克风已授权。'; return 0;;
      denied) say '麦克风未授权：请在「系统设置 → 隐私与安全性 → 麦克风」允许「语音助手」，再运行 authorize。';return 1;;
    esac
    sleep 0.4
  done
  say '还没读到麦克风授权结果。请确认系统提示或系统设置，再运行 status 查看。'
}
pair(){
  local input config tmp origin key normalized
  say '请打开手机后台 → 菜单 → 管理 Mac 设备 → 添加一台 Mac，使用网页生成的一键启用命令。此处仅接受已有的配对 JSON，输入时不会回显。'
  IFS= read -rs input
  printf '\n'
  tmp="$(mktemp "$base/pair.XXXXXX")"
  printf '%s' "$input" >"$tmp"
  origin="$(plutil -extract origin raw -o - "$tmp" 2>/dev/null || true)"
  key="$(plutil -extract key raw -o - "$tmp" 2>/dev/null || true)"
  rm -f "$tmp"
  if [[ ! "$origin" =~ ^https://[A-Za-z0-9.-]+(:[0-9]{1,5})?$ || ! "$key" =~ ^[A-Za-z0-9_-]{43}$ ]]; then
    say '配对信息格式不正确，或服务器地址不是 HTTPS。';return 1
  fi
  if [[ "$(status_field recording)" == true || "$(status_field recording)" == 1 ]]; then
    say '请先结束正在进行的录音。';return 1
  fi
  tmp="$(mktemp "$base/config.XXXXXX")"
  printf '{"remote":{"origin":"%s","key":"%s"}}\n' "$origin" "$key" >"$tmp"
  mv -f "$tmp" "$base/config.json"
  say '配对信息已保存；工具会在几秒内连接。'
}
claim_enrollment(){
  local code="$1" previous="" key response="$stage/enroll.json" http kept
  if [[ -f "$base/config.json" && "$(plutil -extract remote.origin raw -o - "$base/config.json" 2>/dev/null || true)" == "$server_origin" ]]; then
    previous="$(plutil -extract remote.key raw -o - "$base/config.json" 2>/dev/null || true)"
    if [[ ! "$previous" =~ ^[A-Za-z0-9_-]{43}$ ]]; then previous=""; fi
  fi
  if [[ -f "$base/config.json" && -z "$previous" ]]; then
    say '这台 Mac 已配对到其他服务。请保留当前配置，或先手动解除旧配对。';return 1
  fi
  key="$(/usr/bin/openssl rand -base64 32 | /usr/bin/tr '+/' '-_' | /usr/bin/tr -d '=\n')"
  [[ "$key" =~ ^[A-Za-z0-9_-]{43}$ ]] || { say '无法生成设备密钥。';return 1; }
  if ! http="$(
    if [[ -n "$previous" ]]; then printf '{"code":"%s","key":"%s","previous":"%s"}' "$code" "$key" "$previous";
    else printf '{"code":"%s","key":"%s"}' "$code" "$key"; fi |
    /usr/bin/curl -sS --proto '=https' --connect-timeout 10 --max-time 20 \
      -H 'Content-Type: application/json' --data-binary @- -o "$response" -w '%{http_code}' "$server_origin/device/enroll"
  )"; then
    rm -f "$response"; say '无法连接语音助手服务器。请检查网络，必要时重新生成启用命令。';return 1
  fi
  case "$http" in
    200|201) ;;
    410) rm -f "$response"; say '启用命令已使用或超过 15 分钟。请在网页生成新命令。';return 1;;
    *) rm -f "$response"; say "服务器未接受启用请求（HTTP $http）。";return 1;;
  esac
  kept="$(plutil -extract kept raw -o - "$response")"
  case "$kept" in
    true|1)
      [[ -n "$previous" ]] || { say '服务器返回的配对结果无效。'; return 1; }
      say '这台 Mac 已经配对，原有设备名称和录音保持不变。'
      ;;
    false|0) printf '{"remote":{"origin":"%s","key":"%s"}}\n' "$server_origin" "$key" >"$stage/config.json" ;;
    *) say '服务器返回的配对结果无效。';return 1;;
  esac
}
enable_one(){
  local code i
  IFS= read -r code || { say '缺少启用码。请在网页生成命令后整行复制。';return 1; }
  if [[ ! "$code" =~ ^[A-Za-z0-9_-]{43}$ ]]; then say '启用码格式无效。请重新复制整行命令。';return 1; fi
  install_agent "$code"
  if [[ "$(status_field microphone)" == denied ]]; then
    say '配对已保存。请在 Mac 系统设置允许语音助手访问麦克风。';return 0
  fi
  for ((i=0;i<24;i++)); do
    if [[ "$(status_field connected)" =~ ^(true|1)$ ]]; then
      if [[ "$(status_field microphone)" == granted ]]; then say '启用成功。刷新网页即可控制这台 Mac。';return 0;fi
      say '已配对上线。请在 Mac 系统设置允许语音助手访问麦克风。';return 0
    fi
    sleep 0.5
  done
  say '配对已保存，但设备还没连上。请检查网络和「登录项与扩展」后刷新网页。'
}
install_agent(){
  local code="${1:-}"
  if [[ "$(uname -s)" != Darwin || "$(sysctl -n hw.optional.arm64 2>/dev/null || true)" != 1 ]]; then say '本工具支持 Apple 芯片 Mac。'; exit 1; fi
  version="$(sw_vers -productVersion)"; if (( ${version%%.*} < 14 )); then say '需要 macOS 14 或更新版本。'; exit 1; fi
  source_app="$scriptdir/语音助手.app"
  test -d "$source_app" || { say '请先完整解压安装包。'; exit 1; }
  mkdir -p "$base" "$HOME/Library/LaunchAgents"
  chmod 700 "$base"
  if [[ "$(/usr/bin/shasum -a 256 "$source_app/Contents/MacOS/DayVoiceAgent" | /usr/bin/cut -d ' ' -f 1)" != "$agent_hash" ]]; then
    say '安装包里的程序校验失败，请重新下载并解压。';exit 1
  fi
  codesign --verify --strict "$source_app"
  if [[ "$(status_field recording)" =~ ^(true|1)$ || "$(status_field listening)" =~ ^(true|1)$ ]]; then
    say '这台 Mac 正在录音或听音。请先停止，再更新语音助手。';exit 1
  fi
  stage="$(mktemp -d "$base/agent-install.XXXXXX")"
  trap 'rm -rf -- "$stage"' EXIT
  staged_app="$stage/${support##*/}"
  if ! ditto "$source_app" "$staged_app" || ! codesign --verify --strict "$staged_app"; then
    say '安装文件复制或校验失败，请重新解压。';exit 1
  fi
  release_version="$(plutil -extract CFBundleShortVersionString raw -o - "$staged_app/Contents/Info.plist")"
  install -m 700 "$scriptdir/life-record" "$stage/life-record"
  tmp="$stage/agent.plist"
  cat >"$tmp" <<'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>Label</key><string>com.dayvoice.mac.agent</string>
<key>ProgramArguments</key><array><string>placeholder</string></array>
<key>RunAtLoad</key><true/>
<key>LimitLoadToSessionType</key><string>Aqua</string>
<key>AssociatedBundleIdentifiers</key><array><string>com.dayvoice.mac.agent</string></array>
</dict></plist>
PLIST
  plutil -replace ProgramArguments.0 -string "$support/Contents/MacOS/DayVoiceAgent" "$tmp"
  plutil -lint "$tmp" >/dev/null
  if [[ -n "$code" ]]; then claim_enrollment "$code"; fi
  if [[ "$(status_field recording)" =~ ^(true|1)$ || "$(status_field listening)" =~ ^(true|1)$ ]]; then
    say '这台 Mac 正在录音或听音。请先停止，再更新语音助手。';exit 1
  fi
  if test -e "$plist"; then cp -p "$plist" "$stage/previous.plist"; fi
  if test -e "$base/life-record"; then cp -p "$base/life-record" "$stage/previous-cli"; fi
  if test -e "$base/config.json"; then cp -p "$base/config.json" "$stage/previous-config"; fi
  config_changed=false
  stop_agent
  if launchctl print "$domain/$label" >/dev/null 2>&1; then
    say '未能停止旧版后台，请稍后重试。';exit 1
  fi
  if test -e "$support"; then mv "$support" "$stage/previous.app"; fi
  restore_install(){
    trap - EXIT
    stop_agent
    rm -f "$base/request-microphone"
    if test -e "$support"; then rm -rf -- "$support"; fi
    if test -e "$stage/previous.app"; then mv "$stage/previous.app" "$support"; fi
    if test -e "$stage/previous-cli"; then
      mv -f "$stage/previous-cli" "$base/life-record"
    else
      rm -f "$base/life-record"
    fi
    if "$config_changed"; then
      if test -e "$stage/previous-config"; then
        mv -f "$stage/previous-config" "$base/config.json"
      else
        rm -f "$base/config.json"
      fi
    fi
    if test -e "$stage/previous.plist"; then
      mv -f "$stage/previous.plist" "$plist"
      run_agent || say '旧版后台恢复后需要手动启动。'
    else
      rm -f "$plist"
    fi
    rm -rf -- "$stage"
  }
  trap restore_install EXIT
  mv "$staged_app" "$support"
  mv -f "$tmp" "$plist"
  mv -f "$stage/life-record" "$base/life-record"
  if test -e "$stage/config.json"; then
    config_changed=true
    mv -f "$stage/config.json" "$base/config.json"
  fi
  rm -f "$data"
  touch "$base/request-microphone"
  if ! run_agent; then say '后台无法启动，请在「系统设置 → 通用 → 登录项与扩展」检查是否允许。';exit 1;fi
  for ((i=0;i<40;i++)); do
    if [[ "$(status_field version)" == "$release_version" ]]; then break; fi
    sleep 0.5
  done
  if [[ "$(status_field version)" != "$release_version" ]]; then
    say '未收到新版后台的启动确认，正在恢复原版本。';exit 1
  fi
  trap 'rm -rf -- "$stage"' EXIT
  if test -d "$legacy_support"; then rm -rf -- "$legacy_support"; fi
  wait_permission || true
  if [[ -z "$code" ]] && test ! -e "$base/config.json" && test -t 0; then pair; fi
  say '安装完成。之后登录这台 Mac 会自动运行；远程录音从手机后台操作。'
}
case "${1:-install}" in
 enable) enable_one ;;
 install) install_agent ;;
 authorize)
  test -e "$plist" || { say '请先安装。';exit 1; }
  touch "$base/request-microphone"
  run_agent
  wait_permission
  ;;
 authorize-location)
  test -e "$plist" || { say '请先安装新版语音助手。';exit 1; }
  run_agent
  touch "$base/request-location-permission"
  say '请在这台 Mac 的系统弹窗中允许「语音助手」使用定位服务。'
  for ((i=0;i<120;i++)); do
    case "$(status_field location)" in
      granted) say '定位已授权。现在可从网页查询一次位置。';exit 0;;
      denied) say '请在「系统设置 → 隐私与安全性 → 定位服务」开启定位服务和「语音助手」。';exit 1;;
    esac
    sleep 0.5
  done
  say '尚未收到定位授权。请查看系统弹窗或「定位服务」设置。'
  exit 1
  ;;
 pair)
  test -e "$plist" || { say '请先安装。';exit 1; }
  pair
  ;;
 start)
  test -e "$plist" || { say '请先安装。';exit 1; }
  run_agent
  say '后台已启动；它会等待网页录音指令。'
  ;;
 stop-listening)
  test -e "$data" || { say '后台未运行。';exit 1; }
  touch "$base/stop-listening"
  say '已发出本机停止听音指令。'
  ;;
 stop-recording)
  test -e "$data" || { say '后台未运行。';exit 1; }
  touch "$base/stop-recording"
  say '已发出本机停止录音指令。'
  ;;
 stop)
  stop_agent
  say '后台已停止。下次登录会按安装时的设置自动启动。'
  ;;
 status)
  if ! launchctl print "$domain/$label" >/dev/null 2>&1 || ! test -f "$data"; then say '后台未运行，或还未完成启动。';exit 1;fi
  say "麦克风：$(status_field microphone)；已配对：$(status_field paired)；已连服务器：$(status_field connected)；录音中：$(status_field recording)；听音中：$(status_field listening)"
  say "版本：$(status_field version)"
  case "$(status_field location)" in
    granted) say '定位：已授权';;
    prompt) say '定位：等待系统授权';;
    *) say '定位：未授权';;
  esac
  if test -n "$(status_field message)"; then say "$(status_field message)";fi
  ;;
 uninstall)
  stop_agent
  rm -f "$plist"
  if test -d "$support"; then rm -rf "$support";fi
  if test -d "$legacy_support"; then rm -rf "$legacy_support";fi
  say '已关闭登录时启动并移除后台程序；本机录音和配置保留。服务器端可另行撤销此 Mac 的配对。'
  ;;
 *) say '用法：life-record [install|enable|authorize|authorize-location|pair|start|stop-recording|stop-listening|stop|status|uninstall]';exit 2;;
esac
