#!/bin/sh
#
# claude-code-native-update.sh — installa/aggiorna Claude Code all'ultima
# versione, nativo FreeBSD, senza Linuxulator e senza dipendere da npm
# (che dalla 2.1.2xx in poi non spedisce piu' il bundle JS).
#
# Come funziona: Anthropic compila il binario Linux ufficiale con
# `bun build --compile`, che incorpora il bundle JS dentro l'ELF secondo
# il formato interno di bun (modulo delimitato da marker
# "/$bunfs/root/<path>\0...\0// @bun @bytecode @bun-cjs\n(function(...)").
# Questo script scarica quel binario, estrae il modulo cli.js usando quei
# marker (niente offset indovinati a mano - sono gli stessi marker che usa
# bun internamente, quindi restano validi anche se la dimensione del
# bundle cambia da una release all'altra), e lo esegue con un `bun`
# nativo FreeBSD gia' presente sul sistema (build via /usr/ports/lang/bun).
#
# Uso:
#   ./claude-code-native-update.sh              # installa/aggiorna a latest
#   ./claude-code-native-update.sh 2.1.209       # installa una versione specifica
#
# Rieseguibile: se la versione installata combacia gia' con quella
# richiesta, non riscarica nulla. Pensato per essere richiamato anche da
# cron/periodic per restare sempre aggiornati automaticamente.

set -eu

PREFIX="${CLAUDE_NATIVE_PREFIX:-/opt/claude-code-native}"
BIN_LINK="${CLAUDE_NATIVE_BIN:-/usr/local/bin/claude}"
BUN_BIN="${CLAUDE_NATIVE_BUN:-$(command -v bun || true)}"
RELEASES_BASE="https://downloads.claude.ai/claude-code-releases"

log() { echo "==> $*"; }
err() { echo "errore: $*" >&2; exit 1; }

[ -n "$BUN_BIN" ] || err "bun non trovato in PATH. Installalo prima: cd /usr/ports/lang/bun && make install (o 'pkg install bun' se gia' in repo), oppure imposta CLAUDE_NATIVE_BUN=/percorso/bun"
[ -x "$BUN_BIN" ] || err "bun non eseguibile: $BUN_BIN"

case "$(uname -m)" in
	amd64|x86_64) PLATFORM="linux-x64" ;;
	aarch64|arm64) PLATFORM="linux-arm64" ;;
	*) err "architettura non supportata: $(uname -m)" ;;
esac

VERSION="${1:-}"
if [ -z "$VERSION" ]; then
	log "controllo ultima versione disponibile..."
	VERSION="$(fetch -q -o - "${RELEASES_BASE}/latest")" \
		|| err "impossibile leggere ${RELEASES_BASE}/latest"
fi
[ -n "$VERSION" ] || err "versione vuota, qualcosa non ha funzionato nel fetch"
log "versione target: $VERSION"

VERDIR="${PREFIX}/${VERSION}"
CURRENT_VERSION=""
[ -f "${PREFIX}/current/VERSION" ] && CURRENT_VERSION="$(cat "${PREFIX}/current/VERSION")"

if [ "$CURRENT_VERSION" = "$VERSION" ] && [ -f "${PREFIX}/current/claude-entry.js" ]; then
	log "gia' alla versione $VERSION, niente da fare"
	exit 0
fi

WORK="$(mktemp -d "${TMPDIR:-/tmp}/claude-native.XXXXXX")"
trap 'rm -rf "$WORK"' EXIT

BINURL="${RELEASES_BASE}/${VERSION}/${PLATFORM}/claude"
log "scarico binario ufficiale: $BINURL"
fetch -o "${WORK}/claude-bin" "$BINURL" || err "download fallito"

log "estraggo il modulo cli.js dal bundle Bun (parsing dei marker /\$bunfs/root/...)"
python3 - "$WORK/claude-bin" "$WORK/claude-cli.js" <<'PYEOF'
import re, sys

src_path, out_path = sys.argv[1], sys.argv[2]
data = open(src_path, "rb").read()

