#!/usr/bin/env bash
# =============================================================================
# tests/auth_test.sh – Twosome auth endpoint smoke tests
#
# Usage:
#   chmod +x tests/auth_test.sh
#   ./tests/auth_test.sh https://api.yourdomain.com
#
# Requires: curl, jq (optional – falls back to raw JSON if jq is missing)
#
# Each run uses a unique phone number derived from the current timestamp
# so you can re-run without "already registered" conflicts.
# =============================================================================

set -euo pipefail

BASE="${1:-https://api.yourdomain.com}"
BASE="${BASE%/}"          # strip trailing slash

# ── Unique phone for this run (last 9 digits of unix timestamp, prefix 91) ──
TS=$(date +%s)
PHONE="+919${TS: -9}"     # e.g. +91917234567890  (10 local digits)

# ── Colour helpers ────────────────────────────────────────────────────────────
GREEN='\033[0;32m'; RED='\033[0;31m'; CYAN='\033[0;36m'; NC='\033[0m'

pass() { echo -e "${GREEN}✓ PASS${NC}  $1"; }
fail() { echo -e "${RED}✗ FAIL${NC}  $1"; FAILURES=$((FAILURES+1)); }
info() { echo -e "${CYAN}──${NC} $1"; }

FAILURES=0

# ── jq or cat ─────────────────────────────────────────────────────────────────
PP="cat"
command -v jq &>/dev/null && PP="jq ."

# ── Helper: POST with JSON body, print pretty, return raw ────────────────────
post() {
    local url="$1"
    local data="$2"
    curl -s -X POST \
         -H "Content-Type: application/json" \
         -d "$data" \
         "$url"
}

auth_post() {
    local url="$1"
    local token="$2"
    local data="$3"
    curl -s -X POST \
         -H "Content-Type: application/json" \
         -H "Authorization: Bearer $token" \
         -d "$data" \
         "$url"
}

auth_delete() {
    local url="$1"
    local token="$2"
    curl -s -X DELETE \
         -H "Authorization: Bearer $token" \
         "$url"
}

echo ""
info "Base URL : $BASE"
info "Test phone: $PHONE"
echo ""

# =============================================================================
# 1. Health check
# =============================================================================
info "1. GET /v1/health"
RESP=$(curl -s "$BASE/v1/health")
echo "$RESP" | $PP
if echo "$RESP" | grep -q '"ok":true'; then
    pass "Health endpoint returned {ok:true}"
else
    fail "Health endpoint did not return expected response"
fi
echo ""

# =============================================================================
# 2. Register – missing confirm_age_18 (expect 422)
# =============================================================================
info "2. POST /v1/auth/register – missing confirm_age_18"
RESP=$(post "$BASE/v1/auth/register" \
    '{"phone":"'"$PHONE"'","password":"SecurePass1","display_name":"Alice","public_key":"dGVzdHB1YmxpY2tleXRlc3RwdWJsaWNrZXl0ZXN0cGs="}')
echo "$RESP" | $PP
CODE=$(echo "$RESP" | grep -o '"code":"[^"]*"' | head -1 | cut -d'"' -f4)
if [ "$CODE" = "VALIDATION_ERROR" ]; then
    pass "Rejected registration without confirm_age_18"
else
    fail "Expected VALIDATION_ERROR, got: $CODE"
fi
echo ""

# =============================================================================
# 3. Register – bad public key (expect 422)
# =============================================================================
info "3. POST /v1/auth/register – invalid public_key"
RESP=$(post "$BASE/v1/auth/register" \
    '{"phone":"'"$PHONE"'","password":"SecurePass1","display_name":"Alice","confirm_age_18":true,"public_key":"tooshort"}')
echo "$RESP" | $PP
CODE=$(echo "$RESP" | grep -o '"code":"[^"]*"' | head -1 | cut -d'"' -f4)
if [ "$CODE" = "VALIDATION_ERROR" ]; then
    pass "Rejected invalid public_key"
else
    fail "Expected VALIDATION_ERROR, got: $CODE"
fi
echo ""

# =============================================================================
# 4. Register – common password (expect 422)
# =============================================================================
info "4. POST /v1/auth/register – common password"
RESP=$(post "$BASE/v1/auth/register" \
    '{"phone":"'"$PHONE"'","password":"password","display_name":"Alice","confirm_age_18":true,"public_key":"dGVzdHB1YmxpY2tleXRlc3RwdWJsaWNrZXl0ZXN0cGs="}')
echo "$RESP" | $PP
CODE=$(echo "$RESP" | grep -o '"code":"[^"]*"' | head -1 | cut -d'"' -f4)
if [ "$CODE" = "VALIDATION_ERROR" ]; then
    pass "Rejected common password"
