#!/usr/bin/env bash
# script/ci-publish-artifact — upload a built sled artifact to a release
# channel and verify the channel's manifest reflects the published commit.
#
# Usage: script/ci-publish-artifact <artifact-path> [git-hash]
#
# git-hash defaults to `git rev-parse --short HEAD` when omitted.
#
# Required env:
#   HUSKIES_CHANNEL_URL    Base URL of the release channel (Gitea secret).
#   HUSKIES_CHANNEL_TOKEN  Bearer token authorised to publish (Gitea secret).
#
# Protocol against the channel host:
#   POST {HUSKIES_CHANNEL_URL}/<artifact-name>
#     Headers: Authorization: Bearer <token>, X-Git-Hash: <hash>
#     Body:    raw artifact bytes
#   GET  {HUSKIES_CHANNEL_URL}/manifest.json
#     Headers: Authorization: Bearer <token>
#     Body:    JSON object with a "git_hash" field
#
# 5xx responses and network errors are retried with backoff; 4xx responses
# (including auth failures) fail immediately since retrying won't fix them.
set -euo pipefail

ARTIFACT_PATH="${1:?Usage: script/ci-publish-artifact <artifact-path> [git-hash]}"
GIT_HASH="${2:-$(git rev-parse --short HEAD)}"

if [ -z "${HUSKIES_CHANNEL_URL:-}" ]; then
  echo "Error: HUSKIES_CHANNEL_URL is not set." >&2
  exit 1
fi
if [ -z "${HUSKIES_CHANNEL_TOKEN:-}" ]; then
  echo "Error: HUSKIES_CHANNEL_TOKEN is not set." >&2
  exit 1
fi
if [ ! -f "$ARTIFACT_PATH" ]; then
  echo "Error: artifact not found at $ARTIFACT_PATH" >&2
  exit 1
fi

ARTIFACT_NAME="$(basename "$ARTIFACT_PATH")"
CHANNEL_URL="${HUSKIES_CHANNEL_URL%/}"
UPLOAD_URL="${CHANNEL_URL}/${ARTIFACT_NAME}"
MANIFEST_URL="${CHANNEL_URL}/manifest.json"

UPLOAD_MAX_ATTEMPTS="${HUSKIES_CI_PUBLISH_MAX_ATTEMPTS:-3}"
UPLOAD_BACKOFF_SECS="${HUSKIES_CI_PUBLISH_BACKOFF_SECS:-1}"
MANIFEST_MAX_ATTEMPTS=3
MANIFEST_BACKOFF_SECS=1

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

# ── Upload ────────────────────────────────────────────────────────────────
attempt=1
while :; do
  echo "==> Uploading ${ARTIFACT_NAME} (${GIT_HASH}), attempt ${attempt}/${UPLOAD_MAX_ATTEMPTS}..."
  HTTP_CODE=$(curl -sS --connect-timeout 10 --max-time 60 \
    -o "$RESPONSE_FILE" -w "%{http_code}" \
    -X POST \
    -H "Authorization: Bearer ${HUSKIES_CHANNEL_TOKEN}" \
    -H "X-Git-Hash: ${GIT_HASH}" \
    --data-binary "@${ARTIFACT_PATH}" \
    "${UPLOAD_URL}") || HTTP_CODE="000"
  RESPONSE_BODY="$(cat "$RESPONSE_FILE" 2>/dev/null || true)"

  case "$HTTP_CODE" in
    2??)
      echo "==> Upload succeeded (HTTP ${HTTP_CODE})."
      break
      ;;
    401|403)
      echo "Error: upload rejected — authentication failed (HTTP ${HTTP_CODE})." >&2
      echo "Response: ${RESPONSE_BODY}" >&2
      exit 1
      ;;
    4??)
      echo "Error: upload rejected by the channel (HTTP ${HTTP_CODE}); not retrying a client error." >&2
      echo "Response: ${RESPONSE_BODY}" >&2
      exit 1
      ;;
  esac

  if [ "$attempt" -ge "$UPLOAD_MAX_ATTEMPTS" ]; then
    echo "Error: upload failed after ${UPLOAD_MAX_ATTEMPTS} attempts (last HTTP ${HTTP_CODE})." >&2
    echo "Response: ${RESPONSE_BODY}" >&2
    exit 1
  fi

  echo "==> Transient failure (HTTP ${HTTP_CODE}); retrying in ${UPLOAD_BACKOFF_SECS}s..."
  sleep "$UPLOAD_BACKOFF_SECS"
  attempt=$((attempt + 1))
  UPLOAD_BACKOFF_SECS=$((UPLOAD_BACKOFF_SECS * 2))
done

# ── Verify manifest ──────────────────────────────────────────────────────
attempt=1
while :; do
  echo "==> Verifying channel manifest reflects ${GIT_HASH} (attempt ${attempt}/${MANIFEST_MAX_ATTEMPTS})..."
  MANIFEST_JSON=$(curl -sS --connect-timeout 10 --max-time 30 \
    -H "Authorization: Bearer ${HUSKIES_CHANNEL_TOKEN}" \
    "${MANIFEST_URL}") || MANIFEST_JSON=""

  MANIFEST_HASH=$(printf '%s' "$MANIFEST_JSON" \
    | python3 -c "import sys,json; print(json.load(sys.stdin).get('git_hash',''))" 2>/dev/null || echo "")

  if [ "$MANIFEST_HASH" = "$GIT_HASH" ]; then
    echo "==> Published ${ARTIFACT_NAME} (${GIT_HASH}) to ${CHANNEL_URL}; manifest verified."
    exit 0
  fi

  if [ "$attempt" -ge "$MANIFEST_MAX_ATTEMPTS" ]; then
    echo "Error: manifest mismatch — channel reports git_hash '${MANIFEST_HASH}', expected '${GIT_HASH}'." >&2
    echo "Manifest: ${MANIFEST_JSON}" >&2
    exit 1
  fi

  sleep "$MANIFEST_BACKOFF_SECS"
  attempt=$((attempt + 1))
  MANIFEST_BACKOFF_SECS=$((MANIFEST_BACKOFF_SECS * 2))
done
