#!/usr/bin/env bash
# ===========================================================================
# Privacy Proxy Demo — standalone terminal version
# ===========================================================================
# This is the canonical downloadable walkthrough of Cloudflare's Privacy Proxy.
# The repository's cli/demo.sh launcher delegates here so the local and hosted
# versions cannot drift.
#
# A self-contained walkthrough of Cloudflare's Privacy Proxy, for people who
# want to try it in a terminal instead of the hosted web page.
#
# It follows the SAME story as the web page (https://.../):
#   1. DIRECT REQUEST      — see what a destination learns about you today.
#   2. PRIVACY PROXY REQUEST — route the same request through the proxy so your
#                             IP is masked while your chosen location is kept.
#
# It talks to the SAME backend the page does (via /config and /observe), so the
# two demos never drift apart.
#
# Requirements: bash, curl, jq, and pvcli (the client we're demoing).
# Run:   ./privacy-proxy-demo.sh   (against the deployed demo)
#        ./privacy-proxy-demo.sh --host http://127.0.0.1:8787
#                                  (against a local Wrangler server)
# ===========================================================================

set -euo pipefail

# --- Where the demo backend lives ------------------------------------------
# Default to the deployed destination host from shared/config.js. Override with
# --host or the DEMO_HOST env var (handy for a local `wrangler dev`).
DEFAULT_HOST="https://privacy-proxy-demo.cloudflare.app"
TRUSTED_PROXY_ENDPOINT="https://cp-demo.cloudflare.com"
TRUSTED_PAT_ISSUER="demo-pat.issuer.cloudflare.com"
HOST="${DEMO_HOST:-$DEFAULT_HOST}"

# --- Parse flags ------------------------------------------------------------
while [ $# -gt 0 ]; do
  case "$1" in
    --host)
      HOST="${2:-}"; shift 2 ;;
    --host=*)
      HOST="${1#--host=}"; shift ;;
    -h|--help)
      echo "Usage: $0 [--host URL]"
      echo "  --host URL   Base URL of the demo backend"
      echo "               (default: $DEFAULT_HOST)"
      exit 0 ;;
    *)
      echo "Unknown option: $1" >&2; exit 2 ;;
  esac
done
HOST="${HOST%/}"   # strip any trailing slash