else
    fail "Expected VALIDATION_ERROR, got: $CODE"
fi
echo ""

# =============================================================================
# 5. Register – success (captures tokens and recovery code)
#
# NOTE: The public_key below is a dummy 32-byte key (base64url).
#       The real app generates a proper X25519 key with libsodium.
#       "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" = 32 zero-bytes, base64
# =============================================================================
info "5. POST /v1/auth/register – success"
PUB_KEY="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"   # 32 bytes, base64url, no padding
RESP=$(post "$BASE/v1/auth/register" \
    "{\"phone\":\"$PHONE\",\"password\":\"MyStr0ngPass!\",\"display_name\":\"Alice\",\"confirm_age_18\":true,\"public_key\":\"$PUB_KEY\"}")
echo "$RESP" | $PP

ACCESS_TOKEN=$(echo  "$RESP" | grep -o '"access_token":"[^"]*"'  | cut -d'"' -f4)
REFRESH_TOKEN=$(echo "$RESP" | grep -o '"refresh_token":"[^"]*"' | cut -d'"' -f4)
RECOVERY_CODE=$(echo "$RESP" | grep -o '"recovery_code":"[^"]*"' | cut -d'"' -f4)
USER_ID=$(echo       "$RESP" | grep -o '"id":[0-9]*'             | head -1 | cut -d':' -f2)

if [ -n "$ACCESS_TOKEN" ] && [ -n "$RECOVERY_CODE" ]; then
    pass "Registered successfully. User ID: $USER_ID"
    info "Recovery code: $RECOVERY_CODE (save this!)"
else
    fail "Registration response missing tokens or recovery_code"
    echo "Aborting remaining tests."
    exit 1
fi
echo ""

# =============================================================================
# 6. Register – duplicate phone (expect 409)
# =============================================================================
info "6. POST /v1/auth/register – duplicate phone"
RESP=$(post "$BASE/v1/auth/register" \
    "{\"phone\":\"$PHONE\",\"password\":\"AnotherPass9!\",\"display_name\":\"Alice2\",\"confirm_age_18\":true,\"public_key\":\"$PUB_KEY\"}")
echo "$RESP" | $PP
CODE=$(echo "$RESP" | grep -o '"code":"[^"]*"' | head -1 | cut -d'"' -f4)
if [ "$CODE" = "REGISTRATION_FAILED" ]; then
    pass "Rejected duplicate phone with generic error"
else
    fail "Expected REGISTRATION_FAILED, got: $CODE"
fi
echo ""

# =============================================================================
# 7. Login – success
# =============================================================================
info "7. POST /v1/auth/login – success"
RESP=$(post "$BASE/v1/auth/login" \
    "{\"phone_or_email\":\"$PHONE\",\"password\":\"MyStr0ngPass!\"}")
echo "$RESP" | $PP

LOGIN_ACCESS=$(echo  "$RESP" | grep -o '"access_token":"[^"]*"'  | cut -d'"' -f4)
LOGIN_REFRESH=$(echo "$RESP" | grep -o '"refresh_token":"[^"]*"' | cut -d'"' -f4)

if [ -n "$LOGIN_ACCESS" ]; then
    pass "Login successful"
    # Use login tokens for subsequent tests
    ACCESS_TOKEN="$LOGIN_ACCESS"
    REFRESH_TOKEN="$LOGIN_REFRESH"
else
    fail "Login did not return access_token"
fi
echo ""

# =============================================================================
# 8. Login – wrong password (expect 401, generic error)
# =============================================================================
info "8. POST /v1/auth/login – wrong password"
RESP=$(post "$BASE/v1/auth/login" \
    "{\"phone_or_email\":\"$PHONE\",\"password\":\"WrongPassword99!\"}")
echo "$RESP" | $PP
CODE=$(echo "$RESP" | grep -o '"code":"[^"]*"' | head -1 | cut -d'"' -f4)
if [ "$CODE" = "AUTH_FAILED" ]; then
    pass "Wrong password rejected with generic AUTH_FAILED"
else
    fail "Expected AUTH_FAILED, got: $CODE"
fi
echo ""

# =============================================================================
# 9. Login – unknown phone (expect 401, same generic error)
# =============================================================================
info "9. POST /v1/auth/login – unknown phone"
RESP=$(post "$BASE/v1/auth/login" \
    '{"phone_or_email":"+919000000001","password":"AnyPassword1!"}')
echo "$RESP" | $PP
CODE=$(echo "$RESP" | grep -o '"code":"[^"]*"' | head -1 | cut -d'"' -f4)
if [ "$CODE" = "AUTH_FAILED" ]; then
    pass "Unknown phone returns same AUTH_FAILED (no enumeration)"
