#!/usr/bin/env bash
# install-ipatool.sh — Install ipatool from the latest GitHub release.
# Hardened: arch detection (amd64/arm64), GitHub API token support, SHA-256 verification
# from the release checksum file, atomic install to /usr/local/bin, --dry-run.

set -euo pipefail
IFS=$'\n\t'

readonly SCRIPT_NAME="${0##*/}"
DRY_RUN=0
REPO="majd/ipatool"
INSTALL_PATH="/usr/local/bin/ipatool"

usage() {
  cat <<EOF
Usage: sudo $SCRIPT_NAME [--dry-run] [--help]

Resolves the latest release of majd/ipatool, downloads the tarball for your
architecture, verifies its SHA-256 against the published checksums.txt, and
installs the binary to /usr/local/bin/ipatool atomically.

If \$GITHUB_TOKEN is set in the environment, it is used to authenticate the
GitHub API request (avoids rate limits).

Options:
  --dry-run    Print actions without executing.
  --help, -h   Show this help.
EOF
}

log()  { printf '\033[1;34m[%s]\033[0m %s\n' "${SCRIPT_NAME%.sh}" "$*"; }
warn() { printf '\033[1;33m[%s] WARN:\033[0m %s\n' "${SCRIPT_NAME%.sh}" "$*" >&2; }
die()  { printf '\033[1;31m[%s] ERROR:\033[0m %s\n' "${SCRIPT_NAME%.sh}" "$*" >&2; exit 1; }
run()  { if (( DRY_RUN )); then printf '  DRY-RUN: %s\n' "$*"; else eval "$@"; fi; }
trap 'rc=$?; (( rc )) && printf "\033[1;31m[%s] failed at line %s (exit %d)\033[0m\n" "${SCRIPT_NAME%.sh}" "$LINENO" "$rc" >&2' ERR

while (( $# )); do
  case "$1" in
    --dry-run) DRY_RUN=1 ;;
    -h|--help) usage; exit 0 ;;
    *) die "Unknown argument: $1 (try --help)" ;;
  esac
  shift
done

(( EUID == 0 )) || die "Must run as root. Try: sudo $SCRIPT_NAME"

[[ -r /etc/os-release ]] || die "/etc/os-release not found."
# shellcheck disable=SC1091
. /etc/os-release
log "Detected: ${PRETTY_NAME:-unknown}"

ARCH_RAW="$(uname -m)"
case "$ARCH_RAW" in
  x86_64)  GO_ARCH=amd64 ;;
  aarch64) GO_ARCH=arm64 ;;
  armv7l|armv6l) GO_ARCH=arm ;;
  *) die "Unsupported architecture: $ARCH_RAW" ;;
esac
ASSET_SUFFIX="linux-${GO_ARCH}.tar.gz"

export DEBIAN_FRONTEND=noninteractive
log "Installing prerequisites..."
run "apt-get update -qq"
run "apt-get install -y curl jq tar ca-certificates libsecret-1-0"

GH_HDRS=(-H "Accept: application/vnd.github+json")
[[ -n "${GITHUB_TOKEN:-}" ]] && GH_HDRS+=(-H "Authorization: Bearer ${GITHUB_TOKEN}")

log "Querying GitHub API for latest release of $REPO..."
RELEASE_JSON="$(curl -fsSL "${GH_HDRS[@]}" "https://api.github.com/repos/${REPO}/releases/latest")"
TAG="$(jq -r '.tag_name // empty' <<<"$RELEASE_JSON")"
[[ -n "$TAG" ]] || die "Could not parse latest release tag (rate limited? set GITHUB_TOKEN)."
log "Latest release: $TAG"

DOWNLOAD_URL="$(jq -r --arg s "$ASSET_SUFFIX" '.assets[] | select(.name | endswith($s)) | .browser_download_url' <<<"$RELEASE_JSON" | head -n1)"
CHECKSUM_URL="$(jq -r '.assets[] | select(.name | test("checksums?\\.txt$")) | .browser_download_url' <<<"$RELEASE_JSON" | head -n1)"
[[ "$DOWNLOAD_URL" =~ ^https:// ]] || die "No release asset matching '*${ASSET_SUFFIX}'."

STAGE="$(mktemp -d -t ipatool.XXXXXX)"
trap 'rm -rf "$STAGE"' EXIT
TARBALL="$STAGE/ipatool.tar.gz"

log "Downloading $(basename "$DOWNLOAD_URL")..."
run "curl -fsSL -o '$TARBALL' '$DOWNLOAD_URL'"

if [[ -n "$CHECKSUM_URL" ]]; then
  log "Verifying SHA-256..."
  EXPECTED="$(curl -fsSL "$CHECKSUM_URL" | awk -v f="$(basename "$DOWNLOAD_URL")" '$2 ~ f || $2 == "*"f {print $1; exit}')"
  ACTUAL="$(sha256sum "$TARBALL" | awk '{print $1}')"
  if [[ -n "$EXPECTED" && "$EXPECTED" != "$ACTUAL" ]]; then
    die "SHA-256 mismatch: expected=$EXPECTED actual=$ACTUAL"
  fi
  [[ -n "$EXPECTED" ]] && log "SHA-256 ok." || warn "Asset not listed in checksums.txt; skipping."
else
  warn "No checksums.txt in release; skipping SHA-256 verification."
fi

log "Extracting..."
run "tar -xzf '$TARBALL' -C '$STAGE'"
BINARY_PATH="$(find "$STAGE" -type f -name ipatool -executable -not -name '*.tar.gz' | head -n1 || true)"
if [[ -z "$BINARY_PATH" ]]; then
  # Some releases ship the binary without +x; relax the find
  BINARY_PATH="$(find "$STAGE" -type f -name ipatool -not -name '*.tar.gz' | head -n1 || true)"
fi
[[ -n "$BINARY_PATH" || $DRY_RUN -eq 1 ]] || die "ipatool binary not found inside archive."

log "Installing to $INSTALL_PATH..."
run "install -m 0755 '$BINARY_PATH' '$INSTALL_PATH'"

if (( ! DRY_RUN )) && command -v ipatool >/dev/null 2>&1; then
  log "Installed: $(ipatool --version 2>/dev/null || basename "$INSTALL_PATH") ($TAG)"
fi
log "Done."
