#!/data/data/com.termux/files/usr/bin/bash
# claude-update: Actualizador seguro de Claude Code en Termux
# Respeta techo v2.1.112 (v2.1.113+ usa binarios nativos incompatibles con Bionic)
# Alternativa para versiones nuevas: github.com/ferrumclaudepilgrim/claude-code-android

set -euo pipefail

readonly MAX_VERSION="2.1.112"
readonly PKG="@anthropic-ai/claude-code"
readonly CLAUDE_DIR="$PREFIX/lib/node_modules/${PKG}"
readonly BACKUP_ROOT="$HOME/.cache/claude-updates"
readonly LOG="$BACKUP_ROOT/history.log"
readonly SETTINGS="$HOME/.claude/settings.json"
readonly R="\033[0;31m" G="\033[0;32m" Y="\033[1;33m" C="\033[0;36m" N="\033[0m"

CHECK_ONLY=0; DRY_RUN=0; FORCE=0; ROLLBACK=0; TARGET=""

usage() {
    cat <<EOF
Uso: claude-update [--check|--dry-run|--force|--rollback|--version X.Y.Z|-h]

Actualiza Claude Code respetando el techo v${MAX_VERSION}.
v2.1.113+ usa SEA/glibc → incompatible con Bionic libc de Termux.
Path arriesgado para versiones nuevas (~450 MB, glibc-runner):
  github.com/ferrumclaudepilgrim/claude-code-android
EOF
    exit 0
}

log()  { mkdir -p "$BACKUP_ROOT"; echo "[$(date -Iseconds)] $*" >> "$LOG"; }
die()  { echo -e "${R}ERROR:${N} $*" >&2; log "ERROR: $*"; exit 1; }
info() { echo -e "${C}==>${N} $*"; }
warn() { echo -e "${Y}!${N} $*"; }
ok()   { echo -e "${G}[OK]${N} $*"; }

preflight() {
    [[ -n "${PREFIX:-}" && -d "$PREFIX" ]] || die "\$PREFIX inválido: no parece Termux"
    [[ "$(uname -m)" == "aarch64" ]] || die "Solo aarch64 soportado (uname -m: $(uname -m))"
    command -v npm &>/dev/null || die "npm no encontrado (pkg install nodejs)"
    npm view "$PKG" version &>/dev/null || die "sin acceso a npm registry (¿sin red?)"
    if [[ -f "$SETTINGS" ]] && ! grep -q '"DISABLE_AUTOUPDATER"' "$SETTINGS"; then
        warn "settings.json sin DISABLE_AUTOUPDATER=1"
    fi
    mkdir -p "$BACKUP_ROOT"
}

version_gt() { [[ "$1" != "$2" && "$(printf '%s\n%s' "$1" "$2" | sort -V | tail -1)" == "$1" ]]; }
get_current() { claude --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true; }

verify_node_compat() {
    local ver="$1" files
    info "Verificando v$ver es Node.js puro (busca cli.js, rechaza cli-wrapper.cjs)..."
    files=$(npm pack "$PKG@$ver" --dry-run 2>&1 || true)
    echo "$files" | grep -q 'cli-wrapper.cjs' && die "v$ver es binario nativo → incompatible"
    echo "$files" | grep -q 'cli.js' || warn "v$ver: no se detectó cli.js explícito"
}

do_backup() {
    [[ -d "$CLAUDE_DIR" ]] || return 0
    local dest="$BACKUP_ROOT/backup-$(date +%s)-v$(get_current)"
    info "Backup: $CLAUDE_DIR → $dest"
    cp -a "$CLAUDE_DIR" "$dest"
    log "backup $dest"
    ls -1dt "$BACKUP_ROOT"/backup-* 2>/dev/null | tail -n +4 | xargs -r rm -rf
}

do_rollback() {
    local last
    last=$(ls -1dt "$BACKUP_ROOT"/backup-* 2>/dev/null | head -1)
    [[ -n "$last" ]] || die "sin backups en $BACKUP_ROOT"
    info "Rollback: $last → $CLAUDE_DIR"
    [[ -d "$CLAUDE_DIR" ]] && { chmod -R u+w "$CLAUDE_DIR"; rm -rf "$CLAUDE_DIR"; }
    cp -a "$last" "$CLAUDE_DIR"
    chmod -R a-w "$CLAUDE_DIR"
    ok "Restaurado a v$(get_current)"; log "rollback from $last"; exit 0
}

do_install() {
    local ver="$1"
    (( DRY_RUN )) && { info "[DRY-RUN] npm install -g $PKG@$ver (con backup + protección)"; exit 0; }
    do_backup
    info "Desprotegiendo (chmod u+w)..."
    [[ -d "$CLAUDE_DIR" ]] && chmod -R u+w "$CLAUDE_DIR"
    info "Instalando v$ver..."
    npm install -g "$PKG@$ver"
    info "Reprotegiendo (chmod a-w) contra auto-updater..."
    chmod -R a-w "$CLAUDE_DIR"
    local new; new=$(get_current)
    [[ "$new" == "$ver" ]] || die "instalación inconsistente: esperaba $ver, obtuvo $new"
    ok "Claude Code v$new instalado y protegido"; log "installed v$ver"
}

while [[ $# -gt 0 ]]; do
    case "$1" in
        --check)    CHECK_ONLY=1 ;;
        --dry-run)  DRY_RUN=1 ;;
        --force)    FORCE=1 ;;
        --rollback) ROLLBACK=1 ;;
        --version)  shift; TARGET="${1:-}" ;;
        -h|--help)  usage ;;
        *) die "opción desconocida: $1 (--help para ayuda)" ;;
    esac; shift
done

preflight
(( ROLLBACK )) && do_rollback

CURRENT=$(get_current)
LATEST=$(npm view "$PKG" version 2>/dev/null)
echo -e "${C}Actual:${N}   v${CURRENT:-<no instalado>}"
echo -e "${C}Última:${N}   v${LATEST}"
echo -e "${C}Techo:${N}    v${MAX_VERSION}"

if [[ -z "$TARGET" ]]; then
    if version_gt "$LATEST" "$MAX_VERSION"; then
        TARGET="$MAX_VERSION"
        warn "Última v$LATEST > techo → objetivo v$MAX_VERSION"
    else
        TARGET="$LATEST"
    fi
elif version_gt "$TARGET" "$MAX_VERSION"; then
    die "v$TARGET excede techo v$MAX_VERSION (ver --help para Path A)"
fi

echo -e "${C}Objetivo:${N} v${TARGET}"
(( CHECK_ONLY )) && exit 0

if [[ "$CURRENT" == "$TARGET" ]] && (( ! FORCE )); then
    ok "Ya en v$TARGET (usar --force para reinstalar)"; exit 0
fi

verify_node_compat "$TARGET"
do_install "$TARGET"