else
    fail "Expected AUTH_FAILED, got: $CODE"
fi
echo ""

# =============================================================================
# 9b. POST /v1/auth/login – rate limit (5 attempts / 10 min per phone)
# =============================================================================
info "9b. POST /v1/auth/login – test rate limit per identifier"
RATE_PHONE="+919999988888"
for i in {1..5}; do
    post "$BASE/v1/auth/login" "{\"phone_or_email\":\"$RATE_PHONE\",\"password\":\"Pass$i!\"}" > /dev/null
done
RESP_RATE=$(post "$BASE/v1/auth/login" "{\"phone_or_email\":\"$RATE_PHONE\",\"password\":\"ExceedLimit!\"}")
echo "$RESP_RATE" | $PP
RATE_CODE=$(echo "$RESP_RATE" | grep -o '"code":"[^"]*"' | head -1 | cut -d'"' -f4)
if [ "$RATE_CODE" = "RATE_LIMITED" ]; then
    pass "Rate limit triggered on 6th attempt (RATE_LIMITED, 429)"
else
    # In case IP rate limiter hit first or configured higher
    info "Rate limit check completed (code: $RATE_CODE)"
fi
echo ""

# =============================================================================
# 10. Refresh token
# =============================================================================
info "10. POST /v1/auth/refresh"
RESP=$(post "$BASE/v1/auth/refresh" \
    "{\"refresh_token\":\"$REFRESH_TOKEN\"}")
echo "$RESP" | $PP

NEW_ACCESS=$(echo  "$RESP" | grep -o '"access_token":"[^"]*"'  | cut -d'"' -f4)
NEW_REFRESH=$(echo "$RESP" | grep -o '"refresh_token":"[^"]*"' | cut -d'"' -f4)

if [ -n "$NEW_ACCESS" ]; then
    pass "Token refreshed successfully"
    ACCESS_TOKEN="$NEW_ACCESS"
    REFRESH_TOKEN="$NEW_REFRESH"
else
    fail "Refresh did not return new access_token"
fi
echo ""

# =============================================================================
# 11. Refresh – replay old token (expect 401)
# =============================================================================
info "11. POST /v1/auth/refresh – replay old refresh token (should fail)"
RESP=$(post "$BASE/v1/auth/refresh" \
    "{\"refresh_token\":\"$LOGIN_REFRESH\"}")
echo "$RESP" | $PP
HTTP_CODE=$(echo "$RESP" | grep -o '"code":"[^"]*"' | head -1 | cut -d'"' -f4)
if [ "$HTTP_CODE" = "UNAUTHORIZED" ]; then
    pass "Old refresh token correctly rejected after rotation"
else
    fail "Expected UNAUTHORIZED on replayed token, got: $HTTP_CODE"
fi
echo ""

# =============================================================================
# 12. Add recovery email
# =============================================================================
info "12. POST /v1/account/email"
TEST_EMAIL="alice_${TS}@example.com"
RESP=$(auth_post "$BASE/v1/account/email" "$ACCESS_TOKEN" \
    "{\"email\":\"$TEST_EMAIL\"}")
echo "$RESP" | $PP
if echo "$RESP" | grep -q '"ok":true'; then
    pass "Recovery email added"
else
    fail "Add email failed"
fi
echo ""

# =============================================================================
# 13. Login with email
# =============================================================================
info "13. POST /v1/auth/login – login by email"
RESP=$(post "$BASE/v1/auth/login" \
    "{\"phone_or_email\":\"$TEST_EMAIL\",\"password\":\"MyStr0ngPass!\"}")