# Ogni modulo compilato da bun e' preceduto da un'intestazione tipo:
#   /$bunfs/root/<path>\x00[/$bunfs/root/<path>\x00]// @bun @bytecode @bun-cjs\n(function(exports, require, module, __filename, __dirname) { ... }
# I byte esatti prima del path variano (non e' sempre uno \x00 secco), quindi
# ancoriamo sulla stringa fissa "// @bun @bytecode @bun-cjs\n(function(...)"
# e verifichiamo solo che "cli.js" compaia poco prima, senza pretendere una
# struttura di byte nulli identica per ogni modulo.
ANCHOR = b"// @bun @bytecode @bun-cjs\n(function(exports, require, module, __filename, __dirname) {"
positions = [mo.start() for mo in re.finditer(re.escape(ANCHOR), data)]
if not positions:
    sys.exit("nessun modulo bun trovato nel binario (marker @bun @bytecode assente) - formato cambiato?")

target = None
for p in positions:
    preceding = data[max(0, p - 300):p]
    if b"cli.js" in preceding:
        target = p
        break
if target is None:
    sys.exit("trovati moduli bun ma nessuno con 'cli.js' nel path poco prima - formato cambiato?")

start = target + len(b"// @bun @bytecode @bun-cjs\n")  # inizio di "(function(exports, ..."
if data[start:start + 10] != b"(function(":
    sys.exit("il contenuto dopo il marker non inizia con il wrapper CJS atteso")

# Fine modulo = intestazione del modulo successivo ("\x00/$bunfs/root/...",
# la stessa struttura vista prima dell'anchor corrente), oppure fine file
# se cli.js fosse l'ultimo modulo del bundle.
next_header = data.find(b"\x00/$bunfs/root/", start)
end = next_header if next_header != -1 else len(data)

module_bytes = data[start:end]
if not module_bytes.rstrip().endswith(b")"):
    sys.exit("il modulo estratto non termina come atteso (bundle troncato?)")

with open(out_path, "wb") as f:
    f.write(module_bytes)

print(f"estratti {len(module_bytes)} byte")
PYEOF

[ -s "${WORK}/claude-cli.js" ] || err "estrazione fallita, cli.js vuoto"

cat > "${WORK}/claude-entry.js" <<'EOF'
const fs = require('fs');
const path = require('path');
const code = fs.readFileSync(path.join(__dirname, 'claude-cli.js'), 'utf8');
const fn = eval(code);
fn(exports, require, module, __filename, __dirname);
EOF

log "verifico che l'estratto funzioni davvero (claude --version)"
GOTVER="$("$BUN_BIN" "${WORK}/claude-entry.js" --version 2>&1 | head -1)" \
	|| err "il bundle estratto non parte con bun: $GOTVER"
case "$GOTVER" in
	${VERSION}*) ;;
	*) err "versione inattesa dopo l'estrazione: '$GOTVER' (atteso $VERSION)" ;;
esac
log "OK: $GOTVER"

log "installo in ${VERDIR}"
if [ "$(id -u)" != 0 ] && [ ! -w "$(dirname "$PREFIX")" ]; then
	SUDO="sudo"
else
	SUDO=""
fi
$SUDO mkdir -p "$VERDIR"
$SUDO cp "${WORK}/claude-cli.js" "${WORK}/claude-entry.js" "${WORK}/claude-bin" "$VERDIR/"
$SUDO chmod +x "${VERDIR}/claude-bin"
echo "$VERSION" | $SUDO tee "${VERDIR}/VERSION" > /dev/null

$SUDO ln -sfn "$VERDIR" "${PREFIX}/current"

$SUDO tee "$BIN_LINK" > /dev/null <<EOF
#!/bin/sh
export USE_BUILTIN_RIPGREP=0
export DISABLE_AUTOUPDATER=1
exec "$BUN_BIN" "${PREFIX}/current/claude-entry.js" "\$@"
EOF
$SUDO chmod +x "$BIN_LINK"

log "fatto: $BIN_LINK -> Claude Code $VERSION (nativo FreeBSD, bun, no Linuxulator)"
