#!/bin/sh
# Selection-aware update entry point. Core installation lives in ecosystem.sh.
set -eu
BASE="${PI_SETUP_BASE:-https://onboarding.fornace.app}"
PI_DIR="${PI_DIR:-${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}}"
export PI_DIR
TOKEN="${FORNACE_TOKEN:-}"
while [ "$#" -gt 0 ]; do
  case "$1" in
    --token) [ "$#" -ge 2 ] || exit 2; TOKEN="$2"; shift 2 ;;
    --token=*) TOKEN="${1#--token=}"; shift ;;
    *) break ;;
  esac
done
COMPONENTS="${1:-pkgs,ext,rules,skills,mcp,settings,cmux,matchbox,soffio,browser}"
EXTS_ARG="${4:-}"
SKLS_ARG="${5:-}"
has() { case ",$COMPONENTS," in *",$1,"*) return 0 ;; *) return 1 ;; esac; }
if [ -x /opt/homebrew/bin/node ]; then
  export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:$PATH"
elif [ -x /usr/local/bin/node ]; then
  export PATH="/usr/local/bin:$PATH"
fi
hash -r 2>/dev/null || true
command -v node >/dev/null || { echo 'Run install.sh first: Node.js is required.' >&2; exit 1; }
node -e 'process.exit(Number(process.versions.node.split(".")[0])>=26?0:1)' || { echo 'Node.js 26+ required. Run install.sh.' >&2; exit 1; }
mkdir -p "$PI_DIR"
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
curl -fsSL "$BASE/install-pi.sh" -o "$WORK/install-pi.sh"
. "$WORK/install-pi.sh"
resolve_pi_latest
ensure_pi_latest
curl -fsSL "$BASE/manifest.json" -o "$PI_DIR/pi-setup-manifest.json"
verify_file() {
  node -e '
    const fs=require("fs"),crypto=require("crypto");
    const [manifest,name,file]=process.argv.slice(1);
    const wanted=JSON.parse(fs.readFileSync(manifest)).components[name]?.sha256;
    const got=crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex");
    if(!wanted || wanted!==got) throw new Error("Digest mismatch for "+name);
  ' "$PI_DIR/pi-setup-manifest.json" "$1" "$2"
}
curl -fsSL "$BASE/ecosystem.sh" -o "$WORK/ecosystem.sh"
. "$WORK/ecosystem.sh"
ecosystem_init

# Read the existing key unchanged. Age of a Mantice key is not a migration gate.
if [ -z "$TOKEN" ] && [ -f "$PI_DIR/auth.json" ]; then
  TOKEN="$(node -e 'const a=require(process.argv[1]);process.stdout.write(a.mantice?.key||a.fornace?.key||"")' "$PI_DIR/auth.json")"
fi
if [ -n "$TOKEN" ]; then
  TOKEN="$TOKEN" node -e '
    const fs=require("fs"),p=process.argv[1];
    const auth=fs.existsSync(p)?JSON.parse(fs.readFileSync(p)):{};
    auth.mantice={type:"api_key",key:process.env.TOKEN};
    auth.fornace={type:"api_key",key:process.env.TOKEN};
    fs.writeFileSync(p,JSON.stringify(auth,null,2)+"\n",{mode:0o600});fs.chmodSync(p,0o600);
  ' "$PI_DIR/auth.json"
fi
CORE=0
if has pkgs || has soffio || has matchbox; then
  echo '==> Runtime, Soffio migration, local service and Matchbox CLI'
  install_core
  CORE=1
fi
if has browser || has pkgs; then
  echo '==> Scrapling and Camoufox'
  install_browser_tooling
fi
if has pkgs; then install_media_packages; fi
if has ext; then
  echo '==> Extensions'
  curl -fsSL "$BASE/pi-extensions.zip" -o "$WORK/extensions.zip"
  verify_file extensions "$WORK/extensions.zip"
  # The runtime owns RTK rewrites; the standalone extension is a true
  # duplicate handler and is never unpacked. Retirement of an installed
  # copy goes through ecosystem.json retiredPaths.
  if [ -n "$EXTS_ARG" ]; then
    unzip -oq "$WORK/extensions.zip" 'agents/*' 'themes/*' 'keybindings.json' 'guides/*' -d "$PI_DIR" -x 'themes/notte.json'
    for name in $(printf '%s' "$EXTS_ARG" | tr ',' ' '); do
      case "$name" in *.ts) ;; *) name="$name.ts" ;; esac
      [ "$name" = "rtk.ts" ] && continue
      unzip -oq "$WORK/extensions.zip" "extensions/$name" -d "$PI_DIR"
    done
  else
    unzip -oq "$WORK/extensions.zip" -d "$PI_DIR" -x 'extensions/rtk.ts' 'themes/notte.json'
  fi
  # Reconcile after extraction, which must never overwrite the runtime palette.
  node "$PI_DIR/packages/pi-fornace/native/notte/install.mjs" "$PI_DIR"
