#!/usr/bin/env bash
# Poll PyPI for a release, install it in isolation, and smoke-test its commands.
set -euo pipefail

readonly package='blumkin'
readonly poll_interval_s=15
readonly poll_timeout_s=600
temp_root=''

die() {
  printf 'verify-pypi-release: %s\n' "$*" >&2
  exit 1
}

# Retry a command with linear backoff until it succeeds or the time budget runs
# out. `release_is_available()` already polls PyPI/the simple index with backoff;
# the install path needs the same resilience because `uv` / `pip` resolve through
# their own index cache and a different CDN edge, so they can still see a stale
# "no such version" for a beat after the poll passed (issue #199). Callers pass a
# cache-busting flag (uv `--refresh`, `PIP_NO_CACHE_DIR=1`) so a cached negative
# lookup does not stick across attempts.
retry_with_backoff() {
  local -r budget_s="$1"
  local -r label="$2"
  shift 2

  local -r max_attempts=5
  local attempt
  local -r deadline=$((SECONDS + budget_s))
  for attempt in $(seq 1 "$max_attempts"); do
    if "$@"; then
      return 0
    fi
    # No message or sleep after the last attempt - the next line is `die`.
    ((attempt < max_attempts)) || break
    ((SECONDS < deadline)) || break
    printf 'verify-pypi-release: %s attempt %d failed, retrying in %ds\n' \
      "$label" "$attempt" "$((attempt * 15))" >&2
    sleep "$((attempt * 15))"
  done
  die "could not ${label} after retries (PyPI/CDN propagation skew?)"
}

release_is_available() {
  local -r version="$1"
  local -r json_file="$2"
  local -r simple_file="$3"

  timeout 20 curl \
    --connect-timeout 5 \
    --fail \
    --location \
    --max-time 15 \
    --output "$json_file" \
    --silent \
    --show-error \
    "https://pypi.org/pypi/${package}/${version}/json" || return 1
  timeout 20 curl \
    --connect-timeout 5 \
    --fail \
    --location \
    --max-time 15 \
    --output "$simple_file" \
    --silent \
    --show-error \
    "https://pypi.org/simple/${package}/" || return 1

  jq --arg version "$version" \
    --exit-status \
    '.info.version == $version and (.urls | length > 0)' \
    "$json_file" >/dev/null &&
    grep --fixed-strings --quiet "${package}-${version}" "$simple_file"
}

