#!/bin/sh
# ChamPrep CLI installer
#
# Usage:
#   curl -fsSL https://cli.champrep.com/install.sh | sh
#   curl -fsSL https://cli.champrep.com/install.sh | sh -s -- --version v1.2.3 --prefix /usr/local/bin
#
# Options:
#   --version VERSION   install a specific version (default: latest from manifest.json)
#   --prefix PATH       install directory (default: $HOME/.local/bin, or /usr/local/bin if root)
#   --quiet             suppress progress output
#   --uninstall         remove champrep
#   --help              print this help and exit
#
# POSIX sh — no bashisms, no pipefail.

set -eu

# ---------------------------------------------------------------------------
# Defaults
# ---------------------------------------------------------------------------
MANIFEST_URL="https://cli.champrep.com/manifest.json"
DOWNLOAD_BASE="https://cli.champrep.com"
VERSION=""
PREFIX=""
QUIET=0
UNINSTALL=0
TMPDIR_CREATED=""

PROG="champrep"
COLOR_RESET=""
COLOR_RED=""
COLOR_GREEN=""
COLOR_BLUE=""
COLOR_DIM=""

if [ -t 1 ] && [ -t 2 ]; then
  COLOR_RESET="$(printf '\033[0m')"
  COLOR_RED="$(printf '\033[31m')"
  COLOR_GREEN="$(printf '\033[32m')"
  COLOR_BLUE="$(printf '\033[34m')"
  COLOR_DIM="$(printf '\033[2m')"
fi

# ---------------------------------------------------------------------------
# Logging
# ---------------------------------------------------------------------------
info() {
  [ "$QUIET" -eq 1 ] && return 0
  printf '%s==>%s %s\n' "$COLOR_BLUE" "$COLOR_RESET" "$*"
}

ok() {
  [ "$QUIET" -eq 1 ] && return 0
  printf '%s✓%s  %s\n' "$COLOR_GREEN" "$COLOR_RESET" "$*"
}

warn() {
  printf '%swarn:%s %s\n' "$COLOR_DIM" "$COLOR_RESET" "$*" >&2
}

die() {
  _line="${2:-?}"
  printf '%serror (line %s):%s %s\n' "$COLOR_RED" "$_line" "$COLOR_RESET" "$1" >&2
  exit 1
}

# ---------------------------------------------------------------------------
# Cleanup
# ---------------------------------------------------------------------------
cleanup() {
  if [ -n "${TMPDIR_CREATED:-}" ] && [ -d "$TMPDIR_CREATED" ]; then
    rm -rf "$TMPDIR_CREATED" 2>/dev/null || true
  fi
}
trap cleanup EXIT INT HUP TERM

# ---------------------------------------------------------------------------
# Help
# ---------------------------------------------------------------------------
usage() {
  cat <<'EOF'
ChamPrep CLI installer

Usage:
  curl -fsSL https://cli.champrep.com/install.sh | sh
  curl -fsSL https://cli.champrep.com/install.sh | sh -s -- [options]

Options:
  --version VERSION   install a specific version (e.g. v1.2.3)
  --prefix PATH       install directory (default: $HOME/.local/bin)
  --quiet             suppress progress output
  --uninstall         remove champrep from the install prefix
  --help              print this help and exit

Environment:
  CHAMPREP_INSTALL_MANIFEST_URL   override the manifest URL (default: https://cli.champrep.com/manifest.json)
EOF
}

# ---------------------------------------------------------------------------
# Argument parsing
# ---------------------------------------------------------------------------
while [ $# -gt 0 ]; do
  case "$1" in
    --version)
      [ $# -ge 2 ] || die "--version requires an argument" "$LINENO"
      VERSION="$2"
      shift 2
      ;;
    --version=*)
      VERSION="${1#--version=}"
      shift
      ;;
    --prefix)
      [ $# -ge 2 ] || die "--prefix requires an argument" "$LINENO"
      PREFIX="$2"
      shift 2
      ;;
    --prefix=*)
      PREFIX="${1#--prefix=}"
      shift
      ;;
    --quiet|-q)
      QUIET=1
      shift
      ;;
    --uninstall)
      UNINSTALL=1
      shift
      ;;
    --help|-h)
      usage
      exit 0
      ;;
    *)
      die "unknown option: $1 (try --help)" "$LINENO"
      ;;
  esac
done

if [ -n "${CHAMPREP_INSTALL_MANIFEST_URL:-}" ]; then
  MANIFEST_URL="$CHAMPREP_INSTALL_MANIFEST_URL"
fi

# ---------------------------------------------------------------------------
# Detect downloader — prefer curl, fall back to wget
# ---------------------------------------------------------------------------
DL=""
if command -v curl >/dev/null 2>&1; then
  DL="curl"
elif command -v wget >/dev/null 2>&1; then
  DL="wget"
else
  die "neither curl nor wget is installed — cannot fetch the manifest" "$LINENO"
