#!/usr/bin/env bash
#
# walkthrough.sh -- a guided, no-SDK tour of the AgentRouter REST API with plain
# curl. It is a TUTORIAL, not a test: every step echoes what it does, prints the
# curl command it runs, and pretty-prints the JSON response with jq. Nothing is
# asserted -- read along and adapt the calls to your own integration.
#
# It covers the same surface the typed SDKs expose:
#   - whoami                         GET  /v1/me
#   - issue an API key               POST /v1/me/keys           (session only)
#   - catalog reads                  GET  /v1/catalog/{providers,models}
#   - BYOK register / list / revoke  .../aikeys
#   - clients list                   .../clients
#   - MCP catalog + profiles list    /v1/mcp/catalog/servers, .../mcp-profiles
#   - insights logs list + stats     .../request-logs[/stats]
#   - prompt (provider/model + bare) POST .../prompts
#   - session-default prompt         (connect route, empty customer/project)
#   - an RFC 7807 problem+json error (a deliberately bad model)
#
# Prerequisites:
#   curl, jq
#   AGENTROUTER_API_KEY    a credential -- an issued key (looks like
#                          <keyid>.<secret>) or a session token (see README.md
#                          for how to get one).
#   AGENTROUTER_BASE_URL   optional; defaults to https://api.tetrate.ai
#
# Project-scoped steps (BYOK, clients, MCP profiles, insights, explicit-scope
# prompts) need a customer + project. Set these to run them; leave them unset to
# run only the credential-derived default-tenant prompt plus the ungated reads:
#   AGENTROUTER_CUSTOMER_ID   e.g. acme
#   AGENTROUTER_PROJECT_ID    e.g. default
#
# Other optional knobs:
#   AGENTROUTER_WALKTHROUGH_MODEL  model for the explicit-model demos
#                                  (default: openai/gpt-4o-mini).
#   AGENTROUTER_CURL_INSECURE=1    add `curl -k` for the self-signed cert on a
#                                  local dev stack (or set
#                                  CURL_CA_BUNDLE to the dev CA instead).
#
# Run it:
#   export AGENTROUTER_API_KEY=<keyid>.<secret>   # from: tare api keys create
#   bash walkthrough.sh
set -euo pipefail

command -v curl >/dev/null 2>&1 || { echo "walkthrough: curl not found on PATH" >&2; exit 2; }
command -v jq   >/dev/null 2>&1 || { echo "walkthrough: jq not found on PATH"   >&2; exit 2; }

BASE_URL="${AGENTROUTER_BASE_URL:-https://api.tetrate.ai}"
API_KEY="${AGENTROUTER_API_KEY:?set AGENTROUTER_API_KEY (a key from 'tare api keys create', or a session token)}"
CUSTOMER_ID="${AGENTROUTER_CUSTOMER_ID:-}"
PROJECT_ID="${AGENTROUTER_PROJECT_ID:-}"
MODEL="${AGENTROUTER_WALKTHROUGH_MODEL:-openai/gpt-4o-mini}"

INSECURE=()
[[ "${AGENTROUTER_CURL_INSECURE:-0}" == "1" ]] && INSECURE=(-k)

# TOKEN is the Bearer we send. We start with whatever the caller provided, then
# switch to a freshly-issued key after the "issue an API key" step to show that
# an issued key authenticates the rest of the surface exactly the same way.
TOKEN="$API_KEY"

# ---------------------------------------------------------------------------
# Pretty printers. All progress text goes to stdout; read top to bottom.
# ---------------------------------------------------------------------------
rule() { printf '\n========================================================================\n'; }
say()  { printf '\n# %s\n' "$*"; }

# show_curl METHOD PATH [BODY] -- print a copy-pasteable curl command (with the
# token redacted) so you can see exactly what each step sends.
show_curl() {
  local method="$1" path="$2" body="${3:-}"
  printf '  curl -sS -X %s \\\n' "$method"
  printf "    -H 'Authorization: Bearer \$AGENTROUTER_API_KEY' \\\\\n"
  if [[ -n "$body" ]]; then
    printf "    -H 'Content-Type: application/json' \\\\\n"
    printf "    --data '%s' \\\\\n" "$body"
  fi
  printf '    %s%s\n' "$BASE_URL" "$path"
}

