#!/usr/bin/env bash
set -euo pipefail

: "${KOMMODO_API_TOKEN:?Set KOMMODO_API_TOKEN to a read+write API token}"

FILE=${1:?Usage: ./upload-video.sh /absolute/path/to/video.mp4}
if [ ! -f "$FILE" ]; then
  echo "File not found: $FILE" >&2
  exit 1
fi
if ! command -v jq >/dev/null 2>&1; then
  echo "This example requires jq: https://jqlang.org/download/" >&2
  exit 1
fi

ORIGIN=${KOMMODO_API_BASE_URL:-https://kommodo.ai}
ORIGIN=${ORIGIN%/}
API="$ORIGIN/api/public/v2"
SIZE=$(wc -c < "$FILE" | tr -d ' ')
TITLE=$(basename "$FILE")

if [ "$SIZE" -lt 1 ]; then
  echo "The video file is empty" >&2
  exit 1
fi

# 1. Ask Kommodo for short-lived, signed multipart storage URLs.
INIT=$(curl --fail-with-body --silent --show-error \
  -X POST "$API/uploads" \
  -H "Authorization: Bearer $KOMMODO_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data "$(jq -cn --arg title "$TITLE" --argjson size "$SIZE" \
    '{type:"video", title:$title, size_bytes:$size}')")

UPLOAD_ID=$(jq -er '.upload_id' <<<"$INIT")
RECORDING_ID=$(jq -er '.recording_id' <<<"$INIT")
CHUNK_SIZE=$(jq -er '.upload.chunk_size_bytes' <<<"$INIT")
PROVIDER_COUNT=$(jq -er '.upload.providers | length' <<<"$INIT")
COMPLETION_PATH=$(jq -er '.completion.url' <<<"$INIT")
COMPLETION_TOKEN=$(jq -er '.completion.token' <<<"$INIT")
EXPECTED_PARTS=$(((SIZE + CHUNK_SIZE - 1) / CHUNK_SIZE))

WORK_DIR=$(mktemp -d)
ACTIVE_ABORT_URL=""
cleanup() {
  status=$?
  trap - EXIT HUP INT TERM
  if [ "$status" -ne 0 ] && [ -n "$ACTIVE_ABORT_URL" ]; then
    curl --silent --show-error -X DELETE "$ACTIVE_ABORT_URL" >/dev/null || true
  fi
  rm -r "$WORK_DIR"
  exit "$status"
}
trap cleanup EXIT
trap 'exit 130' HUP INT TERM

# 2. Upload every byte directly to one storage provider. If that provider
# fails, abort it and retry the whole file against the next returned provider.
provider_index=0
storage_complete=false
while [ "$provider_index" -lt "$PROVIDER_COUNT" ]; do
  PART_COUNT=$(jq -er ".upload.providers[$provider_index].upload_parts | length" <<<"$INIT")
  COMPLETE_URL=$(jq -er ".upload.providers[$provider_index].complete_url" <<<"$INIT")
  ACTIVE_ABORT_URL=$(jq -er ".upload.providers[$provider_index].abort_url" <<<"$INIT")

  if [ "$PART_COUNT" -ne "$EXPECTED_PARTS" ]; then
    echo "Kommodo returned an invalid part count" >&2
    exit 1
  fi

  XML="$WORK_DIR/complete.xml"
  printf '%s' '<CompleteMultipartUpload>' > "$XML"
  part_index=0
  provider_ok=true

  while [ "$part_index" -lt "$PART_COUNT" ]; do
    PART_NUMBER=$((part_index + 1))
    PART_URL=$(jq -er ".upload.providers[$provider_index].upload_parts[$part_index].url" <<<"$INIT")
    PART_FILE="$WORK_DIR/part"
    HEADERS="$WORK_DIR/headers"

    dd if="$FILE" of="$PART_FILE" bs="$CHUNK_SIZE" skip="$part_index" count=1 2>/dev/null

    if ! curl --fail-with-body --silent --show-error \
      --retry 2 --retry-delay 1 --retry-connrefused \
      -X PUT "$PART_URL" \
      -H "Content-Type: application/octet-stream" \
      --data-binary "@$PART_FILE" \
      -D "$HEADERS" -o /dev/null; then
      provider_ok=false
      break
    fi

    ETAG=$(tr -d '\r' < "$HEADERS" | \
      sed -n 's/^[Ee][Tt][Aa][Gg]:[[:space:]]*//p' | tail -1)
    if [ -z "$ETAG" ]; then
      echo "Storage omitted the ETag for part $PART_NUMBER" >&2
      provider_ok=false
      break
    fi

    printf '<Part><PartNumber>%s</PartNumber><ETag>%s</ETag></Part>' \
      "$PART_NUMBER" "$ETAG" >> "$XML"
    echo "Uploaded part $PART_NUMBER of $PART_COUNT" >&2
    part_index=$((part_index + 1))
  done

  printf '%s' '</CompleteMultipartUpload>' >> "$XML"
  if [ "$provider_ok" = true ] && \
    curl --fail-with-body --silent --show-error \
      --retry 2 --retry-delay 1 --retry-connrefused \
      -X POST "$COMPLETE_URL" \
      -H "Content-Type: application/xml" \
      --data-binary "@$XML" -o /dev/null; then
    storage_complete=true
    ACTIVE_ABORT_URL=""
    break
  fi

  curl --silent --show-error -X DELETE "$ACTIVE_ABORT_URL" >/dev/null || true
  ACTIVE_ABORT_URL=""
  provider_index=$((provider_index + 1))
done

if [ "$storage_complete" != true ]; then
  echo "Every storage provider failed" >&2
  exit 1
fi

# 3. Tell Kommodo that storage completion succeeded. This queues processing.
case "$COMPLETION_PATH" in
  http://*|https://*) COMPLETION_URL=$COMPLETION_PATH ;;
  *) COMPLETION_URL="$ORIGIN$COMPLETION_PATH" ;;
esac
RESULT=$(curl --fail-with-body --silent --show-error \
  --retry 2 --retry-delay 1 --retry-connrefused \
  -X POST "$COMPLETION_URL" \
  -H "Authorization: Bearer $KOMMODO_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data "$(jq -cn --arg token "$COMPLETION_TOKEN" \
    '{completion_token:$token}')")

jq -n --arg upload_id "$UPLOAD_ID" --arg recording_id "$RECORDING_ID" \
  --argjson result "$RESULT" \
  '{upload_id:$upload_id, recording_id:$recording_id, result:$result}'
