#!/usr/bin/env bash
# Meshia Node installer — one line, no sudo, idempotent.
#
#   curl -fsSL https://<host>/install.sh | bash -s -- \
#     --package-url https://<host>/meshia-node-1.0.0.tar.gz \
#     --package-sha256 <sha256> \
#     -- --pair mesh_XXXX --api-url https://meshia.example --tier compute
#
# Everything lands under ~/.meshia (0700) and ~/.local/bin. Nothing is written
# outside the user's home directory and root is never required.
set -euo pipefail

UV_VERSION="${MESHIA_UV_VERSION:-0.8.13}"
PYTHON_VERSION="${MESHIA_PYTHON_VERSION:-3.12}"
MESHIA_HOME="${MESHIA_HOME:-$HOME/.meshia}"
RUNTIME_DIR="$MESHIA_HOME/runtime"
TOOL_DIR="$MESHIA_HOME/bin"
BIN_DIR="${MESHIA_BIN_DIR:-$HOME/.local/bin}"
PACKAGE_URL="${MESHIA_NODE_PACKAGE_URL:-}"
PACKAGE_SHA256="${MESHIA_NODE_PACKAGE_SHA256:-}"
FROM_DIR=""
INSECURE_DEV=0
ALLOW_MACOS="${MESHIA_ALLOW_MACOS:-0}"
PAIR_CODE="${PAIR_CODE:-}"
API_URL="${API_URL:-}"
TIER="${TIER:-personal}"
ACCESS="${ACCESS:-}"
WORKSPACE="${WORKSPACE:-}"
CONNECT_MODE="${CONNECT_MODE:-foreground}"
WORK_DIR=""

log()  { printf '  %s\n' "$*" >&2; }
step() { printf '\n▸ %s\n' "$*" >&2; }
die()  { printf '\nmeshia-node install failed: %s\n' "$*" >&2; exit 1; }

cleanup() { [ -n "$WORK_DIR" ] && rm -rf "$WORK_DIR" || true; }
trap cleanup EXIT

usage() {
  cat >&2 <<'USAGE'
Usage: install.sh [options] [-- <connect options>]

Install options:
  --package-url URL       sdist/wheel to install (or $MESHIA_NODE_PACKAGE_URL)
  --package-sha256 HEX    required checksum for --package-url (or $MESHIA_NODE_PACKAGE_SHA256)
  --from-dir DIR          install from a local checkout instead (dev/test mode)
  --insecure-dev          allow --package-url without a checksum (development only)
  --python X.Y            interpreter version to provision (default 3.12)
  --bin-dir DIR           symlink location (default ~/.local/bin)
  --allow-macos           proceed on macOS instead of pointing at the Mac app
  -h, --help              show this message

Connect options (after `--`):
  --pair CODE             one-time mesh_… pairing code; runs `meshia-node connect`
  --api-url URL           control-plane origin
  --tier personal|compute node tier (default personal)
  --access limited|full   compute access mode (implies --tier compute; limited =
                          read/write workspace + read-only outside, full = read/write
                          everywhere)
  --workspace PATH        workspace directory (default ~/.meshia/workspace)
  --no-connect            install only, even if --pair was supplied
USAGE
}

# ---------------------------------------------------------------- argument parsing
while [ $# -gt 0 ]; do
  case "$1" in
    --package-url)    PACKAGE_URL="${2:-}"; shift 2 ;;
    --package-sha256) PACKAGE_SHA256="${2:-}"; shift 2 ;;
    --from-dir)       FROM_DIR="${2:-}"; shift 2 ;;
    --insecure-dev)   INSECURE_DEV=1; shift ;;
    --python)         PYTHON_VERSION="${2:-}"; shift 2 ;;
    --bin-dir)        BIN_DIR="${2:-}"; shift 2 ;;
    --allow-macos)    ALLOW_MACOS=1; shift ;;
    -h|--help)        usage; exit 0 ;;
    --)               shift; break ;;
    *)                die "unknown option $1 (try --help)" ;;
  esac
done
while [ $# -gt 0 ]; do
  case "$1" in
    --pair)       PAIR_CODE="${2:-}"; shift 2 ;;
    --api-url)    API_URL="${2:-}"; shift 2 ;;
    --tier)       TIER="${2:-}"; shift 2 ;;
    --access)     ACCESS="${2:-}"; TIER="compute"; shift 2 ;;
    --workspace)  WORKSPACE="${2:-}"; shift 2 ;;
    --no-connect) CONNECT_MODE="none"; shift ;;
    -h|--help)    usage; exit 0 ;;
    *)            die "unknown connect option $1 (try --help)" ;;
  esac
done

# ------------------------------------------------------------------ platform gate
OS="$(uname -s)"
ARCH="$(uname -m)"
case "$OS" in
  Linux|Darwin) ;;
  MINGW*|MSYS*|CYGWIN*)
    die "this is Windows: use the PowerShell installer instead (install.ps1, minted by the workspace's Windows connect command)" ;;
  *) die "unsupported operating system: $OS" ;;
