#!/usr/bin/env bash
# Build the wheel the way "Publish PyPI" does, install it into a throwaway venv
# outside the repo, and smoke the console script. Catches missing package data
# and entry-point mistakes that an editable install hides.
set -euo pipefail

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly repo_root
# The build steps below read pyproject.toml and run `git rev-parse` / `uv build`
# relative to CWD; anchor to the repo so this works when called by path from
# anywhere (a local preflight helper, say), like restore_build_metadata assumes.
cd "$repo_root"

temp_root="$(mktemp -d)"
build_dir="$temp_root/dist"
venv_dir="$temp_root/venv"
outside_dir="$temp_root/outside"
home_dir="$temp_root/home"

mkdir -p "$build_dir" "$outside_dir" "$home_dir"

embed_build_metadata="${repo_root}/scripts/embed_build_metadata"
readonly embed_build_metadata

restore_build_metadata() {
  (
    cd "${repo_root}"
    env -u BLUMKIN_EMBED_VERSION -u BLUMKIN_EMBED_COMMIT \
      uv run python "${embed_build_metadata}"
  ) >/dev/null 2>&1 || true
}
cleanup() {
  restore_build_metadata
  rm -rf "$temp_root"
}
trap cleanup EXIT

echo "Embedding build metadata for the wheel..."
version="$(
  uv run python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])'
)"
commit="$(git rev-parse HEAD)"
export BLUMKIN_EMBED_VERSION="${version}"
export BLUMKIN_EMBED_COMMIT="${commit}"
uv run python "${embed_build_metadata}"

echo "Building sdist + wheel (same path as Publish PyPI)..."
timeout 120 uv build --out-dir "$build_dir"
restore_build_metadata

