#!/usr/bin/env sh
# Ghostbox installer + PATH shim bootstrap
# Usage: curl -fsSL https://www.ghost.charity/install.sh | bash
set -eu

REPO="${GHOSTBOX_RELEASE_REPO:-DO-SAY-GO/ghostbox-releases}"
RELEASE_TAG="${GHOSTBOX_RELEASE_TAG:-}"
ARTIFACT_LOCATION="${GHOSTBOX_ARTIFACT:-}"
CHECKSUMS_LOCATION="${GHOSTBOX_CHECKSUMS:-}"
STATE_DIR="${GHOSTBOX_STATE_DIR:-$HOME/.ghostbox}"
WRAPPER_PATH="$STATE_DIR/ghost-wrapper.sh"
RUNTIME_DIR="$STATE_DIR/runtime"
PAYLOAD="$RUNTIME_DIR/ghost"
CHECK_FILE="$STATE_DIR/.last_update_check"
CADENCE="${GHOSTBOX_UPDATE_CADENCE:-3600}"

log() {
  printf '%s\n' "$*"
}

die() {
  printf '%s\n' "$*" >&2
  exit 1
}

path_contains_dir() {
  case ":$PATH:" in
    *":$1:"*) return 0 ;;
    *) return 1 ;;
  esac
}