main() {
  if [[ $# -ne 1 || -z "$1" ]]; then
    die 'usage: scripts/verify-pypi-release VERSION'
  fi

  local -r version="$1"
  local json_file simple_file deadline venv_dir bin_dir version_output
  local available=false

  command -v curl >/dev/null 2>&1 || die 'curl is required'
  command -v jq >/dev/null 2>&1 || die 'jq is required'
  command -v timeout >/dev/null 2>&1 || die 'timeout is required'
  command -v uv >/dev/null 2>&1 || die 'uv is required'

  temp_root="$(mktemp -d)"
  readonly temp_root
  json_file="${temp_root}/release.json"
  readonly json_file
  simple_file="${temp_root}/simple.html"
  readonly simple_file
  venv_dir="${temp_root}/venv"
  readonly venv_dir
  trap 'rm -rf "$temp_root"' EXIT

  deadline=$((SECONDS + poll_timeout_s))
  readonly deadline
  while ((SECONDS < deadline)); do
    if release_is_available "$version" "$json_file" "$simple_file"; then
      printf 'verify-pypi-release: PyPI %s/%s is available\n' "$package" "$version"
      available=true
      break
    fi
    printf 'verify-pypi-release: waiting for PyPI %s/%s\n' "$package" "$version" >&2
    sleep "$poll_interval_s"
  done

  if [[ "$available" != true ]]; then
    die "timed out after ${poll_timeout_s}s waiting for PyPI ${package}/${version}"
  fi

  timeout 120 uv venv --python 3.14 "$venv_dir"
  retry_with_backoff 300 "install ${package}==${version}" \
    timeout 180 uv pip install --refresh \
    --python "${venv_dir}/bin/python" \
    "${package}==${version}"
  bin_dir="${venv_dir}/bin"
  readonly bin_dir

  # Strip any commit override so `--version` must resolve from the wheel's
  # embedded stamp (the installed dist has no .git). A broken embed then prints
  # "(unknown)" and is caught here rather than by a real pipx user.
  version_output="$(
    timeout 30 env -u BLUMKIN_GIT_SHA -u GITHUB_SHA "${bin_dir}/blumkin" --version
  )"
  readonly version_output
  grep --fixed-strings --quiet "$version" <<<"$version_output" ||
    die "blumkin --version did not report ${version}: ${version_output}"
  if grep --fixed-strings --quiet '(unknown)' <<<"$version_output"; then
    die "blumkin --version reported unknown commit: ${version_output}"
  fi
  timeout 30 "${bin_dir}/blumkin" --help >/dev/null

  printf 'verify-pypi-release: installed commands passed for %s\n' "$version"

  verify_pipx_install "$version" "${venv_dir}/bin/python"
}

# pipx has its own venv layout and console-script shims (PIPX_HOME / PIPX_BIN_DIR),
# none of which the uv-pip-install check above exercises - the actual acceptance for
# #54 (`pipx install blumkin`) was only ever checked by hand, on a dev box whose
# ambient `pipx` turned out to be a broken wrapper (issue #142). Install a known-good
# pipx via `uv tool install` under a scratch $HOME instead of trusting (or mutating)
# whatever `pipx` - and whatever it thinks is already installed - lives on the real
# machine's PATH / ~/.local/share/pipx.
verify_pipx_install() {
  local -r version="$1"
  # Pin pipx's own venv to the same 3.14 the uv-pip block above already proved -
  # $HOME is overridden below so uv/pipx can no longer see the managed pythons
  # under the real HOME and would otherwise fall back to the runner's ambient
  # python3, which can be older than blumkin's requires-python and fail the
  # install outright (mergestorm-vortex).
  local -r python_bin="$2"
  local pipx_uv_home pipx_bin pipx_home pipx_bin_dir version_output

  pipx_uv_home="${temp_root}/pipx-uv-home"
  mkdir -p "$pipx_uv_home"
  timeout 60 env HOME="$pipx_uv_home" uv tool install pipx >/dev/null
  pipx_bin="$(env HOME="$pipx_uv_home" uv tool dir --bin)/pipx"
  [[ -x "$pipx_bin" ]] || die "pipx not found at ${pipx_bin} after uv tool install"

  pipx_home="${temp_root}/pipx-home"
  pipx_bin_dir="${temp_root}/pipx-bin"
  mkdir -p "$pipx_home" "$pipx_bin_dir"
  retry_with_backoff 300 "pipx install ${package}==${version}" \
    timeout 180 env HOME="$pipx_uv_home" PIPX_HOME="$pipx_home" PIPX_BIN_DIR="$pipx_bin_dir" \
    PIP_NO_CACHE_DIR=1 \
    "$pipx_bin" install --python "$python_bin" "${package}==${version}"

  version_output="$(
    timeout 30 env -u BLUMKIN_GIT_SHA -u GITHUB_SHA "${pipx_bin_dir}/blumkin" --version
  )"
  readonly version_output
  grep --fixed-strings --quiet "$version" <<<"$version_output" ||
    die "pipx-installed blumkin --version did not report ${version}: ${version_output}"
  if grep --fixed-strings --quiet '(unknown)' <<<"$version_output"; then
    die "pipx-installed blumkin --version reported unknown commit: ${version_output}"
  fi
  timeout 30 "${pipx_bin_dir}/blumkin" --help >/dev/null

  printf 'verify-pypi-release: pipx install passed for %s\n' "$version"
}

main "$@"