# do_req METHOD PATH [BODY] -- run a REST call against $BASE_URL$path with the
# current $TOKEN, leaving the body in $BODY and the numeric status in $STATUS.
# Does NOT use curl --fail: error responses (e.g. the problem+json demo) carry a
# body we want to show, and their non-2xx status must not abort the script.
BODY=""
STATUS=""
do_req() {
  local method="$1" path="$2" data="${3:-}"
  local tmp; tmp="$(mktemp)"
  # ${INSECURE[@]+...} keeps an empty array safe under `set -u` on bash 3.2.
  local -a args=(${INSECURE[@]+"${INSECURE[@]}"} -sS -o "$tmp" -w '%{http_code}'
    -X "$method" -H "Authorization: Bearer ${TOKEN}")
  [[ -n "$data" ]] && args+=(-H "Content-Type: application/json" --data "$data")
  STATUS="$(curl "${args[@]}" "${BASE_URL}${path}")"
  BODY="$(cat "$tmp")"
  rm -f "$tmp"
}

# step DESC METHOD PATH [BODY] -- the workhorse: describe, show the curl, run it,
# print the status, and pretty-print the JSON (raw body if it is not JSON).
step() {
  local desc="$1" method="$2" path="$3" data="${4:-}"
  rule
  say "$desc"
  show_curl "$method" "$path" "$data"
  do_req "$method" "$path" "$data"
  printf '  -> HTTP %s\n' "$STATUS"
  if printf '%s' "$BODY" | jq -e . >/dev/null 2>&1; then
    printf '%s\n' "$BODY" | jq .
  else
    printf '%s\n' "$BODY"
  fi
}

# ---------------------------------------------------------------------------
# Connect-route helper for the session-default prompt. The REST prompt path
# bakes {customer_id}/{project_id} into the URL, so to exercise the
# "resolve my default tenant" behaviour we POST the procedure-style connect
# route with empty ids in the body and the Connect-Protocol-Version header.
# ---------------------------------------------------------------------------
connect_prompt() {
  local desc="$1" body="$2"
  rule
  say "$desc"
  printf '  curl -sS -X POST \\\n'
  printf "    -H 'Authorization: Bearer \$AGENTROUTER_API_KEY' \\\\\n"
  printf "    -H 'Connect-Protocol-Version: 1' \\\\\n"
  printf "    -H 'Content-Type: application/json' \\\\\n"
  printf "    --data '%s' \\\\\n" "$body"
  printf '    %s/tars.inference.v1.InferenceIngressService/Prompt\n' "$BASE_URL"
  local tmp; tmp="$(mktemp)"
  STATUS="$(curl ${INSECURE[@]+"${INSECURE[@]}"} -sS -o "$tmp" -w '%{http_code}' \
    -X POST \
    -H "Authorization: Bearer ${TOKEN}" \
    -H "Connect-Protocol-Version: 1" \
    -H "Content-Type: application/json" \
    --data "$body" \
    "${BASE_URL}/tars.inference.v1.InferenceIngressService/Prompt")"
  BODY="$(cat "$tmp")"; rm -f "$tmp"
  printf '  -> HTTP %s\n' "$STATUS"
  printf '%s\n' "$BODY" | jq .
}

# ===========================================================================
# 1. whoami -- confirm which identity the credential belongs to.
# ===========================================================================
step "whoami: which identity does this credential belong to?" \
  GET "/v1/me"
WHOAMI_ID="$(printf '%s' "$BODY" | jq -r '.identity.id // empty')"
[[ -n "$WHOAMI_ID" ]] && printf '  (authenticated as identity %s)\n' "$WHOAMI_ID"

# ===========================================================================
# 2. issue an API key -- IssueMyKey requires a SESSION credential. If you ran
#    this with a session token, a fresh issued key is minted and used for the
#    rest of the tour. If you ran it with an existing issued key, key issue is
#    not permitted, so we simply keep using the key you provided.
# ===========================================================================
ISSUE_BODY="$(jq -nc --arg name "curl-walkthrough" '{name: $name}')"
step "issue an API key (IssueMyKey -- session credential required)" \
  POST "/v1/me/keys" "$ISSUE_BODY"
ISSUED_SECRET="$(printf '%s' "$BODY" | jq -r '.secret // empty')"
if [[ "$STATUS" == "200" && -n "$ISSUED_SECRET" ]]; then
  TOKEN="$ISSUED_SECRET"
  say "minted a new API key; using it as the Bearer token for the steps below."
else
  say "key issue not available with this credential (status $STATUS); continuing with the supplied credential."
fi

# ===========================================================================
# 3. list keys -- works with either a session token or an issued key.
# ===========================================================================
step "list my API keys (metadata only -- never the plaintext secret)" \
  GET "/v1/me/keys"

# ===========================================================================
# 4. catalog reads -- ungated: any authenticated caller can browse providers
#    and models to discover what to prompt.
# ===========================================================================
step "list catalog providers" \
  GET "/v1/catalog/providers"
