#!/usr/bin/env bash
#
# End-to-end smoke test for the thinkwork CLI's Phase 1+2 commands.
#
# Works fully non-interactively against any deployed stage by relying on the
# CLI's auto-fallback to `api_auth_secret` from `terraform/examples/greenfield/
# terraform.tfvars` (or Lambda env) when no session is cached.
#
# Usage:
#   bash scripts/e2e-cli-smoke.sh dev sleek-squirrel-230
#
#   $1 — stage (default: dev)
#   $2 — tenant slug (default: sleek-squirrel-230)
#
# Exits non-zero on any failure. Each step prints "[OK] <step>" or "[FAIL] <step>".

set -u  # treat unset variables as errors; do NOT set -e so we can summarize all failures

STAGE="${1:-dev}"
TENANT="${2:-sleek-squirrel-230}"
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
CLI="node $ROOT/apps/cli/dist/cli.js"

PASS=0
FAIL=0
FAILED_STEPS=()

step() {
  local name="$1"
  shift
  printf "  · %-60s " "$name"
  local output exit_code
  output=$(eval "$@" 2>&1)
  exit_code=$?
  if [ $exit_code -eq 0 ]; then
    printf "OK\n"
    PASS=$((PASS + 1))
  else
    printf "FAIL\n"
    printf "      └─ %s\n" "$(printf '%s' "$output" | head -5)"
    FAIL=$((FAIL + 1))
    FAILED_STEPS+=("$name")
  fi
}

# A variant of step() that captures output into a variable for downstream use.
step_capture() {
  local var_name="$1"
  local name="$2"
  shift 2
  printf "  · %-60s " "$name"
  local output exit_code
  output=$(eval "$@" 2>&1)
  exit_code=$?
  if [ $exit_code -eq 0 ]; then
    printf "OK\n"
    PASS=$((PASS + 1))
    eval "$var_name=\$output"
  else
    printf "FAIL\n"
    printf "      └─ %s\n" "$(printf '%s' "$output" | head -5)"
    FAIL=$((FAIL + 1))
    FAILED_STEPS+=("$name")
    eval "$var_name=''"
  fi
}

echo ""
echo "================================================================"
echo "  thinkwork CLI e2e smoke test"
echo "  Stage:  $STAGE"
echo "  Tenant: $TENANT"
echo "================================================================"
echo ""
echo "── Phase A · read-only ────────────────────────────────────────"

step "tenant list (admin-ops REST)" "$CLI tenant list --stage $STAGE --json | jq -e '. | length > 0' >/dev/null"

step "tenant get by slug" "$CLI tenant get $TENANT --stage $STAGE --json | jq -e '.slug == \"$TENANT\"' >/dev/null"

step "tenant settings get $TENANT (positional)" "$CLI tenant settings get $TENANT --stage $STAGE --json | jq -e '.tenant.slug == \"$TENANT\"' >/dev/null"

step "member list" "$CLI member list --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "team list" "$CLI team list --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "kb list" "$CLI kb list --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "template list" "$CLI template list --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "agent list" "$CLI agent list --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "thread list" "$CLI thread list --stage $STAGE --tenant $TENANT --limit 3 --json | jq -e '.items | type == \"array\"' >/dev/null"

step "label list" "$CLI label list --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "inbox list" "$CLI inbox list --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

# Phase 3 read-only smoke
step "turn list" "$CLI turn list --stage $STAGE --tenant $TENANT --limit 3 --json | jq -e '.items | type == \"array\"' >/dev/null"

step "wakeup list" "$CLI wakeup list --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "scheduled-job list" "$CLI scheduled-job list --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "webhook list" "$CLI webhook list --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "routine list" "$CLI routine list --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "skill catalog" "$CLI skill catalog --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "skill list" "$CLI skill list --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

# Phase 4+5 read-only smoke
step "recipe list" "$CLI recipe list --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "artifact list" "$CLI artifact list --stage $STAGE --tenant $TENANT --limit 3 --json | jq -e '.items | type == \"array\"' >/dev/null"

