#!/usr/bin/env bash
#
# Space Creation & Member Management API Test
#
# Tests the full lifecycle:
#   1. Create a test space
#   2. Verify space exists
#   3. Add member by EncryptID username (each role)
#   4. List members & verify roles
#   5. Change member role
#   6. Invite by email
#   7. Remove member
#   8. Delete the test space
#
# Usage:
#   ./e2e/tests/space-members-api.sh <AUTH_TOKEN>
#
# Get your token from browser: localStorage.getItem("encryptid_session") → .token
#
# Optionally set:
#   BASE_URL   (default: https://rspace.online)
#   TEST_USER  (default: jeff)  — an existing EncryptID username to add as member

set -euo pipefail

# ── Config ──
TOKEN="${1:-}"
BASE="${BASE_URL:-https://rspace.online}"
TEST_USER="${TEST_USER:-jeff}"
TEST_SLUG="api-test-$(date +%s)"
PASS=0
FAIL=0
WARN=0

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'

if [[ -z "$TOKEN" ]]; then
  echo -e "${RED}Usage: $0 <AUTH_TOKEN>${NC}"
  echo ""
  echo "Get your token from the browser console:"
  echo "  JSON.parse(localStorage.getItem('encryptid_session')).token"
  exit 1
fi

AUTH="Authorization: Bearer $TOKEN"
CT="Content-Type: application/json"

# ── Helpers ──

pass() {
  PASS=$((PASS + 1))
  echo -e "  ${GREEN}PASS${NC} $1"
}

fail() {
  FAIL=$((FAIL + 1))
  echo -e "  ${RED}FAIL${NC} $1"
  if [[ -n "${2:-}" ]]; then
    echo -e "       ${RED}$2${NC}"
  fi
}

warn() {
  WARN=$((WARN + 1))
  echo -e "  ${YELLOW}WARN${NC} $1"
}

assert_status() {
  local label="$1" expected="$2" actual="$3" body="${4:-}"
  if [[ "$actual" == "$expected" ]]; then
    pass "$label (HTTP $actual)"
  else
    fail "$label — expected HTTP $expected, got $actual" "$body"
  fi
}

assert_json_field() {
  local label="$1" json="$2" field="$3" expected="$4"
  local actual
  actual=$(echo "$json" | jq -r "$field" 2>/dev/null || echo "PARSE_ERROR")
  if [[ "$actual" == "$expected" ]]; then
    pass "$label ($field = $actual)"
  else
    fail "$label — $field: expected '$expected', got '$actual'"
  fi
}

api() {
  local method="$1" path="$2"
  shift 2
  curl -s -w "\n%{http_code}" -X "$method" "$BASE$path" -H "$AUTH" "$@"
}

api_with_body() {
  local method="$1" path="$2" body="$3"
  curl -s -w "\n%{http_code}" -X "$method" "$BASE$path" -H "$AUTH" -H "$CT" -d "$body"
}

extract_body() { echo "$1" | sed '$d'; }
extract_status() { echo "$1" | tail -1; }

# ── Preamble ──

echo -e "${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${BOLD}  rSpace — Space Creation & Member Management API Test${NC}"
echo -e "${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo -e "  Base URL:   ${CYAN}$BASE${NC}"
echo -e "  Test slug:  ${CYAN}$TEST_SLUG${NC}"
echo -e "  Test user:  ${CYAN}$TEST_USER${NC}"
echo ""

# ── 0. Verify auth token works ──

echo -e "${BOLD}[0] Verify authentication${NC}"
RES=$(api GET "/api/spaces")
STATUS=$(extract_status "$RES")
BODY=$(extract_body "$RES")
assert_status "GET /api/spaces — token valid" "200" "$STATUS" "$BODY"
echo ""

# ── 1. Create a test space ──

echo -e "${BOLD}[1] Create test space${NC}"
RES=$(api_with_body POST "/api/spaces" "{\"name\":\"API Test Space\",\"slug\":\"$TEST_SLUG\",\"visibility\":\"private\"}")
STATUS=$(extract_status "$RES")
BODY=$(extract_body "$RES")
assert_status "POST /api/spaces — create space" "201" "$STATUS" "$BODY"
assert_json_field "Space slug" "$BODY" ".slug" "$TEST_SLUG"
assert_json_field "Space visibility" "$BODY" ".visibility" "private"
echo ""

# ── 2. Verify space exists ──

echo -e "${BOLD}[2] Verify space exists${NC}"
RES=$(api GET "/api/spaces/$TEST_SLUG")
STATUS=$(extract_status "$RES")
BODY=$(extract_body "$RES")
assert_status "GET /api/spaces/$TEST_SLUG" "200" "$STATUS" "$BODY"
assert_json_field "Space name" "$BODY" ".name" "API Test Space"
echo ""

# ── 3. Cannot create duplicate ──

echo -e "${BOLD}[3] Duplicate slug rejected${NC}"
RES=$(api_with_body POST "/api/spaces" "{\"name\":\"Dupe\",\"slug\":\"$TEST_SLUG\"}")
STATUS=$(extract_status "$RES")
BODY=$(extract_body "$RES")
assert_status "POST /api/spaces — duplicate slug" "409" "$STATUS" "$BODY"
echo ""