step "list catalog models" \
  GET "/v1/catalog/models"

# ===========================================================================
# 5. MCP catalog -- ungated read of the operator's curated MCP server catalog.
# ===========================================================================
step "list MCP catalog servers" \
  GET "/v1/mcp/catalog/servers"

# ===========================================================================
# Project-scoped surface. These paths embed a customer + project, so they run
# only when AGENTROUTER_CUSTOMER_ID / AGENTROUTER_PROJECT_ID are set.
# ===========================================================================
if [[ -n "$CUSTOMER_ID" && -n "$PROJECT_ID" ]]; then
  CP="/v1/customers/${CUSTOMER_ID}/projects/${PROJECT_ID}"

  # --- BYOK: register a provider key, list it, then revoke it. ---
  BYOK_BODY="$(jq -nc \
    --arg provider "PROVIDER_OPENAI" \
    --arg name "curl-walkthrough-byok" \
    --arg secret "sk-not-a-real-key" \
    '{provider: $provider, name: $name, secret: $secret}')"
  step "BYOK: register a provider API key for this project" \
    POST "${CP}/aikeys" "$BYOK_BODY"
  BYOK_ID="$(printf '%s' "$BODY" | jq -r '.key.id // empty')"

  step "BYOK: list this project's provider keys" \
    GET "${CP}/aikeys"

  if [[ -n "$BYOK_ID" ]]; then
    step "BYOK: revoke the key we just registered" \
      DELETE "${CP}/aikeys/${BYOK_ID}"
  fi

  # --- Clients: list the project's gateway clients. ---
  step "clients: list this project's gateway clients" \
    GET "${CP}/clients"

  # --- MCP profiles: list this user's MCP profiles in the project. ---
  step "MCP profiles: list this user's profiles in the project" \
    GET "${CP}/users/${WHOAMI_ID}/mcp-profiles"

  # --- Insights: request-log list + summary stats. ---
  step "insights: list recent request logs (page_size=20)" \
    GET "${CP}/request-logs?page_size=20"
  step "insights: request-log summary stats" \
    GET "${CP}/request-logs/stats"

  # --- Prompt with an explicit provider/model. ---
  PROMPT_BODY="$(jq -nc --arg model "$MODEL" --arg prompt "ping from curl" \
    '{model: $model, prompt: $prompt}')"
  step "prompt: explicit provider/model ($MODEL)" \
    POST "${CP}/prompts" "$PROMPT_BODY"

  # --- Prompt with a bare model name (server resolves the provider). ---
  BARE="${MODEL##*/}"
  BARE_BODY="$(jq -nc --arg model "$BARE" --arg prompt "ping from curl" \
    '{model: $model, prompt: $prompt}')"
  step "prompt: bare model name ($BARE) -- server resolves the provider" \
    POST "${CP}/prompts" "$BARE_BODY"

  # --- RFC 7807: a deliberately bad model. Error responses on the REST surface
  #     are reshaped into application/problem+json with a machine `reason`. ---
  BAD_BODY="$(jq -nc --arg model "no-such-model-xyz" --arg prompt "ping" \
    '{model: $model, prompt: $prompt}')"
  step "error shape: prompt a nonexistent model -> RFC 7807 problem+json" \
    POST "${CP}/prompts" "$BAD_BODY"
  rule
  say "the response above is application/problem+json. Its machine-readable fields:"
  printf '%s\n' "$BODY" | jq '{type, title, status, detail, reason}'
  REASON="$(printf '%s' "$BODY" | jq -r '.reason // empty')"
  [[ -n "$REASON" ]] && printf '  (reason = %s -- branch on this in your client)\n' "$REASON"
else
  rule
  say "AGENTROUTER_CUSTOMER_ID / AGENTROUTER_PROJECT_ID not set -- skipping the"
  say "project-scoped steps (BYOK, clients, MCP profiles, insights, explicit-scope"
  say "prompts). Set them to run those too. Continuing with the default-tenant prompt."
fi

# ===========================================================================
# Session-default prompt -- no customer/project in the call. The server
# resolves the caller's default tenant from the credential's session. This is
# the smallest possible prompt (see quickstart.sh).
# ===========================================================================
DEFAULT_PROMPT_BODY="$(jq -nc --arg model "$MODEL" --arg prompt "ping from curl" \
  '{model: $model, prompt: $prompt}')"
connect_prompt "session-default prompt: no customer/project -- server resolves your default tenant" \
  "$DEFAULT_PROMPT_BODY"

rule
say "done. You drove the AgentRouter REST API end-to-end with nothing but curl + jq."
say "For typed clients (Go / Python / TypeScript), see the per-language SDK examples."