# Recreate the prompt where the user launched the demo so executed commands look
# like normal terminal output instead of an abstract "$" example.
PROMPT_USER="${USER:-$(id -un)}"
PROMPT_HOST="$(hostname -s 2>/dev/null || hostname 2>/dev/null || printf 'localhost')"
PROMPT_PATH="$PWD"
if [ -n "${HOME:-}" ]; then
  case "$PROMPT_PATH" in
    "$HOME") PROMPT_PATH="~" ;;
    "$HOME"/*) PROMPT_PATH="~${PROMPT_PATH#"$HOME"}" ;;
  esac
fi

# --- Colors (with a safe fallback) -----------------------------------------
# Only use color when writing to a real terminal and NO_COLOR isn't set.
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
  BOLD="$(printf '\033[1m')";  DIM="$(printf '\033[2m')"
  RED="$(printf '\033[31m')";  GREEN="$(printf '\033[32m')"
  ORANGE="$(printf '\033[38;5;208m')"; RESET="$(printf '\033[0m')"
else
  BOLD=""; DIM=""; RED=""; GREEN=""; ORANGE=""; RESET=""
fi

hr() { printf '%s\n' "${DIM}--------------------------------------------------------------${RESET}"; }
section() { printf '\n%s\n' "${BOLD}${ORANGE}== $1 ==${RESET}"; }

# A full-width bar of '=' used to fence off result blocks so they stand out
# from the surrounding terminal noise.
BAR="============================================================"
# result_block <color> <title> <subtitle>  — prints a fenced, colored header.
# Follow this with the result rows, then call `result_end <color>`.
result_block() {
  local color="$1" title="$2" subtitle="$3"
  printf '\n%s%s%s\n' "${color}${BOLD}" "$BAR" "${RESET}"
  printf '%s%s%s%s · %s%s\n' "${color}${BOLD}" "$title" "${RESET}" "${DIM}" "$subtitle" "${RESET}"
  printf '%s%s%s\n' "${color}${BOLD}" "$BAR" "${RESET}"
}
result_end() {
  local color="$1"
  printf '%s%s%s\n' "${color}${BOLD}" "$BAR" "${RESET}"
}

# ---------------------------------------------------------------------------
# Geohash encoder — ported from demo/backend/src/geohash.js so the terminal
# and the web page agree on every geohash. A geohash is a short text code for a
# rough area on Earth; fewer characters = a bigger (coarser) area.
# ---------------------------------------------------------------------------
BASE32="0123456789bcdefghjkmnpqrstuvwxyz"

encode_geohash() {
  # args: lat lon [precision]  -> prints the geohash
  local lat="$1" lon="$2" precision="${3:-5}"
  # Work in integers scaled by 1e7 so we don't need floating point in bash.
  # 1e7 is enough headroom to match the JS backend exactly (verified against
  # the canonical geohash vector u4pruydqqvj at 11 chars).
  local latLo=-900000000 latHi=900000000
  local lonLo=-1800000000 lonHi=1800000000
  local latV lonV
  latV=$(awk -v v="$lat" 'BEGIN{printf "%d", v*10000000}')
  lonV=$(awk -v v="$lon" 'BEGIN{printf "%d", v*10000000}')

  local hash="" bits=0 bit=0 even=1 i=0 count="$precision"
  local mid
  while [ "${#hash}" -lt "$count" ]; do
    if [ "$even" -eq 1 ]; then
      mid=$(( (lonLo + lonHi) / 2 ))
      if [ "$lonV" -ge "$mid" ]; then bit=$(( (bit << 1) + 1 )); lonLo=$mid
      else bit=$(( bit << 1 )); lonHi=$mid; fi
      even=0
    else
      mid=$(( (latLo + latHi) / 2 ))
      if [ "$latV" -ge "$mid" ]; then bit=$(( (bit << 1) + 1 )); latLo=$mid
      else bit=$(( bit << 1 )); latHi=$mid; fi
      even=1
    fi
    bits=$(( bits + 1 ))
    if [ "$bits" -eq 5 ]; then
      hash="${hash}${BASE32:$bit:1}"
      bits=0; bit=0
    fi
  done
  printf '%s' "$hash"
}

# Geohash cell dimensions at a given latitude. Cell height is fixed for each
# precision; width narrows toward the poles, so calculate it for the user.
geohash_dimensions() {
  # args: lat precision -> prints "width × height km"
  local lat="$1" precision="$2"
  awk -v lat="$lat" -v p="$precision" '
    function fmt(n) { return n >= 10 ? sprintf("%.0f", n) : sprintf("%.1f", n) }
    BEGIN {
      bits = 5 * p
      lonBits = int((bits + 1) / 2)
      latBits = int(bits / 2)
      lonDegrees = 360 / (2 ^ lonBits)
      latDegrees = 180 / (2 ^ latBits)
      pi = atan2(0, -1)
      width = lonDegrees * 111.32 * cos(lat * pi / 180)
      height = latDegrees * 110.574
      printf "%s × %s km", fmt(width), fmt(height)
    }'
}

# --- Geohash levels (matches the web page's precision control) -------------
# Each level is a geohash precision (character count). Fewer characters = a
# bigger area and less precise geolocation. The geohash is always computed from
# the user's REAL location, then truncated to this length.
# Parallel arrays so this works on the old bash 3.2 that ships with macOS.
OBF_LABELS=(
  "Couple-block radius"
  "Neighborhood"
  "City"
  "Region"
)
OBF_PRECISIONS=(6 5 4 3)
OBF_DEFAULT=3   # 1-based index into the arrays; 3 = City (precision 4)

# ---------------------------------------------------------------------------
# Preflight: make sure the tools we depend on are present.
# ---------------------------------------------------------------------------
need() {
  if ! command -v "$1" >/dev/null 2>&1; then
    printf '%s\n' "${RED}Missing required tool: $1${RESET}" >&2
    return 1
  fi
}

section "Privacy Proxy Demo"
cat <<EOF
Cloudflare's Privacy Proxy is a MASQUE-based forward proxy designed to hide a
client's IP while retaining coarse location context. This demo sends the same
request directly and through the proxy so you can compare what the destination
observes.

For this demo, your machine acts as the client. In production, the client
represents an end user or application accessing a destination.

Backend: ${BOLD}${HOST}${RESET}
EOF

missing=0
need curl || missing=1
need jq || missing=1
if ! command -v pvcli >/dev/null 2>&1; then
  printf '%s\n' "${RED}Missing required tool: pvcli${RESET}" >&2
  printf '%s\n' "Install the public client, then re-run:" >&2
  printf '%s\n' "  cargo install --git https://github.com/cloudflareresearch/pvcli --locked" >&2
  missing=1
fi
if [ "$missing" -ne 0 ]; then
  printf '\n%s\n' "${RED}Please install the missing tool(s) above and run again.${RESET}" >&2
  exit 1
fi
PVCLI_HELP="$(pvcli --help 2>&1 || true)"
case "$PVCLI_HELP" in
  *--pat-issuer*) ;;
  *)
    printf '%s\n' "${RED}Your pvcli installation does not support Privacy Pass authentication.${RESET}" >&2
    printf '%s\n' "Install the current public client, then re-run:" >&2
    printf '%s\n' "  cargo install --git https://github.com/cloudflareresearch/pvcli --locked --force" >&2
    exit 1 ;;
esac

# ---------------------------------------------------------------------------
# Fetch config from the live backend — same source of truth as the page.
# ---------------------------------------------------------------------------
CONFIG_JSON="$(curl -fsS "${HOST}/config" 2>/dev/null || true)"
if [ -z "$CONFIG_JSON" ]; then
  printf '%s\n' "${RED}Couldn't reach ${HOST}/config.${RESET}" >&2
  printf '%s\n' "If you're running the backend locally, start it with 'wrangler dev'" >&2
  printf '%s\n' "and pass --host http://127.0.0.1:8787" >&2
  exit 1
fi
if ! printf '%s' "$CONFIG_JSON" | jq -e '
    type == "object"
    and (.proxyEndpoint | type == "string")
    and (.patIssuer | type == "string")
    and (.usingRealProxy | type == "boolean")
    and (.demoEnabled | type == "boolean")
  ' >/dev/null 2>&1; then
  printf '%s\n' "${RED}${HOST}/config did not return demo configuration.${RESET}" >&2
  printf '%s\n' "The backend may not be deployed at that address yet." >&2
  printf '%s\n' "For local testing, start Wrangler and use --host http://127.0.0.1:8787" >&2
  exit 1
fi
PROXY_ENDPOINT="$(printf '%s' "$CONFIG_JSON" | jq -r '.proxyEndpoint')"
PAT_ISSUER="$(printf '%s' "$CONFIG_JSON" | jq -r '.patIssuer')"
USING_REAL_PROXY="$(printf '%s' "$CONFIG_JSON" | jq -r '.usingRealProxy')"
DEMO_ENABLED="$(printf '%s' "$CONFIG_JSON" | jq -r '.demoEnabled')"
if [ "$DEMO_ENABLED" != "true" ]; then
  printf '%s\n' "${RED}Demo temporarily paused.${RESET}" >&2
  printf '%s\n' "New sessions are disabled while the team performs maintenance. Please try again later." >&2
  exit 1
fi
if [ "$USING_REAL_PROXY" != "true" ]; then
  printf '%s\n' "${RED}The live Privacy Proxy is not enabled in ${HOST}/config.${RESET}" >&2
  exit 1
fi
if [ "$PROXY_ENDPOINT" != "$TRUSTED_PROXY_ENDPOINT" ] || [ "$PAT_ISSUER" != "$TRUSTED_PAT_ISSUER" ]; then
  printf '%s\n' "${RED}${HOST}/config returned an untrusted proxy configuration.${RESET}" >&2
  printf '%s\n' "Refusing to contact an unapproved proxy or Privacy Pass issuer." >&2
  exit 1
fi

# --- Your IP-based location (from the edge) — same source the page uses -----
# /whereami reads Cloudflare's edge geo off the request. We use the real
# lat/lon to compute the geohash at whatever obfuscation level you pick.
WHEREAMI_JSON="$(curl -fsS "${HOST}/whereami" 2>/dev/null || true)"
if ! printf '%s' "$WHEREAMI_JSON" | jq -e '
    (.lat | numbers | . >= -90 and . <= 90)
    and (.lon | numbers | . >= -180 and . <= 180)
    and (.location.country | strings | test("^[A-Za-z]{2}$"))
  ' >/dev/null 2>&1; then
  printf '%s\n' "${RED}The demo couldn't determine a valid IP-based location.${RESET}" >&2
  printf '%s\n' "A real location is required to build the proxy geohash safely." >&2
  exit 1
fi
REAL_LAT="$(printf '%s' "$WHEREAMI_JSON" | jq -r '.lat // empty')"
REAL_LON="$(printf '%s' "$WHEREAMI_JSON" | jq -r '.lon // empty')"
REAL_COUNTRY="$(printf '%s' "$WHEREAMI_JSON" | jq -r '.location.country // empty' | tr 'a-z' 'A-Z')"
REAL_PLACE="$(printf '%s' "$WHEREAMI_JSON" | jq -r '
  [.location.city, .location.region, .location.country]
  | map(select(. != null and . != "")) | join(", ")
  | if . == "" then "your area" else . end' 2>/dev/null || true)"
[ -z "$REAL_PLACE" ] && REAL_PLACE="your area"

# --- Allocate a short-lived random session ---------------------------------
# The CLI intentionally has no Turnstile. The backend rate-limits this route
# before issuing an unguessable Durable Object ID.
SESSION_JSON="$(curl -fsS -X POST "${HOST}/session" 2>/dev/null || true)"
SESSION="$(printf '%s' "$SESSION_JSON" | jq -r '.session // empty')"
if [ -z "$SESSION" ]; then
  printf '%s\n' "${RED}Couldn't create a demo session. Wait a minute and try again.${RESET}" >&2
  exit 1
fi
DEST_BEFORE="${HOST}/observe?session=${SESSION}&phase=before"
DEST_AFTER="${HOST}/observe?session=${SESSION}&phase=after"
# Display a safe placeholder so screenshots and copied terminal output do not
# contain the signed one-hour session capability. Requests still use the real URLs.
DISPLAY_DEST_BEFORE="${HOST}/observe?session=<SESSION>&phase=before"
DISPLAY_DEST_AFTER="${HOST}/observe?session=<SESSION>&phase=after"

# --- Helper: print an observation in FULL -----------------------------------
# Shows everything the destination recorded about the request — every field the
# backend returns — as labeled rows, so nothing is cut off.
show_observation() {
  # args: json
  local json="$1"
  # %-17s pads the labels so the values line up in a neat column.
  printf '  %-17s %s\n' "IP address:"       "$(printf '%s' "$json" | jq -r '.ip // "—"')"
  printf '  %-17s %s\n' "Network:"          "$(printf '%s' "$json" | jq -r '.network.organization // "—"')"
  printf '  %-17s %s\n' "ASN:"              "$(printf '%s' "$json" | jq -r 'if .network.asn then "AS" + (.network.asn | tostring) else "—" end')"
  printf '  %-17s %s\n' "City:"             "$(printf '%s' "$json" | jq -r '.location.city // "—"')"
  printf '  %-17s %s\n' "Region:"           "$(printf '%s' "$json" | jq -r '.location.region // "—"')"
  printf '  %-17s %s\n' "Country:"          "$(printf '%s' "$json" | jq -r '.location.country // "—"')"
  printf '  %-17s %s\n' "Colo:"             "$(printf '%s' "$json" | jq -r '.location.colo // "—"')"
  printf '  %-17s %s\n' "Latitude:"         "$(printf '%s' "$json" | jq -r '.location.latitude // "—"')"
  printf '  %-17s %s\n' "Longitude:"        "$(printf '%s' "$json" | jq -r '.location.longitude // "—"')"
  printf '  %-17s %s\n' "User agent:"       "$(printf '%s' "$json" | jq -r '.userAgent // "—"')"
  printf '  %-17s %s\n' "Observed at:"      "$(printf '%s' "$json" | jq -r '.observedAt // "—"')"
}

# --- Helper: queue a command, run it when the user hits Enter ---------------
# Mirrors the web page's "Copy → run" beat: the command is already loaded, and
# the user just presses Enter to fire it (a small, deliberate interaction). We
# run it for them, then read the result back from the backend.
# Progress text goes to stderr; the observation JSON goes to stdout.
# Args: phase(before|after), safe display command, then real command and args.
# Returns 2 if the user skips and 1 if the request does not record.
run_on_enter() {
  local phase="$1" display_command="$2"; shift 2
  printf '%s' "${BOLD}▶ Press Enter to run this command${RESET} ${DIM}(or type s + Enter to skip):${RESET} " >&2
  local answer=""
  read -r answer || answer=""
  case "$answer" in
    s|S|skip) printf '%s\n' "${DIM}Skipped.${RESET}" >&2; return 2 ;;
  esac
  printf '\n%s%s@%s %s %% %s%s\n' \
    "${BOLD}" "$PROMPT_USER" "$PROMPT_HOST" "$PROMPT_PATH" "$display_command" "${RESET}" >&2
  printf '%s' "Running... " >&2
  # /observe returns the destination's observation directly, so no browser
  # cookie or polling request is needed.
  local output="" request_failed=0 json
  output="$("$@" 2>&1)" || request_failed=1
  json="$(printf '%s' "$output" | jq -c '.' 2>/dev/null || true)"
  if [ "$request_failed" -eq 0 ] && printf '%s' "$json" | jq -e '
      type == "object"
      and (.error == null)
      and (.ip | type == "string" and length > 0)
      and (.location | type == "object")
    ' >/dev/null 2>&1; then
    printf '%s\n' "${GREEN}done.${RESET}" >&2
    printf '%s\n' "$output" >&2
    printf '%s' "$json"
    return 0
  fi
  printf '%s\n' "${RED}no response recorded.${RESET}" >&2
  if [ -n "$output" ]; then
    printf '%s\n' "${RED}pvcli reported:${RESET}" >&2
    printf '%s\n' "$output" >&2
  elif [ "$request_failed" -eq 1 ]; then
    printf '%s\n' "${RED}pvcli exited with an error but provided no details.${RESET}" >&2
  else
    printf '%s\n' "${RED}pvcli completed without a response body.${RESET}" >&2
    printf '%s\n' "The proxy may have rejected CONNECT before reaching the destination." >&2
  fi
  return 1
}

# --- Helper: show one flag being added, then wait for Enter -----------------
# Used to build the proxy command one flag at a time. Prints the command so
# far (with the newest flag highlighted), a one-line "why", then pauses for
# Enter so the user can absorb each piece.
# Args: <why-text> <command-so-far>
add_flag_step() {
  local why="$1" cmd_so_far="$2"
  printf '%s%s%s\n' "${BOLD}" "$why" "${RESET}"
  printf '  %s\n' "$cmd_so_far"
  printf '%s' "${DIM}  (press Enter to continue)${RESET} " >&2
  read -r _ || true
  echo
}

# ===========================================================================
# STEP 1 — DIRECT REQUEST (runs for real, today)
# ===========================================================================
section "Direct Request"
cat <<EOF
A normal request, no proxy. The destination sees the client's real IP and
location. You are manually sending a request that client software would normally
send automatically.

${BOLD}This command is ready to go:${RESET}

  ${BOLD}pvcli "${DISPLAY_DEST_BEFORE}"${RESET}
EOF
echo
# The command is queued — the user just hits Enter to fire it.
if BEFORE_JSON="$(run_on_enter "before" "pvcli \"${DISPLAY_DEST_BEFORE}\"" pvcli "${DEST_BEFORE}")"; then
  result_block "$RED" "BEFORE — DIRECT" "your real information, visible to the destination"
  show_observation "$BEFORE_JSON"
  result_end "$RED"
else
  printf '%s\n' "${RED}No direct request ran, so there's nothing to compare yet.${RESET}"
  printf '%s\n' "Start the demo again when you're ready." >&2
  exit 1
fi

# ===========================================================================
# STEP 2 — PRIVACY PROXY REQUEST
# Order mirrors the web page: title + description, then the command built one
# flag at a time. The obfuscation decision lives inside the geohash flag step.
# ===========================================================================
section "Privacy Proxy Request"
cat <<EOF
The same client request as before, now routed through Privacy Proxy. Privacy
Proxy replaces the client's IP with a Cloudflare egress IP before the request
reaches the destination.

EOF

# --- Build the command one flag at a time ----------------------------------
# Start from the same request as the direct step, then add each proxy flag with
# a short "why" and an Enter beat, so the command visibly grows.
cat <<EOF
We'll build the proxy command together by adding flags one at a time.
EOF
echo

CMD="pvcli \"${DISPLAY_DEST_AFTER}\""
add_flag_step "Start: the same request you sent directly." \
  "$CMD"

CMD="pvcli \\
  ${ORANGE}-x ${PROXY_ENDPOINT}${RESET} \\
  \"${DISPLAY_DEST_AFTER}\""
add_flag_step "Add -x — route the request through the Privacy Proxy." \
  "$CMD"

CMD="pvcli \\
  -x ${PROXY_ENDPOINT} \\
  ${ORANGE}--pat-issuer ${PAT_ISSUER}${RESET} \\
  \"${DISPLAY_DEST_AFTER}\""
add_flag_step "Add --pat-issuer — trigger the issuance flow for a fresh Privacy Pass token when the completed command runs." \
  "$CMD"

cat <<EOF
${DIM}Note: To see the Privacy Pass issuance flow step by step, try the demo from the
Privacy Pass developer documentation:${RESET}
  https://developers.cloudflare.com/privacy-pass/getting-started/
EOF
echo

# --- Geohash selection: chosen as part of adding the geohash header ---------
printf '%s%s%s\n' "${BOLD}" "Add --proxy-header — send the proxy your geohash for egress selection." "${RESET}"
echo
printf '%s\n' "Geohash Selection:"
echo "Choose how precise of a geohash to use for your location. Fewer geohash"
echo "characters = a bigger area = less precise location. The geohash is computed"
echo "from your real location."
printf '%s\n' "(${BOLD}${REAL_PLACE}${RESET})."
echo
i=1
for label in "${OBF_LABELS[@]}"; do
  idx=$(( i - 1 ))
  prec="${OBF_PRECISIONS[$idx]}"
  gh="$(encode_geohash "$REAL_LAT" "$REAL_LON" "$prec")"
  dimensions="$(geohash_dimensions "$REAL_LAT" "$prec")"
  suffix=""
  [ "$i" -eq "$OBF_DEFAULT" ] && suffix=" ${DIM}[default]${RESET}"
  printf '  %d) %-22s %s(%s chars · %s · %s)%s%s\n' \
    "$i" "$label" "${DIM}" "$prec" "$dimensions" "$gh" "${RESET}" "$suffix"
  i=$(( i + 1 ))
done
echo
CHOICE=""
while true; do
  printf '%s' "Enter a number (1-${#OBF_LABELS[@]}), or Enter for default: "
  read -r CHOICE || CHOICE=""
  [ -z "$CHOICE" ] && CHOICE="$OBF_DEFAULT"   # bare Enter = default level
  case "$CHOICE" in
    *[!0-9]*) printf '%s\n' "${RED}Please enter a number.${RESET}"; continue ;;
  esac
  if [ "$CHOICE" -ge 1 ] && [ "$CHOICE" -le "${#OBF_LABELS[@]}" ]; then
    break
  fi
  printf '%s\n' "${RED}Out of range. Try again.${RESET}"
done
SEL_IDX=$(( CHOICE - 1 ))
SEL_LABEL="${OBF_LABELS[$SEL_IDX]}"
SEL_PRECISION="${OBF_PRECISIONS[$SEL_IDX]}"
SEL_GEOHASH="$(encode_geohash "$REAL_LAT" "$REAL_LON" "$SEL_PRECISION")"
SEL_GEOHASH_HEADER="${SEL_GEOHASH}-${REAL_COUNTRY}"
SEL_DIMENSIONS="$(geohash_dimensions "$REAL_LAT" "$SEL_PRECISION")"
printf '\nSelected: %s%s%s  (%s · geohash %s%s%s)\n\n' \
  "${BOLD}" "$SEL_LABEL" "${RESET}" "$SEL_DIMENSIONS" "${ORANGE}" "$SEL_GEOHASH_HEADER" "${RESET}"

CMD="pvcli \\
  -x ${PROXY_ENDPOINT} \\
  --pat-issuer ${PAT_ISSUER} \\
  ${ORANGE}--proxy-header \"sec-ch-geohash: ${SEL_GEOHASH_HEADER}\"${RESET} \\
  \"${DISPLAY_DEST_AFTER}\""
printf '  %s\n' "$CMD"
printf '%s' "${DIM}  (press Enter to continue)${RESET} " >&2
read -r _ || true
echo

AFTER_CMD="pvcli \\
  -x ${PROXY_ENDPOINT} \\
  --pat-issuer ${PAT_ISSUER} \\
  --proxy-header \"sec-ch-geohash: ${SEL_GEOHASH_HEADER}\" \\
  \"${DISPLAY_DEST_AFTER}\""
printf '%s\n\n' "${BOLD}The full command is ready:${RESET}"
printf '  %s\n\n' "$AFTER_CMD"

AFTER_JSON=""
# Queue the live proxy command; Enter runs it and reads the result.
if AFTER_JSON="$(run_on_enter "after" \
    "$AFTER_CMD" pvcli -x "${PROXY_ENDPOINT}" --pat-issuer "${PAT_ISSUER}" \
    --proxy-header "sec-ch-geohash: ${SEL_GEOHASH_HEADER}" "${DEST_AFTER}")"; then
  result_block "$GREEN" "AFTER — PRIVACY PROXY" "what this destination observed"
  show_observation "$AFTER_JSON"
  result_end "$GREEN"
else
  AFTER_STATUS=$?
  AFTER_JSON=""
  if [ "$AFTER_STATUS" -eq 2 ]; then
    printf '%s\n' "${DIM}Skipped — moving on to the comparison.${RESET}"
  else
    printf '%s\n' "${RED}Proxy request failed — moving on without an AFTER result.${RESET}"
  fi
fi

# ===========================================================================
# Summary — side-by-side comparison (what the destination observes)
# ===========================================================================
section "Side-by-side comparison"

# Pull the real IP / location from the direct request we actually ran.
BEFORE_IP="$(printf '%s' "$BEFORE_JSON" | jq -r '.ip // "—"')"
BEFORE_LOC="$(printf '%s' "$BEFORE_JSON" | jq -r '
  [.location.city, .location.region, .location.country]
  | map(select(. != null and . != "")) | join(", ")
  | if . == "" then "unknown" else . end')"

result_block "$RED" "BEFORE — DIRECT" "client IP and estimated location"
printf '  %-22s %s\n' "IP address:" "$BEFORE_IP"
printf '  %-22s %s\n' "IP location estimate:" "$BEFORE_LOC"
result_end "$RED"

if [ -n "${AFTER_JSON:-}" ]; then
  AFTER_IP="$(printf '%s' "$AFTER_JSON" | jq -r '.ip // "—"')"
  AFTER_LOC="$(printf '%s' "$AFTER_JSON" | jq -r '
    [.location.city, .location.region, .location.country]
    | map(select(. != null and . != "")) | join(", ")
    | if . == "" then "unknown" else . end')"
  AFTER_SUBTITLE="Cloudflare egress IP and estimated location"
  result_block "$GREEN" "AFTER — PRIVACY PROXY" "$AFTER_SUBTITLE"
  printf '  %-22s %s\n' "IP address:" "$AFTER_IP"
  printf '  %-22s %s\n' "IP location estimate:" "$AFTER_LOC"
  result_end "$GREEN"
else
  result_block "$GREEN" "AFTER — PRIVACY PROXY" "no result recorded"
  printf '  %-13s %s\n' "Status:" "run the live proxy request to collect evidence"
  result_end "$GREEN"
fi
echo
printf '%s\n' "For the visual version, open the web page: ${BOLD}${HOST}${RESET}"