# ── 4. Add member by username (as 'viewer') ──

echo -e "${BOLD}[4] Add member by username (viewer)${NC}"
RES=$(api_with_body POST "/api/spaces/$TEST_SLUG/members/add" "{\"username\":\"$TEST_USER\",\"role\":\"viewer\"}")
STATUS=$(extract_status "$RES")
BODY=$(extract_body "$RES")
assert_status "POST /members/add — viewer" "200" "$STATUS" "$BODY"
assert_json_field "Role assigned" "$BODY" ".role" "viewer"

# Capture the DID for later use
MEMBER_DID=$(echo "$BODY" | jq -r '.did // empty' 2>/dev/null)
if [[ -n "$MEMBER_DID" ]]; then
  pass "Got member DID: ${MEMBER_DID:0:24}..."
else
  warn "Could not extract member DID from response"
fi
echo ""

# ── 5. List members — verify viewer is present ──

echo -e "${BOLD}[5] List members${NC}"
RES=$(api GET "/api/spaces/$TEST_SLUG/members")
STATUS=$(extract_status "$RES")
BODY=$(extract_body "$RES")
assert_status "GET /members" "200" "$STATUS" "$BODY"

MEMBER_COUNT=$(echo "$BODY" | jq '.members | length' 2>/dev/null || echo "0")
if [[ "$MEMBER_COUNT" -ge 1 ]]; then
  pass "Members list has $MEMBER_COUNT entries"
else
  fail "Expected at least 1 member, got $MEMBER_COUNT"
fi
echo ""

# ── 6. Change role: viewer → member ──

echo -e "${BOLD}[6] Change role: viewer → member${NC}"
if [[ -n "$MEMBER_DID" ]]; then
  ENCODED_DID=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$MEMBER_DID', safe=''))")
  RES=$(api_with_body PATCH "/api/spaces/$TEST_SLUG/members/$ENCODED_DID" "{\"role\":\"member\"}")
  STATUS=$(extract_status "$RES")
  BODY=$(extract_body "$RES")
  assert_status "PATCH /members/:did — viewer→member" "200" "$STATUS" "$BODY"
  assert_json_field "New role" "$BODY" ".role" "member"
else
  warn "Skipped — no DID captured"
fi
echo ""

# ── 7. Change role: member → admin ──

echo -e "${BOLD}[7] Change role: member → admin${NC}"
if [[ -n "$MEMBER_DID" ]]; then
  RES=$(api_with_body PATCH "/api/spaces/$TEST_SLUG/members/$ENCODED_DID" "{\"role\":\"admin\"}")
  STATUS=$(extract_status "$RES")
  BODY=$(extract_body "$RES")
  assert_status "PATCH /members/:did — member→admin" "200" "$STATUS" "$BODY"
  assert_json_field "New role" "$BODY" ".role" "admin"
else
  warn "Skipped — no DID captured"
fi
echo ""

# ── 8. Change role: admin → viewer (demote) ──

echo -e "${BOLD}[8] Demote role: admin → viewer${NC}"
if [[ -n "$MEMBER_DID" ]]; then
  RES=$(api_with_body PATCH "/api/spaces/$TEST_SLUG/members/$ENCODED_DID" "{\"role\":\"viewer\"}")
  STATUS=$(extract_status "$RES")
  BODY=$(extract_body "$RES")
  assert_status "PATCH /members/:did — admin→viewer" "200" "$STATUS" "$BODY"
  assert_json_field "New role" "$BODY" ".role" "viewer"
else
  warn "Skipped — no DID captured"
fi
echo ""

# ── 9. Invalid role rejected ──

echo -e "${BOLD}[9] Invalid role rejected${NC}"
if [[ -n "$MEMBER_DID" ]]; then
  RES=$(api_with_body PATCH "/api/spaces/$TEST_SLUG/members/$ENCODED_DID" "{\"role\":\"superadmin\"}")
  STATUS=$(extract_status "$RES")
  BODY=$(extract_body "$RES")
  assert_status "PATCH /members/:did — invalid role" "400" "$STATUS" "$BODY"
else
  warn "Skipped — no DID captured"
fi
echo ""

# ── 10. Invite by email ──

echo -e "${BOLD}[10] Invite by email${NC}"
RES=$(api_with_body POST "/api/spaces/$TEST_SLUG/invite" "{\"email\":\"test@example.com\",\"role\":\"member\"}")
STATUS=$(extract_status "$RES")
BODY=$(extract_body "$RES")
# May be 200 (ok) or 500 (SMTP not configured) — both indicate the route works
if [[ "$STATUS" == "200" ]]; then
  pass "POST /invite — email invite created (HTTP $STATUS)"
  INVITE_URL=$(echo "$BODY" | jq -r '.inviteUrl // empty' 2>/dev/null)
  if [[ -n "$INVITE_URL" ]]; then
    pass "Invite URL generated: ${INVITE_URL:0:50}..."
  fi
