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

# This check proves the skill pack by calling the live QTI surface. It does not
# parse QTI, compute scores, validate XML, replay idempotency, or implement
# response processing locally.

BASE_URL="${QTI_BASE_URL:-https://platform3-andymontgomery-9773s-projects.vercel.app/qti/1edtech/implementation/api}"
RUN_ID="${QTI_RUN_ID:-skill-check-$(date -u +%Y%m%dT%H%M%SZ)-$$}"
WORK_DIR="${QTI_CHECK_WORK_DIR:-${TMPDIR:-/tmp}/qti-surface-check-$RUN_ID}"
mkdir -p "$WORK_DIR"

need() {
  command -v "$1" >/dev/null 2>&1 || {
    printf 'Missing required command: %s\n' "$1" >&2
    exit 127
  }
}

fail() {
  printf 'QTI surface check failed: %s\n' "$1" >&2
  printf 'Evidence directory: %s\n' "$WORK_DIR" >&2
  exit 1
}

need curl
need jq
need awk
need wc
need tr

curl_json() {
  curl -fsSL "$@"
}

curl_json "$BASE_URL" -o "$WORK_DIR/service-descriptor.json"
jq -e '
  (.quickstart.operationOrder | index("ingestContentPackage")) != null and
  (.quickstart.operationOrder | index("submitAttempt")) != null and
  (.quickstart.operationOrder | index("runConformance")) != null and
  (.endpoints.ingestContentPackage == "/tenants/{tenantId}/qti/packages")
' "$WORK_DIR/service-descriptor.json" >/dev/null \
  || fail "service descriptor is missing required QTI operations"

if [[ -n "${QTI_TOKEN:-}" && -n "${QTI_TENANT_ID:-}" ]]; then
  TOKEN="$QTI_TOKEN"
  TENANT_ID="$QTI_TENANT_ID"
  CANDIDATE_REF="${QTI_CANDIDATE_REF:?QTI_CANDIDATE_REF is required when using a real tenant token}"
  DEMO_PACKAGE_ZIP_URL="${QTI_PACKAGE_ZIP_URL:-/demo/qti-package.zip?runId=$RUN_ID}"
  TOKEN_SOURCE="operator-secret"
else
  DEMO_TOKEN_RESPONSE="$(curl_json -X POST "$BASE_URL/dev/mint?tenantId=demo&runId=$RUN_ID")"
  printf '%s' "$DEMO_TOKEN_RESPONSE" > "$WORK_DIR/demo-token-redacted.json"
  TOKEN="$(printf '%s' "$DEMO_TOKEN_RESPONSE" | jq -r '.token')"
  TENANT_ID="$(printf '%s' "$DEMO_TOKEN_RESPONSE" | jq -r '.tenantId')"
  CANDIDATE_REF="$(printf '%s' "$DEMO_TOKEN_RESPONSE" | jq -r '.demoCandidateRef')"
  DEMO_PACKAGE_ZIP_URL="$(printf '%s' "$DEMO_TOKEN_RESPONSE" | jq -r '.demoPackageZipUrl')"
  jq 'del(.token)' "$WORK_DIR/demo-token-redacted.json" > "$WORK_DIR/demo-token.json"
  rm -f "$WORK_DIR/demo-token-redacted.json"
  TOKEN_SOURCE="demo-mint"
fi

[[ -n "$TOKEN" && "$TOKEN" != "null" ]] || fail "token was not available"
[[ -n "$TENANT_ID" && "$TENANT_ID" != "null" ]] || fail "tenant id was not available"
[[ -n "$CANDIDATE_REF" && "$CANDIDATE_REF" != "null" ]] || fail "candidate ref was not available"

curl -fsSL "$BASE_URL$DEMO_PACKAGE_ZIP_URL" -o "$WORK_DIR/qti-package.zip"

curl_json -X POST "$BASE_URL/tenants/$TENANT_ID/qti/packages" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: pkg-upload-$RUN_ID" \
  -H "Content-Type: application/zip" \
  -H "X-QTI-Profile: qti-3.0" \
  --data-binary @"$WORK_DIR/qti-package.zip" \
  -o "$WORK_DIR/qti-ingest.json"

PACKAGE_ID="$(jq -r '.packageId' "$WORK_DIR/qti-ingest.json")"
ARTIFACT_ID="$(jq -r '.artifacts[0].artifactId' "$WORK_DIR/qti-ingest.json")"
ARTIFACT_VERSION_ID="$(jq -r '.artifacts[0].artifactVersionId' "$WORK_DIR/qti-ingest.json")"
[[ "$PACKAGE_ID" != "null" && "$ARTIFACT_ID" != "null" && "$ARTIFACT_VERSION_ID" != "null" ]] \
  || fail "ingest did not return package/artifact/version identifiers"