esac

case "$ARCH" in
  x86_64|amd64)  UV_ARCH="x86_64" ;;
  aarch64|arm64) UV_ARCH="aarch64" ;;
  *) die "unsupported architecture: $ARCH" ;;
esac

if [ "$OS" = "Darwin" ]; then
  UV_TARGET="${UV_ARCH}-apple-darwin"
elif [ -n "$(ldd --version 2>&1 | grep -i musl || true)" ]; then
  UV_TARGET="${UV_ARCH}-unknown-linux-musl"
else
  UV_TARGET="${UV_ARCH}-unknown-linux-gnu"
fi

command -v curl >/dev/null 2>&1 || command -v wget >/dev/null 2>&1 \
  || die "either curl or wget is required"
command -v tar >/dev/null 2>&1 || die "tar is required"

fetch() { # fetch <url> <destination>
  if command -v curl >/dev/null 2>&1; then
    curl -fsSL --retry 3 --connect-timeout 15 -o "$2" "$1"
  else
    wget -q -T 15 -t 3 -O "$2" "$1"
  fi
}

sha256_of() {
  if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | awk '{print $1}'
  elif command -v shasum >/dev/null 2>&1;  then shasum -a 256 "$1" | awk '{print $1}'
  elif command -v openssl >/dev/null 2>&1; then openssl dgst -sha256 "$1" | awk '{print $NF}'
  else die "no sha256 tool found (need sha256sum, shasum, or openssl)"
  fi
}

verify_sha256() { # verify_sha256 <file> <expected> <label>
  local actual; actual="$(sha256_of "$1")"
  [ "$actual" = "$(printf '%s' "$2" | tr 'A-Z' 'a-z')" ] \
    || die "$3 checksum mismatch (expected $2, got $actual)"
  log "verified $3 sha256 $actual"
}

WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/meshia-install.XXXXXX")"

# ----------------------------------------------------------------- private layout
step "Preparing $MESHIA_HOME"
mkdir -p "$MESHIA_HOME" "$TOOL_DIR" "$BIN_DIR"
chmod 700 "$MESHIA_HOME"
log "state directory ready (0700, no sudo used)"

# ------------------------------------------------------------------------- uv
UV_BIN=""
if [ -x "$TOOL_DIR/uv" ] && "$TOOL_DIR/uv" --version 2>/dev/null | grep -q "$UV_VERSION"; then
  UV_BIN="$TOOL_DIR/uv"
  step "uv $UV_VERSION already installed"
elif command -v uv >/dev/null 2>&1 && uv --version 2>/dev/null | grep -q "$UV_VERSION"; then
  UV_BIN="$(command -v uv)"
  step "using system uv $UV_VERSION"
else
  step "Installing uv $UV_VERSION ($UV_TARGET)"
  ASSET="uv-${UV_TARGET}.tar.gz"
  BASE="https://github.com/astral-sh/uv/releases/download/${UV_VERSION}"
  fetch "${BASE}/${ASSET}" "$WORK_DIR/$ASSET" || die "could not download $ASSET"
  if [ -n "${MESHIA_UV_SHA256:-}" ]; then
    # An out-of-band pin always wins over the release-hosted checksum.
    verify_sha256 "$WORK_DIR/$ASSET" "$MESHIA_UV_SHA256" "uv"
  elif fetch "${BASE}/${ASSET}.sha256" "$WORK_DIR/$ASSET.sha256"; then
    verify_sha256 "$WORK_DIR/$ASSET" "$(awk '{print $1}' "$WORK_DIR/$ASSET.sha256")" "uv"
  else
    die "no uv checksum available; set MESHIA_UV_SHA256 to pin one"
  fi
  tar -xzf "$WORK_DIR/$ASSET" -C "$WORK_DIR"
  install -m 0755 "$WORK_DIR/uv-${UV_TARGET}/uv" "$TOOL_DIR/uv"
  [ -f "$WORK_DIR/uv-${UV_TARGET}/uvx" ] && install -m 0755 "$WORK_DIR/uv-${UV_TARGET}/uvx" "$TOOL_DIR/uvx" || true
  UV_BIN="$TOOL_DIR/uv"
  log "installed $("$UV_BIN" --version)"
fi

export UV_CACHE_DIR="${UV_CACHE_DIR:-$MESHIA_HOME/cache/uv}"
export UV_PYTHON_INSTALL_DIR="${UV_PYTHON_INSTALL_DIR:-$MESHIA_HOME/cache/python}"

# --------------------------------------------------------------------- interpreter
step "Provisioning Python $PYTHON_VERSION"
"$UV_BIN" python install "$PYTHON_VERSION" >/dev/null 2>&1 \
  || log "using an already-available Python $PYTHON_VERSION"