fi
if has rules; then
  echo '==> Global rules'
  curl -fsSL "$BASE/agents-global.md" -o "$WORK/rules.md"
  verify_file global_rules "$WORK/rules.md"
  if [ -f "$PI_DIR/AGENTS.md" ]; then
    cp "$PI_DIR/AGENTS.md" "$PI_DIR/AGENTS.md.bak"
    RULES="$WORK/rules.md" node -e '
      const fs=require("fs"),p=process.argv[1],rules=fs.readFileSync(process.env.RULES,"utf8");
      const original=fs.readFileSync(p,"utf8").split("\n").filter(line=>!line.startsWith("<!-- Your own rules go below this line.")).join("\n");
      const start=original.indexOf("<!-- BEGIN fornace global rules");
      const end=original.indexOf("<!-- END fornace global rules");
      if(start>=0 && end>=start){const after=original.indexOf("-->",end);if(after<0)throw Error("Unclosed managed rules");fs.writeFileSync(p,original.slice(0,start)+rules+original.slice(after+3));}
      else fs.writeFileSync(p,rules+"\n"+original);
    ' "$PI_DIR/AGENTS.md"
  else
    cp "$WORK/rules.md" "$PI_DIR/AGENTS.md"
  fi
fi
if has skills; then
  echo '==> Skills'
  curl -fsSL "$BASE/pi-skills.zip" -o "$WORK/skills.zip"
  verify_file skills "$WORK/skills.zip"
  mkdir -p "$PI_DIR/skills"
  if [ -n "$SKLS_ARG" ]; then
    for name in $(printf '%s' "$SKLS_ARG" | tr ',' ' '); do
      unzip -oq "$WORK/skills.zip" "$name/*" -d "$PI_DIR/skills"
    done
  else
    unzip -oq "$WORK/skills.zip" -d "$PI_DIR/skills"
  fi
  if [ -d "$PI_DIR/skills/dynamic-subtitles/tool" ]; then
    (cd "$PI_DIR/skills/dynamic-subtitles/tool" && npm install --no-audit --no-fund)
  fi
fi
# Native MCP: every server in mcpServers survives. Adapter-owned fields
# (settings, imports, claudePlugins, legacy mcp-servers) migrate into
# mcp-adapter.json; conflicts fail loud instead of overwriting.
if has mcp; then
  echo '==> MCP configuration'
  configure_pi mcp
fi
if has settings; then install_settings; fi
if has cmux; then config_cmux; fi
# Final reconcile runs after extensions and skills are unpacked and settings
# are merged: bundled runtime skills become the owners and the global copies
# are excluded from discovery, never deleted.
if has pkgs || has ext || has skills || has mcp || has settings; then
  echo '==> Skill reconcile'
  configure_pi skills
fi
if [ -n "$TOKEN" ]; then
  TOKEN="$TOKEN" node -e '
    const fs=require("fs"),p=process.argv[1];if(!fs.existsSync(p))process.exit(0);
    const s=JSON.parse(fs.readFileSync(p));
    for(const k of ["banana","cavallo"]) if(s[k] && (!s[k].apiKey || s[k].apiKey==="ASK_FRANCESCO")) s[k].apiKey=process.env.TOKEN;
    fs.writeFileSync(p,JSON.stringify(s,null,2)+"\n");fs.chmodSync(p,0o600);
  ' "$PI_DIR/settings.json"
  curl -fsSL "$BASE/dramafoundry-install.sh" -o "$WORK/dramafoundry.sh"
  FORNACE_TOKEN="$TOKEN" PI_SETUP_BASE="$BASE" sh "$WORK/dramafoundry.sh" update
fi
if [ "$CORE" = 1 ]; then
  node "$HOME/.local/lib/fornace/verify-runtime.mjs"
  if [ -f "$HOME/.config/fornace-matchbox/device.json" ]; then
    fornace-matchbox session >/dev/null
  else
    echo 'Local memory is ready. For team access, sign in once: fornace-matchbox login'
  fi
fi
echo 'Update complete. Reload Pi to use the installed runtime.'
