#!/usr/bin/env bash
# weave — live IT control plane CLI
# One-liner installer for macOS / Linux.
#
#   curl -fsSL https://weavewhatever.com/install.sh | bash
#
# Environment overrides:
#   WEAVE_REPO   Git URL to install from (default: https://github.com/andy-broyles/weavewhatever.git)
#                Must match the GitHub repo pattern below — arbitrary URLs are rejected.
#   WEAVE_REF       Branch/tag/sha to install. Overrides WEAVE_VERSION when set.
#   WEAVE_VERSION   Release to install (e.g. 0.2.0 → tag v0.2.0). When unset and
#                   WEAVE_REF is unset, uses the latest v* tag on WEAVE_REPO, else main.
#   WEAVE_QUIET  Set to 1 to suppress the banner
#   UV_VERSION   Pin Astral's uv to a specific version (default below). Best-effort:
#                Astral's installer reads UV_VERSION for older releases; if the
#                current upstream installer ignores it, uv will fall back to its
#                "install latest" behavior. See TODO(security) below.

set -euo pipefail

DEFAULT_WEAVE_REPO="https://github.com/andy-broyles/weavewhatever.git"
DEFAULT_WEAVE_REF="main"
# TODO(security): pin uv to a known-good version. Astral's installer historically
# honored UV_VERSION, but the canonical way to lock the version is to download
# a specific release artifact (https://github.com/astral-sh/uv/releases) and
# verify its SHA256 before extracting. Until that's wired up, we set a default
# UV_VERSION which the upstream installer *may* honor.
DEFAULT_UV_VERSION="0.5.11"

WEAVE_REPO="${WEAVE_REPO:-$DEFAULT_WEAVE_REPO}"
UV_VERSION="${UV_VERSION:-$DEFAULT_UV_VERSION}"
export UV_VERSION

resolve_weave_ref() {
  if [ -n "${WEAVE_REF:-}" ]; then
    printf '%s' "$WEAVE_REF"
    return
  fi
  if [ -n "${WEAVE_VERSION:-}" ]; then
    local v="${WEAVE_VERSION#v}"
    printf 'v%s' "$v"
    return
  fi
  if command -v git >/dev/null 2>&1; then
    local latest
    latest="$(
      git ls-remote --tags --refs "$WEAVE_REPO" 2>/dev/null \
        | awk -F/ '{print $3}' \
        | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' \
        | sort -t. -k1.2n -k2n -k3n \
        | tail -1
    )" || true
    if [ -n "$latest" ]; then
      printf '%s' "$latest"
      return
    fi
  fi
  printf '%s' "$DEFAULT_WEAVE_REF"
}

WEAVE_REF="$(resolve_weave_ref)"

# --- input validation -----------------------------------------------------
# We refuse to install from arbitrary URLs or arbitrary refs. This is a
# defense-in-depth measure against `curl … | WEAVE_REPO=evil-url bash`
# phishing attempts (especially from copy-pasted instructions). The allow-
# list matches `github.com/<user>/<repo>(.git)?` HTTPS or SSH forms.

WEAVE_REPO_RE='^(https://github\.com/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(\.git)?|git@github\.com:[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(\.git)?)$'
WEAVE_REF_RE='^[A-Za-z0-9_./-]+$'

if ! [[ "$WEAVE_REPO" =~ $WEAVE_REPO_RE ]]; then
  printf '\nERROR: WEAVE_REPO=%s does not look like a GitHub repo URL.\n' "$WEAVE_REPO" >&2
  printf 'Refusing to install from arbitrary sources.\n' >&2
  printf 'Expected: https://github.com/<owner>/<repo>(.git) or git@github.com:<owner>/<repo>(.git)\n\n' >&2
  exit 2
fi

if ! [[ "$WEAVE_REF" =~ $WEAVE_REF_RE ]]; then
  printf '\nERROR: WEAVE_REF=%s contains characters outside the refspec allow-list.\n' "$WEAVE_REF" >&2
  printf 'Allowed: letters, digits, dot, underscore, slash, hyphen.\n\n' >&2
  exit 2
fi

# --- pretty printing ------------------------------------------------------

if [ -t 1 ] && command -v tput >/dev/null 2>&1 && [ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]; then
  C_BOLD=$(tput bold); C_DIM=$(tput dim); C_RESET=$(tput sgr0)
  C_RED=$(tput setaf 1); C_GREEN=$(tput setaf 2); C_AMBER=$(tput setaf 3)
  C_BLUE=$(tput setaf 4); C_TEAL=$(tput setaf 6)
else
  C_BOLD=""; C_DIM=""; C_RESET=""; C_RED=""; C_GREEN=""; C_AMBER=""; C_BLUE=""; C_TEAL=""
fi

info()  { printf '%s• %s%s\n' "$C_BLUE"  "$*" "$C_RESET"; }
ok()    { printf '%s✓ %s%s\n' "$C_GREEN" "$*" "$C_RESET"; }
warn()  { printf '%s⚠ %s%s\n' "$C_AMBER" "$*" "$C_RESET"; }
die()   { printf '%s✗ %s%s\n' "$C_RED"   "$*" "$C_RESET" >&2; exit 1; }

