#!/usr/bin/env bash
# Update a contact without overwriting a change you have not seen:
# read it (ETag), compute the change from what you read, PATCH with If-Match, and on 412 start over.
# Requires bash, curl and jq. Runs in test mode only: it creates a test-mode contact for +12025550100.
#
#   export RELATIONSAI_API_KEY="rai_test_..."
#   bash etag-update.sh
set -euo pipefail

BASE="${RELATIONSAI_API_BASE:-https://voice-api.relationsai.com/v1}"
BASE="${BASE%/}"
if [[ "${RELATIONSAI_API_KEY:-}" != rai_test_* ]]; then
  echo "Set RELATIONSAI_API_KEY to a test key (rai_test_...)." >&2
  exit 1
fi

BODY_FILE="$(mktemp)" HDR_FILE="$(mktemp)"
trap 'rm -f "$BODY_FILE" "$HDR_FILE"' EXIT

header() { { grep -i "^$1:" "$HDR_FILE" || true; } | tail -n 1 | cut -d' ' -f2- | tr -d '\r'; }
error_code() { jq -r '.error.code' "$BODY_FILE"; }
fail() { jq -r --arg s "$1" '"\($s) \(.error.code) (request \(.error.request_id)): \(.error.message)"' "$BODY_FILE" >&2; exit 1; }

# call METHOD PATH [JSON] [EXTRA_HEADER]: prints the status; body in $BODY_FILE, headers in $HDR_FILE.
call() {
  local args=(-sS -X "$1" "$BASE$2" -H "Authorization: Bearer $RELATIONSAI_API_KEY"
    --max-time 30 -D "$HDR_FILE" -o "$BODY_FILE" -w '%{http_code}')
  if [[ -n "${3:-}" ]]; then args+=(-H "Content-Type: application/json" --data "$3"); fi
  if [[ -n "${4:-}" ]]; then args+=(-H "$4"); fi
  curl "${args[@]}"
}

# Make sure the test contact exists (create or update by phone number).
status="$(call POST /contacts/upsert '{"phone_number": "+12025550100", "name": "asha rao"}')"
if (( status >= 400 )); then fail "$status"; fi
contact_id="$(jq -r '.id' "$BODY_FILE")"
echo "Contact $contact_id: name \"$(jq -r '.name' "$BODY_FILE")\""

# Read-modify-write: the change is computed again from the version just read, on every attempt.
for attempt in 1 2 3; do
  status="$(call GET "/contacts/$contact_id")"
  if (( status >= 400 )); then fail "$status"; fi
  etag="$(header ETag)"
  if [[ -z "$etag" ]]; then echo "The response has no ETag header." >&2; exit 1; fi

  # Capitalise the name, based on the stored value rather than on a copy you read earlier.
  patch="$(jq -c '(.name // "") as $old
    | ($old | split(" ") | map((.[:1] | ascii_upcase) + .[1:]) | join(" ")) as $new
    | if $new == $old then empty else {name: $new} end' "$BODY_FILE")"
  if [[ -z "$patch" ]]; then echo "Nothing to change."; exit 0; fi

  # Send the ETag exactly as received, quotes included.
  status="$(call PATCH "/contacts/$contact_id" "$patch" "If-Match: $etag")"
  if (( status < 400 )); then echo "Updated: name \"$(jq -r '.name' "$BODY_FILE")\""; exit 0; fi
  if [[ "$(error_code)" != "version_conflict" ]]; then fail "$status"; fi
  echo "The contact changed since it was read: reading it again"
done
echo "Gave up after 3 conflicting updates." >&2
exit 1