step "cost summary" "$CLI cost summary --stage $STAGE --tenant $TENANT --json | jq -e '.totalUsd != null' >/dev/null"

step "cost by-agent" "$CLI cost by-agent --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "cost by-model" "$CLI cost by-model --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "cost series" "$CLI cost series --stage $STAGE --tenant $TENANT --days 7 --json | jq -e '.items | type == \"array\"' >/dev/null"

step "budget list" "$CLI budget list --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "budget status" "$CLI budget status --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"

step "dashboard" "$CLI dashboard --stage $STAGE --tenant $TENANT --json | jq -e '.agents.total != null' >/dev/null"

# Pick a thread + an agent for deeper checks
step_capture THR_ID "thread list → pick first thread" \
  "$CLI thread list --stage $STAGE --tenant $TENANT --limit 1 --json | jq -er '.items[0].id // empty'"

step_capture AGT_ID "agent list → pick first agent" \
  "$CLI agent list --stage $STAGE --tenant $TENANT --json | jq -er '.items[0].id // empty'"

step_capture TPL_ID "template list → pick first template" \
  "$CLI template list --stage $STAGE --tenant $TENANT --json | jq -er '.items[0].id // empty'"

if [ -n "${THR_ID:-}" ]; then
  step "thread get $THR_ID" "$CLI thread get $THR_ID --stage $STAGE --tenant $TENANT --json | jq -e '.id == \"$THR_ID\"' >/dev/null"
  step "message list $THR_ID" "$CLI message list $THR_ID --stage $STAGE --tenant $TENANT --json | jq -e '.messages | type == \"array\"' >/dev/null"
fi

if [ -n "${AGT_ID:-}" ]; then
  step "agent get $AGT_ID" "$CLI agent get $AGT_ID --stage $STAGE --tenant $TENANT --json | jq -e '.id == \"$AGT_ID\"' >/dev/null"
  step "agent version list $AGT_ID" "$CLI agent version list $AGT_ID --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"
  step "agent api-key list $AGT_ID" "$CLI agent api-key list $AGT_ID --stage $STAGE --tenant $TENANT --json | jq -e '.items | type == \"array\"' >/dev/null"
fi

if [ -n "${TPL_ID:-}" ]; then
  step "template get $TPL_ID" "$CLI template get $TPL_ID --stage $STAGE --tenant $TENANT --json | jq -e '.id == \"$TPL_ID\"' >/dev/null"
  if [ -n "${AGT_ID:-}" ]; then
    step "template diff $TPL_ID $AGT_ID" "$CLI template diff $TPL_ID $AGT_ID --stage $STAGE --tenant $TENANT --json | jq -e '.template.id == \"$TPL_ID\"' >/dev/null"
  fi
fi

echo ""
echo "── Phase B · safe mutation round-trips ───────────────────────"
echo "  (uses the api-key auto-fallback bearer; paths that require Cognito"
echo "   user identity stay gated and are exercised in Phase C)"

# Tenant-wide CRUD that the api-key bearer is permitted to do.
SUFFIX=$(date +%s)
LABEL_NAME="e2e-$SUFFIX"

step_capture NEW_LBL "label create '$LABEL_NAME' --color #888888" \
  "$CLI label create '$LABEL_NAME' --color '#888888' --stage $STAGE --tenant $TENANT --json | jq -er '.id // empty'"

if [ -n "${NEW_LBL:-}" ]; then
  step "label update --description (round-trip)" \
    "$CLI label update $NEW_LBL --description 'e2e smoke label' --stage $STAGE --tenant $TENANT --json | jq -e '.id == \"$NEW_LBL\"' >/dev/null"

  step "label delete (cleanup)" \
    "$CLI label delete $NEW_LBL --yes --stage $STAGE --tenant $TENANT --json | jq -e '.deleted == true' >/dev/null"
fi