banner() {
  [ "${WEAVE_QUIET:-0}" = "1" ] && return 0
  printf '\n'
  printf '  %s%sweave%s — live IT control plane\n' "$C_BOLD" "$C_TEAL" "$C_RESET"
  printf '  %shttps://weavewhatever.com%s\n\n' "$C_DIM" "$C_RESET"
}

# Always print what we're about to install so a piped invocation is auditable
# in the terminal scrollback even if the rest of the output is colored.
print_resolved_targets() {
  printf '  %sInstalling from:%s %s\n' "$C_BOLD" "$C_RESET" "$WEAVE_REPO"
  printf '  %sRef:%s            %s\n' "$C_BOLD" "$C_RESET" "$WEAVE_REF"
  printf '  %sPinned uv:%s      %s\n' "$C_BOLD" "$C_RESET" "$UV_VERSION"
  printf '\n'
}

# --- environment checks ---------------------------------------------------

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

detect_os() {
  case "$(uname -s)" in
    Linux*)   OS=linux ;;
    Darwin*)  OS=macos ;;
    *)        die "Unsupported OS: $(uname -s). Use install.ps1 on Windows." ;;
  esac
}

detect_arch() {
  case "$(uname -m)" in
    x86_64|amd64)  ARCH=x86_64 ;;
    arm64|aarch64) ARCH=arm64 ;;
    *)             ARCH="$(uname -m)" ;;
  esac
}

ensure_python() {
  if need_cmd python3; then
    PY=python3
  elif need_cmd python; then
    PY=python
  else
    die "Python 3.11+ is required but no python interpreter was found. Install Python 3.11 or newer and re-run."
  fi
  if ! "$PY" -c 'import sys; sys.exit(0 if sys.version_info >= (3, 11) else 1)'; then
    die "Python 3.11+ required (found $($PY --version 2>&1))."
  fi
  ok "Python $($PY -c 'import sys;print("%d.%d.%d"%sys.version_info[:3])') ready"
}

ensure_curl() {
  need_cmd curl || die "curl is required but not installed."
}

ensure_uv() {
  if need_cmd uv; then
    ok "uv $(uv --version | awk '{print $2}') already installed"
    return
  fi
  info "Installing uv ${UV_VERSION} (fast Python package + tool manager)…"
  # UV_VERSION is exported above; the upstream installer reads it for
  # versions that support pinning. See TODO(security) at the top.
  curl -fsSL https://astral.sh/uv/install.sh | sh
  for d in "$HOME/.local/bin" "$HOME/.cargo/bin"; do
    [ -d "$d" ] && case ":$PATH:" in *":$d:"*) ;; *) PATH="$d:$PATH";; esac
  done
  export PATH
  need_cmd uv || die "uv installation failed. See https://docs.astral.sh/uv/ for help."
  ok "uv installed"
}

install_weave() {
  info "Installing weave from ${WEAVE_REPO}@${WEAVE_REF}…"
  uv tool install --force --python ">=3.11" "git+${WEAVE_REPO}@${WEAVE_REF}"
  ok "weave installed"
}

post_install() {
  printf '\n'
  printf '  %s%s✓ weave is ready.%s\n' "$C_BOLD" "$C_GREEN" "$C_RESET"
  printf '\n'
  printf '  Try it:\n'
  printf '    %sweave setup%s  %s(weave init is the same)%s\n' "$C_DIM" "$C_RESET" "$C_DIM" "$C_RESET"
  printf '    %sweave --help%s\n' "$C_DIM" "$C_RESET"
  printf '\n'
  printf '  Developing locally? From the repo root:\n'
  printf '    %spip install -e .%s\n' "$C_DIM" "$C_RESET"
  printf '  Ensure ~/.local/bin (or your venv bin) is on PATH.\n'
  printf '\n'
  printf '  Docs:  %shttps://weavewhatever.com/getting-started.html%s\n' "$C_BLUE" "$C_RESET"
  printf '  Repo:  %s%s%s\n' "$C_BLUE" "$WEAVE_REPO" "$C_RESET"
  printf '\n'
  if need_cmd weave; then
    printf '  %sInstalled:%s %s\n' "$C_BOLD" "$C_RESET" "$(weave --version 2>/dev/null || weave version 2>/dev/null || echo weave)"
  else
    warn "weave was installed, but isn't on PATH in this shell yet."
    printf '    Open a new terminal, or run:  %sexport PATH="$HOME/.local/bin:$PATH"%s\n\n' "$C_DIM" "$C_RESET"
  fi
}


# --- main -----------------------------------------------------------------

banner
print_resolved_targets
detect_os
detect_arch
info "Detected ${OS}/${ARCH}"
ensure_curl
ensure_python
ensure_uv
install_weave
post_install
