#!/usr/bin/env bash
# Requires curl, jq, Python 3 (binary slicing), and a server-side environment.
set +x
set -euo pipefail
: "${VTN_API_BASE:?Set the HTTPS API base URL}"
: "${VTN_API_KEY:?Read an API key from your server secret store}"
FILE=${1:?Usage: quickstart.sh media.mp4}
umask 077
WORK=$(mktemp -d)
UPLOAD=""
CONFIRMED=false
cleanup() {
  if [[ -n $UPLOAD && $CONFIRMED == false ]]; then api POST "/api/v1/uploads/$UPLOAD/abort" '{}' >/dev/null 2>&1 || true; fi
  rm -rf -- "$WORK"
}
trap cleanup EXIT

# Service credentials go through stdin, never command-line arguments or output.
api() {
  local method=$1 path=$2 body=${3-} identity=${4-} code attempt
  local -a args=()
  [[ -z $identity ]] || args+=(--header "Idempotency-Key: $identity")
  [[ -z $body ]] || args+=(--data "$body")
  for attempt in 1 2 3 4 5 6; do
    code=$(printf 'header = "Authorization: Bearer %s"\n' "$VTN_API_KEY" |
      curl --config - --silent --show-error --proto '=https' --max-time 120 \
        --request "$method" "${VTN_API_BASE%/}$path" \
        --header 'Content-Type: application/json' \
        "${args[@]}" --dump-header "$WORK/headers" \
        --output "$WORK/response" --write-out '%{http_code}')
    if [[ $code == 429 ]]; then
      delay=$(awk 'tolower($1)=="retry-after:" {gsub("\r", "", $2);print $2}' "$WORK/headers")
      [[ $delay =~ ^[0-9]+$ ]] || delay=60
      sleep "$delay"
      continue
    fi
    if [[ $code != 2* ]]; then
      printf 'API request failed: HTTP %s %s\n' "$code" "$(jq -r '.error.code // "HTTP_ERROR"' "$WORK/response")" >&2
      return 1
    fi
    cat "$WORK/response"
    return
  done
  return 1
}

if [[ $FILE == cancel || $FILE == resume ]]; then
  api POST "/api/v1/jobs/${2:?Supply a job ID}/$FILE" '{}'
  exit
fi
if [[ -e api-request.json ]]; then
  printf 'api-request.json already exists. Replay that saved request; do not create a duplicate job.\n' >&2
  exit 1
fi

api GET /api/v1/account > "$WORK/account.json"
jq -e '.eligible and .capabilities.api_admission_enabled' "$WORK/account.json" >/dev/null
SIZE=$(python -c 'import os,sys;print(os.path.getsize(sys.argv[1]))' "$FILE")
EXT=$(python -c 'from pathlib import Path;import sys;print(Path(sys.argv[1]).suffix[1:].lower())' "$FILE")
api POST /api/v1/uploads "$(jq -nc --argjson size "$SIZE" --arg ext "$EXT" '{byte_size:$size,extension:$ext}')" > "$WORK/upload.json"
UPLOAD=$(jq -r .upload_id "$WORK/upload.json")
PART_SIZE=$(jq -r .part_size "$WORK/upload.json")
COUNT=$(jq -r .part_count "$WORK/upload.json")
printf '[]' > "$WORK/parts.json"
for ((NUMBER=1; NUMBER<=COUNT; NUMBER++)); do
  api POST "/api/v1/uploads/$UPLOAD/parts" "{\"part_numbers\":[$NUMBER]}" > "$WORK/target.json"
  python - "$FILE" "$PART_SIZE" "$NUMBER" "$WORK/part" <<'PY'
import sys
with open(sys.argv[1], "rb") as source, open(sys.argv[4], "wb") as target:
    source.seek(int(sys.argv[2]) * (int(sys.argv[3]) - 1))
    target.write(source.read(int(sys.argv[2])))
PY
  # Signed storage URL through stdin; no service Authorization on storage PUT.
  jq -r '"url = " + (.targets[0].url | tojson)' "$WORK/target.json" |
    curl --config - --silent --show-error --fail --proto '=https' --max-time 120 \
      --request PUT --upload-file "$WORK/part" --dump-header "$WORK/put-headers" --output /dev/null
  ETAG=$(awk 'tolower($1)=="etag:" {sub(/^[^:]*:[ ]*/, "");gsub("\r", "");print}' "$WORK/put-headers")
  test -n "$ETAG"
  # The ETag includes its surrounding quotes; jq escapes them without removing them.
  jq --argjson number "$NUMBER" --arg etag "$ETAG" '. + [{part_number:$number,etag:$etag}]' "$WORK/parts.json" > "$WORK/next.json"
  mv "$WORK/next.json" "$WORK/parts.json"
done
api POST "/api/v1/uploads/$UPLOAD/complete" "$(jq -c '{parts:.}' "$WORK/parts.json")" >/dev/null
CONFIRMED=true
IDENTITY=$(python -c 'import uuid;print(uuid.uuid4().hex)')
# Preserve this content-free request identity BEFORE POST; a lost response can be replayed.
jq -n --arg upload "$UPLOAD" --arg key "$IDENTITY" '{upload_id:$upload,idempotency_key:$key}' > api-request.json
api POST /api/v1/jobs "{\"upload_id\":\"$UPLOAD\"}" "$IDENTITY" > "$WORK/job.json"
JOB=$(jq -r .job_id "$WORK/job.json")
printf 'Job: %s\n' "$JOB"
while ! jq -e '.state | IN("completed","completed_with_gaps","interrupted","cancelled","failed","expired")' "$WORK/job.json" >/dev/null; do
  DELAY=$(jq -r '[5,.poll_after_seconds] | max' "$WORK/job.json")
  sleep "$DELAY"
  api GET "/api/v1/jobs/$JOB" > "$WORK/job.json"
done
jq '{job_id,state,charged_seconds,error}' "$WORK/job.json"
if jq -e .result_available "$WORK/job.json" >/dev/null; then
  CURSOR=""
  : > api-results.jsonl
  while :; do
    api GET "/api/v1/jobs/$JOB/result$CURSOR" > "$WORK/page.json"
    jq -c . "$WORK/page.json" >> api-results.jsonl
    NEXT=$(jq -r '.next_cursor // empty' "$WORK/page.json")
    [[ -n $NEXT ]] || break
    CURSOR="?cursor=$(jq -rn --arg cursor "$NEXT" '$cursor|@uri')"
  done
fi
# During processing, cancel from another terminal (use the same stdin-header pattern):
# api POST "/api/v1/jobs/$JOB/cancel" '{}'
# Safe resume, when status.can_resume is true, uses POST .../resume with '{}'.
# Use client.py result JOB --download exports to fetch every artifact shard.