curl_json "$BASE_URL/tenants/$TENANT_ID/qti/packages?limit=5" \
  -H "Authorization: Bearer $TOKEN" \
  -o "$WORK_DIR/qti-packages-list.json"
curl_json "$BASE_URL/tenants/$TENANT_ID/qti/artifacts?limit=5" \
  -H "Authorization: Bearer $TOKEN" \
  -o "$WORK_DIR/qti-artifacts-list.json"
curl_json "$BASE_URL/tenants/$TENANT_ID/qti/artifact-versions?limit=5" \
  -H "Authorization: Bearer $TOKEN" \
  -o "$WORK_DIR/qti-artifact-versions-list.json"
jq -e 'has("items") and (.items | type == "array") and has("nextCursor")' "$WORK_DIR/qti-packages-list.json" >/dev/null \
  || fail "package list response did not match the surface list shape"
jq -e 'has("items") and (.items | type == "array") and has("nextCursor")' "$WORK_DIR/qti-artifacts-list.json" >/dev/null \
  || fail "artifact list response did not match the surface list shape"
jq -e 'has("items") and (.items | type == "array") and has("nextCursor")' "$WORK_DIR/qti-artifact-versions-list.json" >/dev/null \
  || fail "artifact version list response did not match the surface list shape"

curl_json "$BASE_URL/tenants/$TENANT_ID/qti/artifact-versions/$ARTIFACT_VERSION_ID/delivery-json" \
  -H "Authorization: Bearer $TOKEN" \
  -o "$WORK_DIR/qti-delivery.json"
jq -e '.documentId and .qti' "$WORK_DIR/qti-delivery.json" >/dev/null \
  || fail "delivery JSON did not include documentId and qti"

curl -fsSL -D "$WORK_DIR/qti-authoring-read.headers" \
  "$BASE_URL/tenants/$TENANT_ID/qti/artifacts/$ARTIFACT_ID/authoring-json" \
  -H "Authorization: Bearer $TOKEN" \
  -o "$WORK_DIR/qti-authoring-read.json"
ETAG="$(awk 'BEGIN{IGNORECASE=1} /^etag:/ {sub(/\r$/, "", $2); print $2}' "$WORK_DIR/qti-authoring-read.headers")"
[[ -n "$ETAG" ]] || fail "authoring read did not return an ETag"

curl_json -X PUT "$BASE_URL/tenants/$TENANT_ID/qti/artifacts/$ARTIFACT_ID/authoring-json" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "If-Match: $ETAG" \
  --data-binary @"$WORK_DIR/qti-authoring-read.json" \
  -o "$WORK_DIR/qti-save.json"
SAVED_ARTIFACT_VERSION_ID="$(jq -r '.artifactVersionId' "$WORK_DIR/qti-save.json")"
[[ "$SAVED_ARTIFACT_VERSION_ID" != "null" ]] || fail "authoring save did not return an artifactVersionId"

curl -fsSL -D "$WORK_DIR/qti-xml.headers" \
  "$BASE_URL/tenants/$TENANT_ID/qti/artifact-versions/$SAVED_ARTIFACT_VERSION_ID/xml" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/xml" \
  -o "$WORK_DIR/qti-export.xml"
XML_BYTES="$(wc -c < "$WORK_DIR/qti-export.xml" | tr -d ' ')"
[[ "$XML_BYTES" =~ ^[0-9]+$ && "$XML_BYTES" -gt 0 ]] || fail "XML export was empty"

curl_json -X POST "$BASE_URL/tenants/$TENANT_ID/qti/delivery-sessions" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"candidateRef\":\"$CANDIDATE_REF\",\"artifactVersionId\":\"$SAVED_ARTIFACT_VERSION_ID\"}" \
  -o "$WORK_DIR/qti-session.json"
DELIVERY_SESSION_ID="$(jq -r '.deliverySessionId' "$WORK_DIR/qti-session.json")"
[[ "$DELIVERY_SESSION_ID" != "null" ]] || fail "delivery session was not created"

curl_json -X POST "$BASE_URL/tenants/$TENANT_ID/qti/delivery-sessions/$DELIVERY_SESSION_ID/attempts" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"artifactVersionId\":\"$SAVED_ARTIFACT_VERSION_ID\",\"responses\":{\"RESPONSE\":\"ChoiceA\"}}" \
  -o "$WORK_DIR/qti-attempt.json"