is_under_home() {
  case "$1" in
    "$HOME"/*) return 0 ;;
    *) return 1 ;;
  esac
}

ensure_dir() {
  [ -d "$1" ] || mkdir -p "$1"
}

dir_is_usable_without_sudo() {
  dir="$1"
  probe="$dir"

  while [ ! -d "$probe" ]; do
    next_probe=$(dirname "$probe")
    [ "$next_probe" != "$probe" ] || break
    probe="$next_probe"
  done

  [ -d "$probe" ] && [ -w "$probe" ]
}

command_dir() {
  existing=$(command -v ghost 2>/dev/null || true)
  if [ -n "$existing" ]; then
    dirname "$existing"
  fi
}

choose_global_dir() {
  existing_dir=$(command_dir)
  if [ -n "$existing_dir" ] && ! is_under_home "$existing_dir" && path_contains_dir "$existing_dir"; then
    printf '%s\n' "$existing_dir"
    return 0
  fi

  for dir in /usr/local/bin /opt/homebrew/bin; do
    if path_contains_dir "$dir"; then
      printf '%s\n' "$dir"
      return 0
    fi
  done

  return 1
}

choose_user_dir() {
  existing_dir=$(command_dir)
  if [ -n "$existing_dir" ] && is_under_home "$existing_dir" && path_contains_dir "$existing_dir" && dir_is_usable_without_sudo "$existing_dir"; then
    printf '%s\n' "$existing_dir"
    return 0
  fi

  for dir in "$HOME/.local/bin" "$HOME/bin" "$STATE_DIR/bin"; do
    if path_contains_dir "$dir" && dir_is_usable_without_sudo "$dir"; then
      printf '%s\n' "$dir"
      return 0
    fi
  done

  old_ifs=$IFS
  IFS=:
  for dir in $PATH; do
    IFS=$old_ifs
    [ -n "$dir" ] || continue
    if is_under_home "$dir" && dir_is_usable_without_sudo "$dir"; then
      printf '%s\n' "$dir"
      return 0
    fi
    IFS=:
  done
  IFS=$old_ifs

  return 1
}

can_use_passwordless_sudo() {
  command -v sudo >/dev/null 2>&1 && sudo -n true >/dev/null 2>&1
}

prompt_yes_no() {
  prompt="$1"
  default_answer="${2:-Y}"

  if [ ! -r /dev/tty ]; then
    return 1
  fi

  if [ "$default_answer" = "Y" ]; then
    suffix='[Y/n]'
  else
    suffix='[y/N]'
  fi

  while true; do
    printf '%s %s ' "$prompt" "$suffix" > /dev/tty
    if ! IFS= read -r reply < /dev/tty; then
      return 1
    fi

    case "$reply" in
      "") [ "$default_answer" = "Y" ] && return 0 || return 1 ;;
      y|Y|yes|YES) return 0 ;;
      n|N|no|NO) return 1 ;;
    esac
  done
}

write_user_wrapper() {
  ensure_dir "$STATE_DIR"
  ensure_dir "$RUNTIME_DIR"

  cat > "$WRAPPER_PATH" <<'EOF'
#!/usr/bin/env sh
set -eu

REPO="${GHOSTBOX_RELEASE_REPO:-DO-SAY-GO/ghostbox-releases}"
RELEASE_TAG="${GHOSTBOX_RELEASE_TAG:-}"
ARTIFACT_LOCATION="${GHOSTBOX_ARTIFACT:-}"
CHECKSUMS_LOCATION="${GHOSTBOX_CHECKSUMS:-}"
STATE_DIR="${GHOSTBOX_STATE_DIR:-$HOME/.ghostbox}"
WRAPPER_PATH="$STATE_DIR/ghost-wrapper.sh"
RUNTIME_DIR="$STATE_DIR/runtime"
PAYLOAD="$RUNTIME_DIR/ghost"
CHECK_FILE="$STATE_DIR/.last_update_check"
CADENCE="${GHOSTBOX_UPDATE_CADENCE:-3600}"

log() {
  printf '%s\n' "$*"
}

die() {
  printf '%s\n' "$*" >&2
  exit 1
}

should_update() {
  if [ -n "$ARTIFACT_LOCATION" ]; then
    return 0
  fi

  if [ ! -x "$PAYLOAD" ]; then
    return 0
  fi

  if [ ! -f "$CHECK_FILE" ]; then
    return 0
  fi

  last_check=$(cat "$CHECK_FILE" 2>/dev/null || printf '0')
  case "$last_check" in
    ''|*[!0-9]*) return 0 ;;
  esac

  now=$(date +%s)
  [ $((now - last_check)) -ge "$CADENCE" ]
}

resolve_asset() {
  case "$(uname -s)" in
    Darwin) os="macos"; ext="zip" ;;
    Linux) os="linux"; ext="tar.gz" ;;
    *) die "unsupported OS: $(uname -s)" ;;
  esac

  case "$(uname -m)" in
    x86_64|amd64) arch="x64" ;;
    arm64|aarch64) arch="arm64" ;;
    *) die "unsupported architecture: $(uname -m)" ;;
  esac

  printf 'ghost-%s-%s.%s\n' "$os" "$arch" "$ext"
}

fetch_location() {
  location="$1"
  dest="$2"
  case "$location" in
    http://*|https://*)
      curl -fsSL "$location" -o "$dest"
      ;;
    *)
      cp "$location" "$dest"
      ;;
  esac
}

download_and_install() {
  asset=$(resolve_asset)
  release_path="latest/download"
  if [ -n "$RELEASE_TAG" ]; then
    release_path="download/${RELEASE_TAG}"
  fi
  if [ -z "$ARTIFACT_LOCATION" ] && [ -z "$RELEASE_TAG" ]; then
    artifact_location="https://www.ghost.charity/downloads/${asset}"
  else
    artifact_location="${ARTIFACT_LOCATION:-https://github.com/${REPO}/releases/${release_path}/${asset}}"
  fi

  if [ -z "$CHECKSUMS_LOCATION" ] && [ -z "$RELEASE_TAG" ]; then
    checksums_location="https://www.ghost.charity/downloads/SHA256SUMS.txt"
  else
    checksums_location="${CHECKSUMS_LOCATION:-https://github.com/${REPO}/releases/${release_path}/SHA256SUMS.txt}"
  fi

  tmp=$(mktemp -d)
  trap 'rm -rf "$tmp"' EXIT INT HUP TERM

  log "Checking for Ghostbox updates..."
  fetch_location "$artifact_location" "$tmp/$asset" || die "Could not fetch $asset from $artifact_location."
  fetch_location "$checksums_location" "$tmp/SHA256SUMS.txt" || die "Could not fetch SHA256SUMS.txt from $checksums_location."

  expected=$(grep "  ${asset}$" "$tmp/SHA256SUMS.txt" | awk '{print $1}' || true)
  [ -n "$expected" ] || die "Missing checksum entry for $asset"

  if command -v shasum >/dev/null 2>&1; then
    actual=$(shasum -a 256 "$tmp/$asset" | awk '{print $1}')
  elif command -v sha256sum >/dev/null 2>&1; then
    actual=$(sha256sum "$tmp/$asset" | awk '{print $1}')
  else
    die "Need shasum or sha256sum to verify $asset"
  fi

  [ "$expected" = "$actual" ] || die "Checksum verification failed for $asset"

  case "$asset" in
    *.tar.gz)
      tar -xzf "$tmp/$asset" -C "$tmp"
      ;;
    *.zip)
      command -v unzip >/dev/null 2>&1 || die "unzip is required to extract $asset"
      unzip -q "$tmp/$asset" -d "$tmp"
      ;;
  esac

  source_path=$(find "$tmp" -type f -name ghost -print | head -n 1)
  [ -n "$source_path" ] || die "Could not locate ghost in $asset"

  ensure_dir "$RUNTIME_DIR"
  install -m 755 "$source_path" "$PAYLOAD"
  date +%s > "$CHECK_FILE"
}

ensure_dir() {
  [ -d "$1" ] || mkdir -p "$1"
}

if should_update; then
  download_and_install
fi

if [ "${GHOSTBOX_INSTALL_ONLY:-0}" = "1" ]; then
  log "Ghostbox payload is ready at $PAYLOAD"
  exit 0
fi

[ -x "$PAYLOAD" ] || die "Ghostbox is not installed. Re-run the installer."
exec "$PAYLOAD" "$@"
EOF

  chmod 755 "$WRAPPER_PATH"
}

write_shim() {
  shim_path="$1"

  cat > "$shim_path" <<'EOF'
#!/usr/bin/env sh
set -eu
STATE_DIR="${GHOSTBOX_STATE_DIR:-$HOME/.ghostbox}"
exec "$STATE_DIR/ghost-wrapper.sh" "$@"
EOF

  chmod 755 "$shim_path"
}

install_shim() {
  source_path="$1"
  target_dir="$2"
  target_path="$target_dir/ghost"
  use_sudo="$3"

  if [ "$use_sudo" = "1" ]; then
    sudo mkdir -p "$target_dir"
    sudo install -m 755 "$source_path" "$target_path"
  else
    ensure_dir "$target_dir"
    install -m 755 "$source_path" "$target_path"
  fi
}

main() {
  ensure_dir "$STATE_DIR"
  write_user_wrapper

  global_dir=$(choose_global_dir || true)
  user_dir=$(choose_user_dir || true)
  target_dir=""
  install_scope=""
  use_sudo="0"

  if [ -n "$global_dir" ]; then
    if dir_is_usable_without_sudo "$global_dir"; then
      target_dir="$global_dir"
      install_scope="global"
    elif can_use_passwordless_sudo; then
      target_dir="$global_dir"
      install_scope="global"
      use_sudo="1"
    elif prompt_yes_no "Install Ghostbox globally to $global_dir using sudo?" Y; then
      target_dir="$global_dir"
      install_scope="global"
      use_sudo="1"
    fi
  fi

  if [ -z "$target_dir" ] && [ -n "$user_dir" ]; then
    target_dir="$user_dir"
    install_scope="user"
  fi

  [ -n "$target_dir" ] || die "No suitable install directory found on PATH. Grant global install access or add a writable user bin directory such as ~/.local/bin to PATH, then rerun the installer."

  tmp=$(mktemp -d)
  trap 'rm -rf "$tmp"' EXIT INT HUP TERM
  shim_path="$tmp/ghost"
  write_shim "$shim_path"

  if ! install_shim "$shim_path" "$target_dir" "$use_sudo"; then
    if [ -n "$user_dir" ] && [ "$target_dir" != "$user_dir" ]; then
      log "Global install failed; falling back to $user_dir"
      target_dir="$user_dir"
      install_scope="user"
      use_sudo="0"
      install_shim "$shim_path" "$target_dir" "$use_sudo"
    else
      die "Could not install Ghostbox to $target_dir"
    fi
  fi

  GHOSTBOX_INSTALL_ONLY=1 "$WRAPPER_PATH"

  log "Installed Ghostbox command to $target_dir/ghost ($install_scope scope)"
  log "Ghostbox wrapper: $WRAPPER_PATH"
  log "Ghostbox payload: $PAYLOAD"
}

main "$@"
