#!/bin/sh
# xin installer — https://xinproxy.com
#
#   curl -fsSL https://xinproxy.com/install.sh | sh
#   curl -fsSL https://xinproxy.com/install.sh | sh -s -- --product xin-gateway
#
# Enrols the signed xin repository with your system package manager (apt or
# dnf/yum) and installs from it, so `apt-get upgrade` keeps working afterwards.
# Falls back to a signed tarball only where no package feed exists.
#
# Flags:
#   --product <xin|xin-gateway|xin-envoyd>   default: xin
#   --version <x.y.z>                        default: 0.1.1 (tarball path only)
#   --method  <auto|apt|rpm|tarball>         default: auto
#   --prefix  <dir>                          tarball install prefix, default /usr/local
#   --start                                  enable and start the service after install
#   --dry-run                                print what would run, change nothing
#   --help
#
# This script is proprietary software, free for non-commercial use.
# Read it before piping it to a shell. That advice is not specific to us.

set -eu

XIN_VERSION="0.1.1"
PRODUCT="xin"
METHOD="auto"
PREFIX="/usr/local"
START=0
DRY=0

APT_HOST="https://apt.xinproxy.com"
RPM_HOST="https://rpm.xinproxy.com"
DL_HOST="https://dl.xinproxy.com"

# sha256 of a zero-byte file. Some artefacts on the download host are
# placeholders, and their real checksum in SHA256SUMS is this value — so a
# checksum match alone does NOT prove a usable download. Reject it explicitly.
EMPTY_SHA="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"

RED=''; GRN=''; YEL=''; DIM=''; BLD=''; RST=''
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
  RED=$(printf '\033[31m'); GRN=$(printf '\033[32m'); YEL=$(printf '\033[33m')
  DIM=$(printf '\033[2m');  BLD=$(printf '\033[1m');  RST=$(printf '\033[0m')
fi

say()  { printf '%s\n' "$*"; }
step() { printf '%s==>%s %s\n' "$GRN" "$RST" "$*"; }
warn() { printf '%swarning:%s %s\n' "$YEL" "$RST" "$*" >&2; }
die()  { printf '%serror:%s %s\n' "$RED" "$RST" "$*" >&2; exit 1; }

usage() { sed -n '2,21p' "$0" | sed 's/^# \{0,1\}//'; exit 0; }

while [ $# -gt 0 ]; do
  case "$1" in
    --product) PRODUCT="${2:-}"; shift 2 ;;
    --version) XIN_VERSION="${2:-}"; shift 2 ;;
    --method)  METHOD="${2:-}";  shift 2 ;;
    --prefix)  PREFIX="${2:-}";  shift 2 ;;
    --start)   START=1; shift ;;
    --dry-run) DRY=1; shift ;;
    -h|--help) usage ;;
    *) die "unknown option: $1 (try --help)" ;;
  esac
done

case "$PRODUCT" in
  xin|xin-gateway|xin-envoyd) ;;
  gateway) PRODUCT="xin-gateway" ;;
  envoyd)  PRODUCT="xin-envoyd" ;;
  *) die "unknown product: $PRODUCT (xin, xin-gateway, xin-envoyd)" ;;
esac

# ---------------------------------------------------------------- privileges
SUDO=""
if [ "$(id -u)" -ne 0 ]; then
  if command -v sudo >/dev/null 2>&1; then
    SUDO="sudo"
  else
    die "this needs root and sudo was not found; re-run as root"
  fi
fi

run() {
  if [ "$DRY" -eq 1 ]; then
    printf '%s  %s%s\n' "$DIM" "$*" "$RST"
  else
    # shellcheck disable=SC2086
    $SUDO "$@"
  fi
}

# Run a shell pipeline as root (for the here-doc/tee patterns below).
run_sh() {
  if [ "$DRY" -eq 1 ]; then
    printf '%s  %s%s\n' "$DIM" "$1" "$RST"
  else
    if [ -n "$SUDO" ]; then $SUDO sh -c "$1"; else sh -c "$1"; fi
  fi
}

have() { command -v "$1" >/dev/null 2>&1; }

fetch() {
  # fetch <url> <dest>
  if have curl; then
    curl -fsSL --proto '=https' --tlsv1.2 -o "$2" "$1"
  elif have wget; then
    wget -qO "$2" "$1"
  else
    die "neither curl nor wget is available"
  fi
}

# ---------------------------------------------------------------- detection
UNAME_S=$(uname -s 2>/dev/null || echo unknown)
UNAME_M=$(uname -m 2>/dev/null || echo unknown)

