#!/usr/bin/env bash
# Auth smoke test against a running dev server.
#
#   php artisan serve --port=8000    # in another terminal
#   bash tests/smoke-auth.sh
#
# Uses http://localhost (not 127.0.0.1) because SESSION_DOMAIN=localhost —
# cookies set for that domain are discarded on a mismatched host.

set -u
API="${API:-http://localhost:8000}"
JAR="$(mktemp)"
PASS=0
FAIL=0

cleanup() { rm -f "$JAR" "$JAR.new"; }
trap cleanup EXIT

check() { # name expected actual
  if [ "$2" = "$3" ]; then
    echo "  PASS  $1"
    PASS=$((PASS + 1))
  else
    echo "  FAIL  $1"
    echo "        expected: $2"
    echo "        actual:   $3"
    FAIL=$((FAIL + 1))
  fi
}

# Every request must carry Origin (or Referer): Sanctum's stateful middleware
# uses it to decide whether to start a session at all. Without it the request
# is treated as token-auth and silently has no session — which looks exactly
# like a broken login.
ORIGIN="http://localhost:4321"

# Sanctum's CSRF cookie is URL-encoded in the jar; decode before echoing it
# back in the header.
csrf() {
  curl -s -c "$JAR" -b "$JAR" -o /dev/null \
    -H "Origin: $ORIGIN" "$API/sanctum/csrf-cookie"
  awk '/XSRF-TOKEN/{print $7}' "$JAR" | sed 's/%3D/=/g'
}

api() { # method path [json]
  local method="$1" path="$2" body="${3:-}"
  local token
  token="$(csrf)"
  if [ -n "$body" ]; then
    curl -s -b "$JAR" -c "$JAR" -X "$method" "$API$path" \
      -H "Content-Type: application/json" -H "Accept: application/json" \
      -H "X-XSRF-TOKEN: $token" -H "Origin: $ORIGIN" -d "$body"
  else
    curl -s -b "$JAR" -c "$JAR" -X "$method" "$API$path" \
      -H "Accept: application/json" -H "X-XSRF-TOKEN: $token" \
      -H "Origin: $ORIGIN"
  fi
}

# Status code only, same headers as api().
status() { # method path
  local token
  token="$(csrf)"
  curl -s -o /dev/null -w "%{http_code}" -b "$JAR" -c "$JAR" -X "$1" "$API$2" \
    -H "Accept: application/json" -H "X-XSRF-TOKEN: $token" -H "Origin: $ORIGIN"
}

field() { # json key
  echo "$1" | /c/xampp/php/php.exe -r '
    $in = stream_get_contents(STDIN);
    $d = json_decode($in, true);
    $k = $argv[1];
    $v = $d;
    foreach (explode(".", $k) as $part) {
      $v = is_array($v) && array_key_exists($part, $v) ? $v[$part] : null;
    }
    echo is_bool($v) ? ($v ? "true" : "false") : (string) ($v ?? "null");
  ' "$2"
}

echo "Auth smoke test — $API"
echo

echo "signed out"
r="$(api GET /api/me)"
check "/me returns null user" "null" "$(field "$r" user)"

echo
echo "login"
r="$(api POST /api/login '{"email":"steph@campsteph.test","password":"bride123"}')"
check "bride signs in"          "Stephanie" "$(field "$r" user.name)"
check "role is BRIDE"           "BRIDE"     "$(field "$r" user.role)"
check "next step is dashboard"  "/dashboard" "$(field "$r" next)"
check "hash is not exposed"     "null"      "$(field "$r" user.password_hash)"

echo
echo "session persists"
r="$(api GET /api/me)"
check "/me knows the bride" "Stephanie" "$(field "$r" user.name)"

echo
echo "wrong password"
r="$(api POST /api/login '{"email":"steph@campsteph.test","password":"wrong"}')"
check "rejected with the shared message" \
  "That email/code and password don't match." "$(field "$r" errors.email.0)"

echo
echo "unknown account gives the same message (no enumeration)"
r="$(api POST /api/login '{"email":"nobody@example.com","password":"whatever"}')"
check "same message as a wrong password" \
  "That email/code and password don't match." "$(field "$r" errors.email.0)"

echo
echo "logout"
api POST /api/logout > /dev/null
r="$(api GET /api/me)"
check "/me returns null again" "null" "$(field "$r" user)"

echo
echo "admin"
r="$(api POST /api/login '{"email":"admin@campsteph.test","password":"campfire123"}')"
check "admin signs in" "ADMIN" "$(field "$r" user.role)"

echo
echo "case-insensitive identifier"
api POST /api/logout > /dev/null
r="$(api POST /api/login '{"email":"ADMIN@CampSteph.test","password":"campfire123"}')"
check "uppercase email still matches" "ADMIN" "$(field "$r" user.role)"

# The role gate is the real authorization boundary — middleware and hidden nav
# links are UX. These run against a probe route registered only when
# CAMPSTEPH_PROBE_ROUTES=true, so they're skipped in a normal run.
if [ "$(status GET /api/probe-admin)" != "404" ]; then
  echo
  echo "role gate"
  check "admin reaches an ADMIN route" "200" "$(status GET /api/probe-admin)"

  api POST /api/logout > /dev/null
  api POST /api/login '{"email":"steph@campsteph.test","password":"bride123"}' > /dev/null
  check "bride is refused (403, not 401)" "403" "$(status GET /api/probe-admin)"

  api POST /api/logout > /dev/null
  check "signed out is refused (401)" "401" "$(status GET /api/probe-admin)"
fi

echo
echo "-------------------------"
echo "$PASS passed, $FAIL failed"
[ "$FAIL" -eq 0 ]