fi

# fetch URL to stdout
fetch() {
  _url="$1"
  if [ "$DL" = "curl" ]; then
    curl -fsSL "$_url"
  else
    wget -qO- "$_url"
  fi
}

# download URL to file path
download_to() {
  _url="$1"
  _out="$2"
  if [ "$DL" = "curl" ]; then
    curl -fsSL -o "$_out" "$_url"
  else
    wget -q -O "$_out" "$_url"
  fi
}

# ---------------------------------------------------------------------------
# Detect OS / arch
# ---------------------------------------------------------------------------
detect_os() {
  _os=$(uname -s 2>/dev/null || echo unknown)
  case "$_os" in
    Darwin) echo "darwin" ;;
    Linux)  echo "linux" ;;
    *)
      die "unsupported OS: $_os (darwin and linux only — Windows users install via Scoop)" "$LINENO"
      ;;
  esac
}

detect_arch() {
  _arch=$(uname -m 2>/dev/null || echo unknown)
  case "$_arch" in
    x86_64|amd64)  echo "amd64" ;;
    arm64|aarch64) echo "arm64" ;;
    *)
      die "unsupported architecture: $_arch (amd64 and arm64 only)" "$LINENO"
      ;;
  esac
}

# ---------------------------------------------------------------------------
# Resolve install prefix
# ---------------------------------------------------------------------------
resolve_prefix() {
  if [ -n "$PREFIX" ]; then
    echo "$PREFIX"
    return 0
  fi
  _uid=$(id -u 2>/dev/null || echo 1000)
  if [ "$_uid" = "0" ]; then
    echo "/usr/local/bin"
  else
    echo "$HOME/.local/bin"
  fi
}

# ---------------------------------------------------------------------------
# Uninstall path
# ---------------------------------------------------------------------------
if [ "$UNINSTALL" -eq 1 ]; then
  UNPREFIX=$(resolve_prefix)
  TARGET="$UNPREFIX/$PROG"
  if [ -f "$TARGET" ]; then
    rm -f "$TARGET" || die "failed to remove $TARGET" "$LINENO"
    ok "removed $TARGET"
  else
    info "$TARGET does not exist — nothing to uninstall"
  fi
  exit 0
fi

# ---------------------------------------------------------------------------
# JSON extraction — jq fast path, awk/sed fallback
# ---------------------------------------------------------------------------
has_jq=0
if command -v jq >/dev/null 2>&1; then
  has_jq=1
fi

# scalar_from_json <json-text> <dot-path>
#   example: scalar_from_json "$json" ".version"
#            scalar_from_json "$json" ".downloads.darwin_arm64.url"
scalar_from_json() {
  _json="$1"
  _path="$2"
  if [ "$has_jq" -eq 1 ]; then
    printf '%s' "$_json" | jq -r "$_path // empty"
    return 0
  fi

  # POSIX fallback: flatten the JSON, then grep the leaf key.
  # Works for the fixed shape we ship — not a general JSON parser.
  _leaf=$(printf '%s' "$_path" | awk -F'.' '{print $NF}')

  # Normalise whitespace and newlines, then grep for "leaf" : "value"
  printf '%s' "$_json" \
    | tr '\n' ' ' \
    | tr -s ' ' \
    | awk -v key="$_leaf" '
      {
        line=$0
        pat="\"" key "\"[[:space:]]*:[[:space:]]*\"[^\"]*\""
        while (match(line, pat)) {
          s=substr(line, RSTART, RLENGTH)
          # extract value between the last pair of quotes
          n=split(s, parts, "\"")
          print parts[4]
          line=substr(line, RSTART+RLENGTH)
        }
      }
    ' \
    | head -n 1
}

# Get a nested download entry without jq by scoping the search to its block.
# extract_download <json> <os_arch> <field>   (field = url|sha256)
extract_download() {
  _json="$1"
  _key="$2"
  _field="$3"
  if [ "$has_jq" -eq 1 ]; then
    printf '%s' "$_json" | jq -r ".downloads.${_key}.${_field} // empty"
    return 0
  fi
  # Isolate the block "os_arch": { ... }
  printf '%s' "$_json" \
    | tr '\n' ' ' \
    | tr -s ' ' \
    | awk -v key="$_key" -v field="$_field" '
      {
        txt=$0
        pat="\"" key "\"[[:space:]]*:[[:space:]]*\\{[^}]*\\}"
        if (match(txt, pat)) {
          block=substr(txt, RSTART, RLENGTH)
          fpat="\"" field "\"[[:space:]]*:[[:space:]]*\"[^\"]*\""
          if (match(block, fpat)) {
            v=substr(block, RSTART, RLENGTH)
            n=split(v, parts, "\"")
            print parts[4]
          }
        }
      }
    '
}