echo "$RESP" | $PP
EMAIL_ACCESS=$(echo "$RESP" | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
if [ -n "$EMAIL_ACCESS" ]; then
    pass "Login by email successful"
else
    fail "Login by email failed"
fi
echo ""

# =============================================================================
# 14. Recover password
# =============================================================================
info "14. POST /v1/auth/recover – reset password with recovery code"
RESP=$(post "$BASE/v1/auth/recover" \
    "{\"phone\":\"$PHONE\",\"recovery_code\":\"$RECOVERY_CODE\",\"new_password\":\"NewStr0ng#Pass\"}")
echo "$RESP" | $PP

NEW_ACCESS=$(echo   "$RESP" | grep -o '"access_token":"[^"]*"'  | cut -d'"' -f4)
NEW_REFRESH=$(echo  "$RESP" | grep -o '"refresh_token":"[^"]*"' | cut -d'"' -f4)
NEW_RECOVERY=$(echo "$RESP" | grep -o '"recovery_code":"[^"]*"' | cut -d'"' -f4)

if [ -n "$NEW_ACCESS" ] && [ -n "$NEW_RECOVERY" ]; then
    pass "Password recovered. New recovery code: $NEW_RECOVERY"
    ACCESS_TOKEN="$NEW_ACCESS"
    REFRESH_TOKEN="$NEW_REFRESH"
else
    fail "Recovery did not return new tokens or recovery_code"
fi
echo ""

# =============================================================================
# 15. Recover – reuse old code (expect 401)
# =============================================================================
info "15. POST /v1/auth/recover – reuse old recovery code (should fail)"
RESP=$(post "$BASE/v1/auth/recover" \
    "{\"phone\":\"$PHONE\",\"recovery_code\":\"$RECOVERY_CODE\",\"new_password\":\"AnotherPass9!\"}")
echo "$RESP" | $PP
CODE=$(echo "$RESP" | grep -o '"code":"[^"]*"' | head -1 | cut -d'"' -f4)
if [ "$CODE" = "AUTH_FAILED" ]; then
    pass "Old recovery code rejected after rotation"
else
    fail "Expected AUTH_FAILED on old recovery code, got: $CODE"
fi
echo ""

# =============================================================================
# 16. Login with new password (old one should fail, new should succeed)
# =============================================================================
info "16. POST /v1/auth/login – old password after recovery (should fail)"
RESP=$(post "$BASE/v1/auth/login" \
    "{\"phone_or_email\":\"$PHONE\",\"password\":\"MyStr0ngPass!\"}")
echo "$RESP" | $PP
CODE=$(echo "$RESP" | grep -o '"code":"[^"]*"' | head -1 | cut -d'"' -f4)
if [ "$CODE" = "AUTH_FAILED" ]; then
    pass "Old password rejected after recovery"
else
    fail "Expected AUTH_FAILED on old password, got: $CODE"
fi

info "16b. POST /v1/auth/login – new password after recovery (should succeed)"
RESP=$(post "$BASE/v1/auth/login" \
    "{\"phone_or_email\":\"$PHONE\",\"password\":\"NewStr0ng#Pass\"}")
echo "$RESP" | $PP
NEW_ACCESS=$(echo "$RESP" | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
if [ -n "$NEW_ACCESS" ]; then
    pass "New password works after recovery"
    ACCESS_TOKEN="$NEW_ACCESS"
    REFRESH_TOKEN=$(echo "$RESP" | grep -o '"refresh_token":"[^"]*"' | cut -d'"' -f4)
else
    fail "New password did not work after recovery"
fi
echo ""

# =============================================================================
# 17. Logout
# =============================================================================
info "17. POST /v1/auth/logout"
RESP=$(auth_post "$BASE/v1/auth/logout" "$ACCESS_TOKEN" \
    "{\"refresh_token\":\"$REFRESH_TOKEN\"}")
echo "$RESP" | $PP
if echo "$RESP" | grep -q '"ok":true'; then
    pass "Logged out successfully"
else
    fail "Logout did not return {ok:true}"
fi
echo ""

# =============================================================================
# 18. Access protected route after logout with same access token
#     (still works for up to 15 min – this is expected JWT behaviour)
# =============================================================================
info "18. POST /v1/account/email after logout (access token still valid briefly)"
RESP=$(auth_post "$BASE/v1/account/email" "$ACCESS_TOKEN" \
    "{\"email\":\"newemail_${TS}@example.com\"}")
echo "$RESP" | $PP
info "(Access token may still work for up to 15 min after logout – expected.)"
echo ""

# =============================================================================
# 19. Register a second user and delete their account
# =============================================================================
info "19. DELETE /v1/account"
TS2=$((TS+1))
PHONE2="+919${TS2: -9}"
RESP=$(post "$BASE/v1/auth/register" \
    "{\"phone\":\"$PHONE2\",\"password\":\"Delete#Me99\",\"display_name\":\"ToDelete\",\"confirm_age_18\":true,\"public_key\":\"$PUB_KEY\"}")
DEL_TOKEN=$(echo "$RESP" | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)

if [ -n "$DEL_TOKEN" ]; then
    RESP=$(auth_delete "$BASE/v1/account" "$DEL_TOKEN")
    echo "$RESP" | $PP
    if echo "$RESP" | grep -q '"ok":true'; then
        pass "Account deleted successfully"
    else
        fail "Account deletion did not return {ok:true}"
    fi
else
    fail "Could not register second user for deletion test"
fi
echo ""

# =============================================================================
# Summary
# =============================================================================
echo "────────────────────────────────────────────"
if [ "$FAILURES" -eq 0 ]; then
    echo -e "${GREEN}All tests passed!${NC}"
else
    echo -e "${RED}$FAILURES test(s) FAILED.${NC}"
    exit 1
fi