step "Creating the runtime at $RUNTIME_DIR"
"$UV_BIN" venv --python "$PYTHON_VERSION" --allow-existing "$RUNTIME_DIR" >/dev/null
VENV_PYTHON="$RUNTIME_DIR/bin/python"
[ -x "$VENV_PYTHON" ] || die "the runtime venv was not created at $RUNTIME_DIR"

# ------------------------------------------------------------------------ package
step "Installing meshia-node"
if [ -n "$FROM_DIR" ]; then
  [ -f "$FROM_DIR/pyproject.toml" ] || die "--from-dir $FROM_DIR has no pyproject.toml"
  TARGET="$FROM_DIR"
  log "development install from $FROM_DIR"
else
  if [ -z "$PACKAGE_URL" ]; then
    BASE_ORIGIN="${API_URL:-https://meshia.io}"
    BASE_ORIGIN="${BASE_ORIGIN%/}"
    MANIFEST_URL="${BASE_ORIGIN}/meshia-node/release.json"
    MANIFEST_FILE="$WORK_DIR/release.json"
    log "fetching release manifest from $MANIFEST_URL"
    fetch "$MANIFEST_URL" "$MANIFEST_FILE" || die "could not download release manifest from $MANIFEST_URL"

    PKG_NAME=""
    PKG_SHA=""
    if [ -x "$VENV_PYTHON" ]; then
      PKG_NAME="$("$VENV_PYTHON" -c "import json; print(json.load(open('$MANIFEST_FILE'))['package'])" 2>/dev/null || true)"
      PKG_SHA="$("$VENV_PYTHON" -c "import json; print(json.load(open('$MANIFEST_FILE'))['sha256'])" 2>/dev/null || true)"
    fi
    if [ -z "$PKG_NAME" ] || [ -z "$PKG_SHA" ]; then
      PKG_NAME="$(grep -o '"package": *"[^"]*"' "$MANIFEST_FILE" | head -1 | cut -d'"' -f4)"
      PKG_SHA="$(grep -o '"sha256": *"[^"]*"' "$MANIFEST_FILE" | head -1 | cut -d'"' -f4)"
    fi
    [ -n "$PKG_NAME" ] && [ -n "$PKG_SHA" ] || die "could not parse package information from $MANIFEST_URL"
    PACKAGE_URL="${BASE_ORIGIN}/meshia-node/${PKG_NAME}"
    PACKAGE_SHA256="$PKG_SHA"
  fi

  ARCHIVE="$WORK_DIR/$(basename "${PACKAGE_URL%%\?*}")"
  fetch "$PACKAGE_URL" "$ARCHIVE" || die "could not download $PACKAGE_URL"
  if [ -n "$PACKAGE_SHA256" ]; then
    verify_sha256 "$ARCHIVE" "$PACKAGE_SHA256" "meshia-node package"
  elif [ "$INSECURE_DEV" = "1" ]; then
    log "WARNING: installing an unverified package because --insecure-dev was set"
  else
    die "--package-sha256 (or \$MESHIA_NODE_PACKAGE_SHA256) is required; use --insecure-dev to override"
  fi
  TARGET="$ARCHIVE"
fi
"$UV_BIN" pip install --python "$VENV_PYTHON" --quiet --upgrade "$TARGET"

# ------------------------------------------------------------------------ symlink
ln -sfn "$RUNTIME_DIR/bin/meshia-node" "$BIN_DIR/meshia-node"
log "linked $BIN_DIR/meshia-node -> $RUNTIME_DIR/bin/meshia-node"
"$BIN_DIR/meshia-node" --version >/dev/null || die "the installed CLI is not runnable"

case ":$PATH:" in
  *":$BIN_DIR:"*) ;;
  *)
    step "Add $BIN_DIR to your PATH"
    log "echo 'export PATH=\"$BIN_DIR:\$PATH\"' >> ~/.profile && export PATH=\"$BIN_DIR:\$PATH\""
    ;;
esac

step "Installed $("$BIN_DIR/meshia-node" --version)"

# ------------------------------------------------------------------------ connect
if [ -n "$PAIR_CODE" ] && [ "$CONNECT_MODE" != "none" ]; then
  [ -n "$API_URL" ] || API_URL="https://meshia.io"
  step "Connecting this machine to $API_URL"
  ACCESS_VAL="${ACCESS:-limited}"
  TIER_VAL="${TIER:-compute}"
  [ -n "$ACCESS" ] && TIER_VAL="compute"
  set -- connect "$PAIR_CODE" --api-url "$API_URL" --tier "$TIER_VAL" --access "$ACCESS_VAL"
  [ -n "$WORKSPACE" ] && set -- "$@" --workspace "$WORKSPACE"
  exec "$BIN_DIR/meshia-node" "$@"
fi

cat >&2 <<EOF

Next steps:
  $BIN_DIR/meshia-node doctor
  $BIN_DIR/meshia-node connect <mesh_pairing_code> --api-url <origin>
EOF