wheels=("$build_dir"/*.whl)
sdists=("$build_dir"/*.tar.gz)
wheel_path="${wheels[0]}"
sdist_path="${sdists[0]}"
if [[ ! -f "$wheel_path" ]]; then
  echo "No wheel produced under $build_dir" >&2
  exit 1
fi
if [[ ! -f "$sdist_path" ]]; then
  echo "No sdist produced under $build_dir" >&2
  exit 1
fi

echo "Installing wheel into an isolated virtual environment..."
timeout 120 uv venv --python 3.14 "$venv_dir"
timeout 120 uv pip install --python "$venv_dir/bin/python" "$wheel_path"
bin_dir="$venv_dir/bin"
readonly bin_dir

cd "$outside_dir"
export HOME="$home_dir"

installed_env() {
  # Prove the embedded stamp wins over an ambient GITHUB_SHA (every GitHub
  # Actions job exports one for its own repo) and drop any operator override.
  # A real pipx install has neither - the embedded stamp answers.
  env -u BLUMKIN_GIT_SHA GITHUB_SHA=0000000000000000000000000000000000000000 "$@"
}

"$bin_dir/blumkin" --help >/dev/null

version_line="$(installed_env "$bin_dir/blumkin" --version)"
printf '%s\n' "$version_line"
if [[ "$version_line" == *"(unknown)"* ]]; then
  echo "Installed --version still reports an unknown commit: $version_line" >&2
  exit 1
fi
if ! grep --fixed-strings --quiet "$version" <<<"$version_line"; then
  echo "Installed --version is missing package version ${version}: $version_line" >&2
  exit 1
fi
if ! grep --fixed-strings --quiet "${commit:0:12}" <<<"$version_line"; then
  echo "Installed --version is missing embedded commit ${commit:0:12}: $version_line" >&2
  exit 1
fi
if [[ "$version_line" == *"000000000000"* ]]; then
  echo "Installed --version leaked ambient GITHUB_SHA instead of the embedded commit: $version_line" >&2
  exit 1
fi

# skills list is a static catalog - it needs the packaged module tree but no auth.
installed_env "$bin_dir/blumkin" skills list --json >/dev/null

echo "Installed wheel packaging smoke passed"

# pipx has its own venv layout and console-script shims (PIPX_HOME / PIPX_BIN_DIR),
# none of which `uv pip install` 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). Always install a known-good
# pipx via `uv tool install` instead of trusting whatever `pipx` is already on PATH -
# it lands under the already-isolated $HOME set above, not the host's real one.
echo "Installing the wheel with pipx (separate venv layout from the uv pip install above)..."
timeout 60 uv tool install pipx >/dev/null
pipx_bin="$(uv tool dir --bin)/pipx"
if [[ ! -x "$pipx_bin" ]]; then
  echo "pipx not found at $pipx_bin after uv tool install" >&2
  exit 1
fi

pipx_home="$temp_root/pipx-home"
pipx_bin_dir="$temp_root/pipx-bin"
mkdir -p "$pipx_home" "$pipx_bin_dir"
# Pin pipx's own venv to the same 3.14 the uv-pip block above already proved
# ($venv_dir/bin/python) - HOME is now the scratch dir, 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).
timeout 120 env PIPX_HOME="$pipx_home" PIPX_BIN_DIR="$pipx_bin_dir" \
  "$pipx_bin" install --python "$venv_dir/bin/python" "$wheel_path"

pipx_version_line="$(installed_env "$pipx_bin_dir/blumkin" --version)"
printf '%s\n' "$pipx_version_line"
if [[ "$pipx_version_line" == *"(unknown)"* ]]; then
  echo "pipx-installed --version reports an unknown commit: $pipx_version_line" >&2
  exit 1
fi
if ! grep --fixed-strings --quiet "$version" <<<"$pipx_version_line"; then
  echo "pipx-installed --version is missing package version ${version}: $pipx_version_line" >&2
  exit 1
fi
if ! grep --fixed-strings --quiet "${commit:0:12}" <<<"$pipx_version_line"; then
  echo "pipx-installed --version is missing embedded commit ${commit:0:12}: $pipx_version_line" >&2
  exit 1
fi
"$pipx_bin_dir/blumkin" --help >/dev/null

echo "Installed pipx packaging smoke passed"

# `blumkin upgrade` round-trip against PyPI (issue #143 / #239). The block above
# installs the freshly built wheel; here we install the *second-newest* published
# release into a second, custom PIPX_BIN_DIR, run its `blumkin upgrade`, and
# assert it moved to the newest published release and reported the managed path
# under that custom bin dir. The binary under test is the previous release, so
# the check tolerates both the old `pipx_app` JSON shape and the
# install-method-aware shape. Needs >=2 published releases, so this is a no-op
# skip from the first release and real from the second onward.
echo "Checking the blumkin upgrade round-trip against PyPI..."
roundtrip_home="$temp_root/pipx-roundtrip-home"
roundtrip_bin_dir="$temp_root/pipx-roundtrip-bin"
mkdir -p "$roundtrip_home" "$roundtrip_bin_dir"

roundtrip_env() {
  # `blumkin upgrade` resolves pipx via PATH; pin the pipx env to the second bin dir.
  env PIPX_HOME="$roundtrip_home" PIPX_BIN_DIR="$roundtrip_bin_dir" \
    PATH="$(dirname "$pipx_bin"):$PATH" "$@"
}

# Newest and second-newest *final* published versions, matching what `pipx
# upgrade` (pip) actually resolves against: no pre-releases, no yanked releases.
# A parse error prints nothing (Python side); a hang / SIGTERM from the outer
# `timeout` is caught by `|| pypi_versions=""` (shell side). Either way the block
# below skips instead of aborting the job (set -euo pipefail).
pypi_versions="$(
  timeout 30 "$venv_dir/bin/python" - <<'PY'
import json
import re
import urllib.request

FINAL = re.compile(r"^[0-9]+(?:\.[0-9]+)*$")  # 1.2.3 — excludes 1.2.3rc1 / .post1 / .dev0
try:
    with urllib.request.urlopen("https://pypi.org/pypi/blumkin/json", timeout=25) as response:
        releases = json.load(response)["releases"]
    finals = sorted(
        (
            v
            for v, files in releases.items()
            if FINAL.match(v)
            and files
            and not all(f.get("yanked", False) for f in files)
        ),
        key=lambda v: [int(p) for p in v.split(".")],
    )
except Exception:
    finals = []
print(finals[-2] if len(finals) >= 2 else "")
print(finals[-1] if finals else "")
PY
)" || pypi_versions=""
prev_version="$(printf '%s\n' "$pypi_versions" | sed -n '1p')"
latest_version="$(printf '%s\n' "$pypi_versions" | sed -n '2p')"

if [[ -z "$prev_version" ]]; then
  echo "Skipping upgrade round-trip: fewer than two blumkin releases on PyPI"
else
  echo "Round-trip: install blumkin==${prev_version}, upgrade, expect ${latest_version}"
  roundtrip_env timeout 120 "$pipx_bin" install \
    --python "$venv_dir/bin/python" "blumkin==${prev_version}"
  upgrade_json="$temp_root/pipx-roundtrip-upgrade.json"
  roundtrip_env timeout 180 "$roundtrip_bin_dir/blumkin" upgrade --json >"$upgrade_json"
  cat "$upgrade_json"
  "$venv_dir/bin/python" - "$upgrade_json" "$prev_version" "$latest_version" <<'PY'
import json
import sys

data = json.load(open(sys.argv[1]))
prev_version, latest_version = sys.argv[2], sys.argv[3]
# The binary under test is the *previously published* release, so tolerate both
# JSON shapes: <=0.6.0 nested `pipx_app.{before,after,path}`, and the
# install-method-aware shape from #239 (`from` / `to` / `managed_path`, plus
# `install_method == "pipx"` once a release ships it).
app = data.get("pipx_app") or {}
before = data.get("from") or app.get("before")
after = data.get("to") or app.get("after")
managed = data.get("managed_path") or app.get("path") or ""
running = (data.get("running_from") or {}).get("path") or ""
assert data.get("ok") is True, data
assert before and after, f"upgrade did not read both builds: {data}"
assert before != after, f"upgrade was a no-op: {data}"
assert before.split()[0] == prev_version, data
assert after.split()[0] == latest_version, data
assert "pipx-roundtrip" in managed or "pipx-roundtrip" in running, data
if "install_method" in data:  # new shape - a release has shipped #239
    assert data["install_method"] == "pipx", data
    assert data.get("action_taken") == "pipx upgrade blumkin", data
print(f"upgrade round-trip: {before} -> {after}")
PY
  "$roundtrip_bin_dir/blumkin" --help >/dev/null
  echo "blumkin upgrade round-trip passed"
fi