[ "$UNAME_S" = "Linux" ] || die "xin currently ships Linux binaries only (detected: $UNAME_S)"

case "$UNAME_M" in
  x86_64|amd64)      ARCH="amd64" ;;
  aarch64|arm64)     ARCH="arm64" ;;
  i386|i486|i586|i686) ARCH="i686" ;;
  armv7l|armv7)      ARCH="armv7" ;;
  armv6l|armv6)      ARCH="armv6" ;;
  ppc64le)           ARCH="ppc64le" ;;
  s390x)             ARCH="s390x" ;;
  *) die "unsupported architecture: $UNAME_M" ;;
esac

# Raspberry Pi 1 / Zero are ARMv6 but Debian's armhf means ARMv7+VFP3, so they
# get their own apt suite. dpkg cannot tell the two apart on its own.
APT_SUITE="stable"
if [ "$ARCH" = "armv6" ]; then
  APT_SUITE="raspbian"
fi

OS_ID=""; OS_LIKE=""
if [ -r /etc/os-release ]; then
  # shellcheck disable=SC1091
  . /etc/os-release
  OS_ID="${ID:-}"; OS_LIKE="${ID_LIKE:-}"
fi

pick_method() {
  case "$METHOD" in
    apt|rpm|tarball) echo "$METHOD"; return ;;
  esac
  if have apt-get; then echo apt; return; fi
  if have dnf || have yum; then echo rpm; return; fi
  echo tarball
}

METHOD=$(pick_method)

say ""
say "${BLD}xin installer${RST}  ${DIM}$PRODUCT · $ARCH · ${OS_ID:-unknown} · via $METHOD${RST}"
say ""

# ---------------------------------------------------------------- apt
install_apt() {
  step "Enrolling $APT_HOST (suite: $APT_SUITE)"
  run_sh "install -d -m 0755 /usr/share/keyrings"

  if [ "$DRY" -eq 1 ]; then
    printf '%s  curl -fsSL %s/keys/xin-archive-keyring.gpg > /usr/share/keyrings/xin.gpg%s\n' \
      "$DIM" "$APT_HOST" "$RST"
  else
    tmpkey=$(mktemp)
    fetch "$APT_HOST/keys/xin-archive-keyring.gpg" "$tmpkey" \
      || die "could not download the signing key from $APT_HOST"
    [ -s "$tmpkey" ] || die "the downloaded signing key is empty"
    $SUDO install -m 0644 "$tmpkey" /usr/share/keyrings/xin.gpg
    rm -f "$tmpkey"
  fi

  run_sh "printf 'deb [signed-by=/usr/share/keyrings/xin.gpg] %s %s main\n' '$APT_HOST' '$APT_SUITE' > /etc/apt/sources.list.d/xin.list"

  step "apt-get update"
  run env DEBIAN_FRONTEND=noninteractive apt-get update

  step "Installing $PRODUCT"
  run env DEBIAN_FRONTEND=noninteractive apt-get install -y "$PRODUCT"
}

# ---------------------------------------------------------------- rpm
install_rpm() {
  # The published RPM feed is built for the EL8 baseline and installs cleanly
  # back to CentOS 7, which is why the metadata is gzip+sqlite rather than zstd.
  step "Enrolling $RPM_HOST/el8"
  run_sh "cat > /etc/yum.repos.d/xin.repo <<'XINREPO'
[xin]
name=xin
baseurl=$RPM_HOST/el8
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=$RPM_HOST/keys/xin-archive-keyring.asc
XINREPO"

  step "Installing $PRODUCT"
  if have dnf; then
    run dnf install -y "$PRODUCT"
  else
    run yum install -y "$PRODUCT"
  fi
}