SCORE="$(jq -r '.outcomeState.SCORE' "$WORK_DIR/qti-attempt.json")"
TRACE_EVENT="$(jq -r '.processingTrace[0].event // .processingTrace[0].rule' "$WORK_DIR/qti-attempt.json")"
[[ "$SCORE" == "1" ]] || fail "surface returned unexpected score: $SCORE"
[[ "$TRACE_EVENT" == "match-correct" ]] || fail "surface returned unexpected processing trace: $TRACE_EVENT"

curl_json "$BASE_URL/tenants/$TENANT_ID/qti/candidates/$CANDIDATE_REF/runtime-data" \
  -H "Authorization: Bearer $TOKEN" \
  -o "$WORK_DIR/qti-runtime-data.json"
SESSIONS="$(jq -r '.deliverySessionCount' "$WORK_DIR/qti-runtime-data.json")"
ATTEMPTS="$(jq -r '.attemptCount' "$WORK_DIR/qti-runtime-data.json")"
[[ "$SESSIONS" =~ ^[0-9]+$ && "$SESSIONS" -ge 1 ]] || fail "runtime data did not include the created session"
[[ "$ATTEMPTS" =~ ^[0-9]+$ && "$ATTEMPTS" -ge 1 ]] || fail "runtime data did not include the created attempt"

DELETE_STATUS="$(curl -sS -o "$WORK_DIR/qti-delete-body.txt" -w "%{http_code}" -X DELETE \
  "$BASE_URL/tenants/$TENANT_ID/qti/candidates/$CANDIDATE_REF/runtime-data" \
  -H "Authorization: Bearer $TOKEN")"
[[ "$DELETE_STATUS" == "204" ]] || fail "runtime-data delete returned HTTP $DELETE_STATUS"

curl_json -X POST "$BASE_URL/qti/conformance-runs" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  --data '{}' \
  -o "$WORK_DIR/qti-conformance.json"
CONFORMANCE_STATUS="$(jq -r '.status' "$WORK_DIR/qti-conformance.json")"
FAILED_ASSERTIONS="$(jq -r '.summary.byStatus.failed // 0' "$WORK_DIR/qti-conformance.json")"
ERROR_ASSERTIONS="$(jq -r '.summary.byStatus.error // 0' "$WORK_DIR/qti-conformance.json")"
[[ "$CONFORMANCE_STATUS" == "passed" ]] || fail "conformance status was $CONFORMANCE_STATUS"
[[ "$FAILED_ASSERTIONS" == "0" && "$ERROR_ASSERTIONS" == "0" ]] || fail "conformance returned failed or error assertions"

jq -n \
  --arg runId "$RUN_ID" \
  --arg baseUrl "$BASE_URL" \
  --arg tokenSource "$TOKEN_SOURCE" \
  --arg tenantId "$TENANT_ID" \
  --arg packageId "$PACKAGE_ID" \
  --arg artifactId "$ARTIFACT_ID" \
  --arg artifactVersionId "$SAVED_ARTIFACT_VERSION_ID" \
  --arg deliverySessionId "$DELIVERY_SESSION_ID" \
  --arg score "$SCORE" \
  --arg traceEvent "$TRACE_EVENT" \
  --arg sessions "$SESSIONS" \
  --arg attempts "$ATTEMPTS" \
  --arg deleteStatus "$DELETE_STATUS" \
  --arg xmlBytes "$XML_BYTES" \
  --arg conformanceStatus "$CONFORMANCE_STATUS" \
  --arg byStatus "$(jq -c '.summary.byStatus' "$WORK_DIR/qti-conformance.json")" \
  --arg evidenceDir "$WORK_DIR" \
  '{
    runId:$runId,
    baseUrl:$baseUrl,
    tokenSource:$tokenSource,
    tenantId:$tenantId,
    packageId:$packageId,
    artifactId:$artifactId,
    artifactVersionId:$artifactVersionId,
    deliverySessionId:$deliverySessionId,
    result:{score:$score, traceEvent:$traceEvent},
    runtime:{sessions:$sessions, attempts:$attempts, deleteStatus:$deleteStatus},
    xmlBytes:$xmlBytes,
    conformance:{status:$conformanceStatus, byStatus:($byStatus|fromjson)},
    evidenceDir:$evidenceDir,
    jwt:"redacted"
  }' | tee "$WORK_DIR/check-summary.json"

printf '\nQTI surface check passed. Evidence directory: %s\n' "$WORK_DIR" >&2