# ---------------------------------------------------------------------------
# sha256 verification
# ---------------------------------------------------------------------------
sha256_of() {
  _f="$1"
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$_f" | awk '{print $1}'
  elif command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "$_f" | awk '{print $1}'
  else
    die "no sha256 utility (sha256sum / shasum) found — cannot verify download" "$LINENO"
  fi
}

# ---------------------------------------------------------------------------
# Main install flow
# ---------------------------------------------------------------------------
OS=$(detect_os)
ARCH=$(detect_arch)
OS_ARCH="${OS}_${ARCH}"

info "detected $OS/$ARCH"

info "fetching manifest: $MANIFEST_URL"
MANIFEST_JSON=$(fetch "$MANIFEST_URL") || die "failed to fetch $MANIFEST_URL" "$LINENO"
[ -n "$MANIFEST_JSON" ] || die "empty manifest from $MANIFEST_URL" "$LINENO"

if [ -z "$VERSION" ]; then
  VERSION=$(scalar_from_json "$MANIFEST_JSON" ".version")
  [ -n "$VERSION" ] || die "could not read .version from manifest" "$LINENO"
fi
# normalise: strip leading 'v' for URL construction, keep for display
VERSION_NOV=${VERSION#v}
VERSION_DISPLAY="v${VERSION_NOV}"

info "installing version $VERSION_DISPLAY"

URL=$(extract_download "$MANIFEST_JSON" "$OS_ARCH" "url")
SHA=$(extract_download "$MANIFEST_JSON" "$OS_ARCH" "sha256")

[ -n "$URL" ] || die "no download URL in manifest for $OS_ARCH" "$LINENO"
[ -n "$SHA" ] || die "no sha256 in manifest for $OS_ARCH" "$LINENO"

INSTALL_PREFIX=$(resolve_prefix)
info "install prefix: $INSTALL_PREFIX"

TMPDIR_CREATED=$(mktemp -d 2>/dev/null || mktemp -d -t champrep) \
  || die "failed to create temp dir" "$LINENO"

ARCHIVE="$TMPDIR_CREATED/champrep.tar.gz"
info "downloading $URL"
download_to "$URL" "$ARCHIVE" || die "download failed: $URL" "$LINENO"

info "verifying sha256"
GOT=$(sha256_of "$ARCHIVE")
if [ "$GOT" != "$SHA" ]; then
  die "sha256 mismatch for $URL
  expected: $SHA
  got:      $GOT" "$LINENO"
fi
ok "sha256 ok ($GOT)"

info "extracting"
( cd "$TMPDIR_CREATED" && tar -xzf "$ARCHIVE" ) \
  || die "failed to extract $ARCHIVE" "$LINENO"

# Locate the binary in the extracted tree (handles flat or nested layouts)
BIN=""
if [ -f "$TMPDIR_CREATED/$PROG" ]; then
  BIN="$TMPDIR_CREATED/$PROG"
else
  BIN=$(find "$TMPDIR_CREATED" -type f -name "$PROG" 2>/dev/null | head -n 1 || true)
fi
[ -n "$BIN" ] && [ -f "$BIN" ] || die "couldn't find '$PROG' binary in archive" "$LINENO"

chmod +x "$BIN" || die "failed to chmod +x $BIN" "$LINENO"

mkdir -p "$INSTALL_PREFIX" 2>/dev/null || die "cannot create $INSTALL_PREFIX (permission denied?)" "$LINENO"

TARGET="$INSTALL_PREFIX/$PROG"
if ! mv "$BIN" "$TARGET" 2>/dev/null; then
  if command -v sudo >/dev/null 2>&1; then
    warn "$INSTALL_PREFIX is not writable — retrying with sudo"
    sudo mv "$BIN" "$TARGET" || die "sudo mv failed" "$LINENO"
    sudo chmod +x "$TARGET" || die "sudo chmod failed" "$LINENO"
  else
    die "cannot write to $INSTALL_PREFIX (re-run with --prefix or as root)" "$LINENO"
  fi
fi

ok "installed $TARGET ($VERSION_DISPLAY)"

# ---------------------------------------------------------------------------
# PATH hint
# ---------------------------------------------------------------------------
case ":$PATH:" in
  *:"$INSTALL_PREFIX":*) ;;
  *)
    warn "$INSTALL_PREFIX is not in your PATH"
    warn "  add to ~/.profile or ~/.zshrc:  export PATH=\"$INSTALL_PREFIX:\$PATH\""
    ;;
esac

if [ "$QUIET" -eq 0 ]; then
  cat <<EOF

${COLOR_GREEN}champrep ${VERSION_DISPLAY} is installed.${COLOR_RESET}

Next:
  ${COLOR_BLUE}champrep auth token${COLOR_RESET}       # CI / automation (paste an API key)
  ${COLOR_BLUE}champrep auth login${COLOR_RESET}       # interactive browser login
  ${COLOR_BLUE}champrep drive ls${COLOR_RESET}

Docs: https://cli.champrep.com/
EOF
fi

exit 0