elif [[ "$STATUS" == "500" ]]; then
  warn "POST /invite — SMTP not configured (HTTP 500, expected in dev)"
else
  fail "POST /invite — unexpected HTTP $STATUS" "$BODY"
fi
echo ""

# ── 11. Invite with invalid role rejected ──

echo -e "${BOLD}[11] Invite with invalid role rejected${NC}"
RES=$(api_with_body POST "/api/spaces/$TEST_SLUG/invite" "{\"email\":\"test@example.com\",\"role\":\"overlord\"}")
STATUS=$(extract_status "$RES")
BODY=$(extract_body "$RES")
assert_status "POST /invite — invalid role" "400" "$STATUS" "$BODY"
echo ""

# ── 12. Add member by nonexistent username ──

echo -e "${BOLD}[12] Add nonexistent username${NC}"
RES=$(api_with_body POST "/api/spaces/$TEST_SLUG/members/add" "{\"username\":\"nonexistent-user-xyz-99999\"}")
STATUS=$(extract_status "$RES")
BODY=$(extract_body "$RES")
assert_status "POST /members/add — nonexistent user" "404" "$STATUS" "$BODY"
echo ""

# ── 13. Remove member ──

echo -e "${BOLD}[13] Remove member${NC}"
if [[ -n "$MEMBER_DID" ]]; then
  RES=$(api DELETE "/api/spaces/$TEST_SLUG/members/$ENCODED_DID")
  STATUS=$(extract_status "$RES")
  BODY=$(extract_body "$RES")
  assert_status "DELETE /members/:did" "200" "$STATUS" "$BODY"
else
  warn "Skipped — no DID captured"
fi
echo ""

# ── 14. Verify member removed ──

echo -e "${BOLD}[14] Verify member removed${NC}"
RES=$(api GET "/api/spaces/$TEST_SLUG/members")
STATUS=$(extract_status "$RES")
BODY=$(extract_body "$RES")
assert_status "GET /members after removal" "200" "$STATUS" "$BODY"
# Should have 0 non-owner members (owner isn't in members by default)
MEMBER_COUNT=$(echo "$BODY" | jq '.members | length' 2>/dev/null || echo "?")
pass "Members after removal: $MEMBER_COUNT"
echo ""

# ── 15. Re-add as admin for multi-role verification ──

echo -e "${BOLD}[15] Add member as admin${NC}"
RES=$(api_with_body POST "/api/spaces/$TEST_SLUG/members/add" "{\"username\":\"$TEST_USER\",\"role\":\"admin\"}")
STATUS=$(extract_status "$RES")
BODY=$(extract_body "$RES")
assert_status "POST /members/add — admin role" "200" "$STATUS" "$BODY"
assert_json_field "Role assigned" "$BODY" ".role" "admin"
echo ""

# ── 16. Re-add as moderator (overwrite) ──

echo -e "${BOLD}[16] Overwrite role via add (admin → moderator)${NC}"
RES=$(api_with_body POST "/api/spaces/$TEST_SLUG/members/add" "{\"username\":\"$TEST_USER\",\"role\":\"moderator\"}")
STATUS=$(extract_status "$RES")
BODY=$(extract_body "$RES")
assert_status "POST /members/add — moderator overwrite" "200" "$STATUS" "$BODY"
assert_json_field "Role assigned" "$BODY" ".role" "moderator"
echo ""

# ── 17. Unauthenticated access denied ──

echo -e "${BOLD}[17] Unauthenticated access denied${NC}"
RES=$(curl -s -w "\n%{http_code}" -X POST "$BASE/api/spaces" -H "$CT" -d '{"name":"Nope","slug":"nope"}')
STATUS=$(extract_status "$RES")
assert_status "POST /api/spaces — no auth" "401" "$STATUS"
echo ""

# ── 18. Cleanup: remove member, then delete space ──

echo -e "${BOLD}[18] Cleanup — remove member & delete space${NC}"
if [[ -n "$MEMBER_DID" ]]; then
  api DELETE "/api/spaces/$TEST_SLUG/members/$ENCODED_DID" > /dev/null 2>&1 || true
fi
RES=$(api DELETE "/api/spaces/$TEST_SLUG")
STATUS=$(extract_status "$RES")
BODY=$(extract_body "$RES")
assert_status "DELETE /api/spaces/$TEST_SLUG" "200" "$STATUS" "$BODY"
echo ""

# ── 19. Verify space gone ──

echo -e "${BOLD}[19] Verify space deleted${NC}"
RES=$(api GET "/api/spaces/$TEST_SLUG")
STATUS=$(extract_status "$RES")
assert_status "GET deleted space" "404" "$STATUS"
echo ""

# ── Summary ──

TOTAL=$((PASS + FAIL))
echo -e "${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${BOLD}  Results: ${GREEN}$PASS passed${NC} / ${RED}$FAIL failed${NC} / ${YELLOW}$WARN warnings${NC} (${TOTAL} total)"
echo -e "${BOLD}${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"

if [[ $FAIL -gt 0 ]]; then
  exit 1
fi