# Phase B2: tenant-admin mutations that the new `service` auth type (PR #1381)
# now admits via bearer. These used to live in Phase C as "gate fires"
# assertions; they're positive happy-path now.
if [ -n "${AGT_ID:-}" ]; then
  step "agent capabilities set (service-auth) → web-search disabled" \
    "$CLI agent capabilities set $AGT_ID --capability web-search --disabled --stage $STAGE --tenant $TENANT --json | jq -e '.capability == \"web-search\" and .enabled == false' >/dev/null"

  step "agent capabilities set (service-auth) → restore web-search" \
    "$CLI agent capabilities set $AGT_ID --capability web-search --enabled --stage $STAGE --tenant $TENANT --json | jq -e '.capability == \"web-search\" and .enabled == true' >/dev/null"
fi

step "tenant settings set (service-auth) → feature flag round-trip" \
  "$CLI tenant settings set $TENANT --feature e2e_test_$SUFFIX=true --stage $STAGE --json | jq -e '.id != null' >/dev/null"

# Phase B3: admin-write resolvers that PR #1390 broadened from
# requireTenantAdmin (Cognito-only) → requireAdminOrServiceCaller. Each
# is a create + cleanup round-trip so the smoke leaves no residue.

step_capture NEW_TEAM "team create (service-auth) → returns id" \
  "$CLI team create 'e2e-team-$SUFFIX' --description 'smoke' --stage $STAGE --tenant $TENANT --json | jq -er '.id // empty'"

if [ -n "${NEW_TEAM:-}" ]; then
  step "team delete (service-auth) → cleanup" \
    "$CLI team delete $NEW_TEAM --yes --stage $STAGE --tenant $TENANT --json | jq -e '.id == \"$NEW_TEAM\"' >/dev/null"
fi

step_capture NEW_TPL "template create (service-auth) → returns id" \
  "$CLI template create 'e2e-template-$SUFFIX' --description 'smoke' --stage $STAGE --tenant $TENANT --json | jq -er '.id // empty'"

if [ -n "${NEW_TPL:-}" ]; then
  step "template delete (service-auth) → cleanup" \
    "$CLI template delete $NEW_TPL --yes --stage $STAGE --tenant $TENANT --json | jq -e '.deleted == true' >/dev/null"
fi

echo ""
echo "── Phase C · auth-gated mutations that stay gated ─────────────"
echo "  (createThread / sendMessage stay Cognito-required by design —"
echo "   service callers cannot ghost-write as a user. memoryRecords"
echo "   + performance queries still need a declared user identity.)"

# Helper: assert the command fails with the expected error message in stderr.
assert_fails_with() {
  local name="$1"
  local pattern="$2"
  shift 2
  printf "  · %-60s " "$name"
  local output
  output=$(eval "$@" 2>&1) && {
    printf "FAIL (expected error, got success)\n"
    FAIL=$((FAIL + 1))
    FAILED_STEPS+=("$name")
    return
  }
  if printf '%s' "$output" | grep -E -q "$pattern"; then
    printf "OK (gate fired)\n"
    PASS=$((PASS + 1))
  else
    printf "FAIL (wrong error)\n"
    printf "      └─ wanted: %s\n" "$pattern"
    printf "      └─ got:    %s\n" "$(printf '%s' "$output" | head -3)"
    FAIL=$((FAIL + 1))
    FAILED_STEPS+=("$name")
  fi
}

assert_fails_with "thread create requires user identity" \
  "Requester user identity required" \
  "$CLI thread create 'e2e-blocked-$SUFFIX' --stage $STAGE --tenant $TENANT --json"

assert_fails_with "memory list requires a user scope (bearer-only callers blocked)" \
  "User context required|Tenant admin role required|Unexpected error" \
  "$CLI memory list --stage $STAGE --tenant $TENANT --json"

assert_fails_with "performance agents requires a user scope (bearer-only callers blocked)" \
  "User context required|Tenant admin role required|Unexpected error" \
  "$CLI performance agents --stage $STAGE --tenant $TENANT --json"

echo ""
echo "================================================================"
echo "  Summary: $PASS passed, $FAIL failed"
if [ $FAIL -gt 0 ]; then
  echo "  Failed steps:"
  for s in "${FAILED_STEPS[@]}"; do echo "    - $s"; done
fi
echo "================================================================"
echo ""

exit $FAIL