# ---------------------------------------------------------------- tarball
install_tarball() {
  # Prefer the static musl build: no libc floor to satisfy.
  base="$PRODUCT-$XIN_VERSION-$ARCH"
  for cand in "$base-musl" "$base"; do
    url="$DL_HOST/$cand.tar.gz"
    code=$(curl -fsS -o /dev/null -w '%{http_code}' --head "$url" 2>/dev/null || echo 000)
    if [ "$code" = "200" ]; then
      TARNAME="$cand.tar.gz"; TARURL="$url"; break
    fi
  done
  [ -n "${TARURL:-}" ] || die "no tarball published for $PRODUCT $XIN_VERSION on $ARCH — use apt or dnf, or see https://xinproxy.com/releases"

  step "Downloading $TARNAME"
  tmpd=$(mktemp -d)
  trap 'rm -rf "$tmpd"' EXIT

  if [ "$DRY" -eq 1 ]; then
    printf '%s  fetch %s%s\n' "$DIM" "$TARURL" "$RST"
    return
  fi

  fetch "$TARURL" "$tmpd/$TARNAME" || die "download failed: $TARURL"

  step "Verifying checksum"
  fetch "$DL_HOST/SHA256SUMS" "$tmpd/SHA256SUMS" || die "could not download SHA256SUMS"

  want=$(grep -E "[[:space:]]\*?$TARNAME\$" "$tmpd/SHA256SUMS" | awk '{print $1}' | head -n1)
  [ -n "$want" ] || die "$TARNAME is not listed in SHA256SUMS"

  if have sha256sum; then
    got=$(sha256sum "$tmpd/$TARNAME" | awk '{print $1}')
  elif have shasum; then
    got=$(shasum -a 256 "$tmpd/$TARNAME" | awk '{print $1}')
  else
    die "no sha256sum or shasum available to verify the download"
  fi

  [ "$want" = "$got" ] || die "checksum mismatch for $TARNAME (expected $want, got $got)"

  # A published-but-empty artefact checksums correctly against SHA256SUMS,
  # because the recorded hash is the hash of nothing. Refuse it loudly rather
  # than "successfully" installing zero bytes.
  if [ "$got" = "$EMPTY_SHA" ] || [ ! -s "$tmpd/$TARNAME" ]; then
    die "$TARNAME is published but empty — that build is not available yet. Use apt/dnf, or pick another architecture: https://xinproxy.com/releases"
  fi

  step "Installing into $PREFIX"
  tar -xzf "$tmpd/$TARNAME" -C "$tmpd"
  found=0
  for b in "$PRODUCT" xind xin-gateway xin-envoyd; do
    p=$(find "$tmpd" -type f -name "$b" -perm -u+x 2>/dev/null | head -n1)
    if [ -n "$p" ]; then
      $SUDO install -m 0755 "$p" "$PREFIX/bin/$b"
      say "    $PREFIX/bin/$b"
      found=1
    fi
  done
  [ "$found" -eq 1 ] || die "no executable found inside $TARNAME"

  warn "A tarball install has no systemd unit and no package upgrades."
  warn "Where a package feed exists, prefer --method apt or --method rpm."
}

case "$METHOD" in
  apt)     install_apt ;;
  rpm)     install_rpm ;;
  tarball) install_tarball ;;
  *) die "unknown method: $METHOD" ;;
esac

[ "$DRY" -eq 1 ] && { say ""; say "${DIM}dry run — nothing was changed${RST}"; exit 0; }

# ---------------------------------------------------------------- verify
say ""
step "Verifying the install"

BIN="xind"
[ "$PRODUCT" = "xin-gateway" ] && BIN="xin-gateway"
[ "$PRODUCT" = "xin-envoyd" ]  && BIN="xin-envoyd"

if have "$BIN"; then
  "$BIN" -v 2>/dev/null || "$BIN" --version 2>/dev/null || true
else
  warn "$BIN is not on PATH yet — open a new shell, or check $PREFIX/bin"
fi

# For the proxy, the useful first action is reading the nginx config you
# already have. This only reads; it starts nothing and changes nothing.
if [ "$PRODUCT" = "xin" ] && [ -r /etc/nginx/nginx.conf ] && have xind; then
  say ""
  step "Reading your existing /etc/nginx/nginx.conf (read-only)"
  say ""
  xind -t -c /etc/nginx/nginx.conf || true
fi

# ---------------------------------------------------------------- start
if [ "$START" -eq 1 ]; then
  if have systemctl; then
    svc="xin"
    [ "$PRODUCT" = "xin-gateway" ] && svc="xin-gateway"
    [ "$PRODUCT" = "xin-envoyd" ]  && svc="xin-envoyd"
    step "Enabling and starting $svc"
    run systemctl enable --now "$svc"
    run systemctl --no-pager --full status "$svc" || true
  else
    warn "--start needs systemd; start $BIN yourself"
  fi
else
  say ""
  say "${BLD}Installed.${RST} xin is not running yet — it never starts itself."
  if [ "$PRODUCT" = "xin" ]; then
    say ""
    say "  ${DIM}# check what xin makes of your config${RST}"
    say "  xind -t -c /etc/nginx/nginx.conf"
    say ""
    say "  ${DIM}# when you are ready — stop nginx first, they share port 80${RST}"
    say "  sudo systemctl stop nginx && sudo systemctl enable --now xin"
  else
    say ""
    say "  sudo systemctl enable --now $PRODUCT"
  fi
fi

say ""
say "Docs and benchmarks: ${BLD}https://xinproxy.com${RST}"
say ""
