#!/bin/bash

# auth_test.sh - Authentication System Test Suite
# Tests the unified nostr_core_lib authentication system integrated into ginxsom

# Configuration
SERVER_URL="http://localhost:9001"
UPLOAD_ENDPOINT="${SERVER_URL}/upload"
DB_PATH="db/ginxsom.db"
TEST_DIR="tests/auth_test_tmp"

# Test results tracking
TESTS_PASSED=0
TESTS_FAILED=0
TOTAL_TESTS=0

# Test keys for different scenarios - Using WSB's keys for TEST_USER1
TEST_USER1_PRIVKEY="22cc83aa57928a2800234c939240c9a6f0f44a33ea3838a860ed38930b195afd"
TEST_USER1_PUBKEY="8ff74724ed641b3c28e5a86d7c5cbc49c37638ace8c6c38935860e7a5eedde0e"

TEST_USER2_PRIVKEY="182c3a5e3b7a1b7e4f5c6b7c8b4a5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
TEST_USER2_PUBKEY="c95195e5e7de1ad8c4d3c0ac4e8b5c0c4e0c4d3c1e5c8d4c2e7e9f4a5b6c7d8e"

# Helper function to record test results
record_test_result() {
    local test_name="$1"
    local expected="$2"
    local actual="$3"
    local success="${4:-}"  # Optional success override
    
    TOTAL_TESTS=$((TOTAL_TESTS + 1))
    
    if [[ -n "$success" ]]; then
        # Use explicit success value
        if [[ "$success" == "true" ]]; then
            echo "✅ $test_name - PASSED"
            TESTS_PASSED=$((TESTS_PASSED + 1))
        else
            echo "❌ $test_name - FAILED"
            TESTS_FAILED=$((TESTS_FAILED + 1))
        fi
    elif [[ "$expected" == "ANY" ]]; then
        # Any result is acceptable
        echo "✅ $test_name - PASSED (HTTP $actual)"
        TESTS_PASSED=$((TESTS_PASSED + 1))
    elif [[ "$actual" == "$expected" ]]; then
        echo "✅ $test_name - PASSED"
        TESTS_PASSED=$((TESTS_PASSED + 1))
    else
        echo "❌ $test_name - FAILED (Expected: $expected, Got: $actual)"
        TESTS_FAILED=$((TESTS_FAILED + 1))
    fi
}

# Check prerequisites
for cmd in nak curl jq sqlite3; do
    if ! command -v $cmd &> /dev/null; then
        echo "$cmd command not found"
        exit 1
    fi
done

# Check if server is running
if ! curl -s -f "${SERVER_URL}/" > /dev/null 2>&1; then
    echo "Server not running at $SERVER_URL"
    echo "Start with: ./restart-all.sh"
    exit 1
fi

# Check if database exists
if [[ ! -f "$DB_PATH" ]]; then
    echo "Database not found at $DB_PATH"
    exit 1
fi

# Setup test environment and auth rules ONCE at the beginning
mkdir -p "$TEST_DIR"

# Enable authentication rules using admin API
curl -s -X PUT -H "Content-Type: application/json" -d '{"value": "true"}' "http://localhost:9001/api/config/auth_rules_enabled" > /dev/null

# Note: With the new simplified authentication system, we no longer use auth_rules table.
# The system now uses a simpler approach with unified config table.

# Create test files for blacklist testing
echo "test content for hash blacklist" > "$TEST_DIR/blacklisted_file.txt"
BLACKLISTED_HASH=$(sha256sum "$TEST_DIR/blacklisted_file.txt" | cut -d' ' -f1)

# Helper functions
create_test_file() {
    local filename="$1"
    local content="${2:-test content for $filename}"
    local filepath="$TEST_DIR/$filename"
    echo "$content" > "$filepath"
    echo "$filepath"
}

create_auth_event() {
    local privkey="$1" 
    local operation="$2"
    local hash="$3"
    local expiration_offset="${4:-3600}"  # 1 hour default
    
    local expiration=$(date -d "+${expiration_offset} seconds" +%s)
    
    local event_args=(-k 24242 -c "" --tag "t=$operation" --tag "expiration=$expiration" --sec "$privkey")
    
    if [[ -n "$hash" ]]; then
        event_args+=(--tag "x=$hash")
    fi
    
    nak event "${event_args[@]}"
}

test_upload() {
    local test_name="$1"
    local privkey="$2"
    local file_path="$3"
    local expected_status="${4:-ANY}"
    
    local file_hash=$(sha256sum "$file_path" | cut -d' ' -f1)
    
    # Create auth event
    local event=$(create_auth_event "$privkey" "upload" "$file_hash")
    local auth_header="Nostr $(echo "$event" | base64 -w 0)"
    
    # Make upload request
    local response_file=$(mktemp)
    local http_status=$(curl -s -w "%{http_code}" \
        -H "Authorization: $auth_header" \
        -H "Content-Type: text/plain" \
        --data-binary "@$file_path" \
        -X PUT "$UPLOAD_ENDPOINT" \
        -o "$response_file" 2>/dev/null)
    
    rm -f "$response_file"
    
    # Record result
    record_test_result "$test_name" "$expected_status" "$http_status"
}

# Run the tests

# Test 1: Valid authenticated user (should succeed)
test_file1=$(create_test_file "whitelisted_upload.txt" "Content from authenticated user")
test_upload "Test 1: Authenticated User Upload" "$TEST_USER1_PRIVKEY" "$test_file1" "200"

# Test 2: Another valid authenticated user (should succeed - no blacklisting in simplified system)
test_file2=$(create_test_file "blacklisted_upload.txt" "Content from another authenticated user")
test_upload "Test 2: Another Authenticated User Upload" "$TEST_USER2_PRIVKEY" "$test_file2" "200"

# Test 3: Third valid authenticated user (should succeed - no hash blacklisting in simplified system)
test_upload "Test 3: Authenticated User + Any Hash" "$TEST_USER1_PRIVKEY" "$TEST_DIR/blacklisted_file.txt" "200"

# Test 4: Random user (should succeed with valid authentication)
test_file4=$(create_test_file "random_upload.txt" "Content from random user")
# Use a different private key
RANDOM_PRIVKEY="abcd1234567890abcd1234567890abcd1234567890abcd1234567890abcd1234"
test_upload "Test 4: Random User (Valid Auth)" "$RANDOM_PRIVKEY" "$test_file4" "200"

# Test 5: Test with authentication disabled
# First disable authentication using admin API
curl -s -X PUT -H "Content-Type: application/json" -d '{"value": "false"}' "http://localhost:9001/api/config/auth_rules_enabled" > /dev/null

# No restart needed - admin API automatically refreshes cache
echo "Using admin API to disable authentication (with automatic cache refresh)..."
sleep 1  # Brief pause for API call to complete

test_file5=$(create_test_file "auth_disabled.txt" "Upload with auth disabled")
test_upload "Test 5: Upload with Authentication Disabled" "$TEST_USER2_PRIVKEY" "$test_file5" "200"

# Re-enable authentication using admin API (no restart needed thanks to cache refresh)
curl -s -X PUT -H "Content-Type: application/json" -d '{"value": "true"}' "http://localhost:9001/api/config/auth_rules_enabled" > /dev/null

# No restart needed - admin API automatically refreshes cache
echo "Re-enabling authentication via admin API (with automatic cache refresh)..."
sleep 1  # Brief pause for API call to complete

# Test failure modes - comprehensive edge case testing

# Helper function for failure mode tests
test_failure_mode() {
    local test_name="$1"
    local auth_header="$2"
    local file_content="${3:-failure_test_content}"
    local expected_status="${4:-401}"
    
    local test_file=$(mktemp)
    echo "$file_content" > "$test_file"
    
    local response_file=$(mktemp)
    local http_status=$(curl -s -w "%{http_code}" \
        ${auth_header:+-H "Authorization: $auth_header"} \
        -H "Content-Type: text/plain" \
        --data-binary "@$test_file" \
        -X PUT "$UPLOAD_ENDPOINT" \
        -o "$response_file" 2>/dev/null)
    
    # Show detailed error response if test fails unexpectedly
    if [[ "$http_status" != "$expected_status" ]]; then
        echo ""
        echo "=== DETAILED ERROR RESPONSE FOR: $test_name ==="
        echo "Expected HTTP Status: $expected_status"
        echo "Actual HTTP Status: $http_status"
        echo "Response Body:"
        if [[ -f "$response_file" && -s "$response_file" ]]; then
            cat "$response_file"
        else
            echo "(No response body or empty response)"
        fi
        echo "=== END DETAILED ERROR RESPONSE ==="
        echo ""
    fi
    
    rm -f "$test_file" "$response_file"
    
    # Record result
    record_test_result "$test_name" "$expected_status" "$http_status"
}

# Test 6a: Missing Authorization Header
test_failure_mode "Test 6a: Missing Authorization Header" ""

# Test 6b: Invalid Authorization Prefix
test_failure_mode "Test 6b: Invalid Authorization Prefix" "Bearer invalidtoken123"

# Test 6c: Invalid Base64 in Authorization
test_failure_mode "Test 6c: Invalid Base64 in Authorization" "Nostr invalid!@#base64"

# Test malformed JSON events

# Test 7a: Invalid JSON Structure
malformed_json='{"kind":24242,"content":"","created_at":'  # Incomplete JSON
malformed_b64=$(echo -n "$malformed_json" | base64 -w 0)
test_failure_mode "Test 7a: Invalid JSON Structure" "Nostr $malformed_b64"

# Test 7b: Missing Required Fields
missing_fields_json='{"kind":24242,"content":"","created_at":1234567890,"tags":[]}'
missing_fields_b64=$(echo -n "$missing_fields_json" | base64 -w 0)
test_failure_mode "Test 7b: Missing Required Fields (no pubkey)" "Nostr $missing_fields_b64"

# Test invalid key formats

# Test 8a: Short Public Key
echo "short_key_test" > "$TEST_DIR/short_key.txt"
file_hash=$(sha256sum "$TEST_DIR/short_key.txt" | cut -d' ' -f1)
short_pubkey="1234567890abcdef1234567890abcdef"  # 32 chars instead of 64
short_key_event=$(cat << EOF
{
  "kind": 24242,
  "content": "",
  "created_at": $(date +%s),
  "pubkey": "$short_pubkey",
  "tags": [["t", "upload"], ["x", "$file_hash"]],
  "id": "0000000000000000000000000000000000000000000000000000000000000000",
  "sig": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
}
EOF
)
short_key_b64=$(echo -n "$short_key_event" | base64 -w 0)
test_failure_mode "Test 8a: Short Public Key" "Nostr $short_key_b64"

# Test 8b: Non-hex Public Key
echo "Test 8b: Non-hex Public Key"
echo "nonhex_key_test" > "$TEST_DIR/nonhex_key.txt"
file_hash=$(sha256sum "$TEST_DIR/nonhex_key.txt" | cut -d' ' -f1)
nonhex_pubkey="gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg"  # Invalid hex
nonhex_key_event=$(cat << EOF
{
  "kind": 24242,
  "content": "",
  "created_at": $(date +%s),
  "pubkey": "$nonhex_pubkey",
  "tags": [["t", "upload"], ["x", "$file_hash"]],
  "id": "0000000000000000000000000000000000000000000000000000000000000000",
  "sig": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
}
EOF
)
nonhex_key_b64=$(echo -n "$nonhex_key_event" | base64 -w 0)
test_failure_mode "Test 8b: Non-hex Public Key" "Nostr $nonhex_key_b64"

echo "=== Test 9: Wrong Event Kind ==="

# Test 9a: Wrong Kind (1 instead of 24242)
echo "Test 9a: Wrong Kind (kind 1 instead of 24242)"
echo "wrong_kind_test" > "$TEST_DIR/wrong_kind.txt"
file_hash=$(sha256sum "$TEST_DIR/wrong_kind.txt" | cut -d' ' -f1)
wrong_kind_event=$(nak event -k 1 -c "wrong kind test" --tag "t=upload" --tag "x=$file_hash" --sec "$TEST_USER1_PRIVKEY")
wrong_kind_b64=$(echo -n "$wrong_kind_event" | base64 -w 0)
test_failure_mode "Test 9a: Wrong Event Kind" "Nostr $wrong_kind_b64"

echo "=== Test 10: Missing or Invalid Tags ==="

# Test 10a: Missing 't' tag
echo "Test 10a: Missing 't' (method) tag"
echo "missing_t_tag_test" > "$TEST_DIR/missing_t_tag.txt"
file_hash=$(sha256sum "$TEST_DIR/missing_t_tag.txt" | cut -d' ' -f1)
missing_t_event=$(nak event -k 24242 -c "" --tag "x=$file_hash" --sec "$TEST_USER1_PRIVKEY")
missing_t_b64=$(echo -n "$missing_t_event" | base64 -w 0)
test_failure_mode "Test 10a: Missing 't' tag" "Nostr $missing_t_b64"

# Test 10b: Missing 'x' tag
echo "Test 10b: Missing 'x' (hash) tag"
echo "missing_x_tag_test" > "$TEST_DIR/missing_x_tag.txt"
missing_x_event=$(nak event -k 24242 -c "" --tag "t=upload" --sec "$TEST_USER1_PRIVKEY")
missing_x_b64=$(echo -n "$missing_x_event" | base64 -w 0)
test_failure_mode "Test 10b: Missing 'x' tag" "Nostr $missing_x_b64"

# Test 10c: Hash mismatch in 'x' tag
echo "Test 10c: Hash mismatch in 'x' tag"
echo "hash_mismatch_test" > "$TEST_DIR/hash_mismatch.txt"
wrong_hash="0000000000000000000000000000000000000000000000000000000000000000"
hash_mismatch_event=$(nak event -k 24242 -c "" --tag "t=upload" --tag "x=$wrong_hash" --sec "$TEST_USER1_PRIVKEY")
hash_mismatch_b64=$(echo -n "$hash_mismatch_event" | base64 -w 0)
test_failure_mode "Test 10c: Hash mismatch" "Nostr $hash_mismatch_b64"

echo "=== Test 11: Expired Events ==="

# Test 11a: Event with past expiration
echo "Test 11a: Event with past expiration"
echo "expired_event_test" > "$TEST_DIR/expired_event.txt"
file_hash=$(sha256sum "$TEST_DIR/expired_event.txt" | cut -d' ' -f1)
past_time=$(($(date +%s) - 3600))  # 1 hour ago
expired_event=$(nak event -k 24242 -c "" --tag "t=upload" --tag "x=$file_hash" --tag "expiration=$past_time" --sec "$TEST_USER1_PRIVKEY")
expired_b64=$(echo -n "$expired_event" | base64 -w 0)
test_failure_mode "Test 11a: Expired Event" "Nostr $expired_b64"

echo "=== Test 12: Invalid Signatures ==="

# Test 12a: Corrupted signature
echo "Test 12a: Corrupted signature"
echo "corrupted_sig_test" > "$TEST_DIR/corrupted_sig.txt"
file_hash=$(sha256sum "$TEST_DIR/corrupted_sig.txt" | cut -d' ' -f1)
valid_event=$(nak event -k 24242 -c "" --tag "t=upload" --tag "x=$file_hash" --sec "$TEST_USER1_PRIVKEY")
# Corrupt the signature by changing the last character
corrupted_event=$(echo "$valid_event" | sed 's/.\{1\}$/x/')  # Replace last char with 'x'
corrupted_b64=$(echo -n "$corrupted_event" | base64 -w 0)
test_failure_mode "Test 12a: Corrupted Signature" "Nostr $corrupted_b64"


echo "=== Test 13: NIP-42 Authentication Support ==="

# Helper function to create NIP-42 challenge request
test_nip42_challenge() {
    local response_file=$(mktemp)
    local http_status=$(curl -s -w "%{http_code}" -o "$response_file" \
        -X GET "${SERVER_URL}/auth" 2>/dev/null)
    
    if [[ "$http_status" == "200" ]]; then
        local challenge=$(cat "$response_file" | jq -r '.challenge' 2>/dev/null)
        if [[ -n "$challenge" && "$challenge" != "null" ]]; then
            echo "$challenge" > "$TEST_DIR/nip42_challenge"
            record_test_result "NIP-42 Challenge Generation" "200" "$http_status"
            rm -f "$response_file"
            return 0
        else
            record_test_result "NIP-42 Challenge Generation" "200" "INVALID_FORMAT"
            rm -f "$response_file"
            return 1
        fi
    elif [[ "$http_status" == "404" ]]; then
        record_test_result "NIP-42 Challenge Generation" "DISABLED" "DISABLED" "true"
        rm -f "$response_file"
        return 2  # Disabled, not an error
    else
        record_test_result "NIP-42 Challenge Generation" "200" "$http_status"
        rm -f "$response_file"
        return 1
    fi
}

# Helper function to create NIP-42 authentication event
create_nip42_auth_event() {
    local challenge="$1"
    local privkey="$2"
    
    # Create NIP-42 authentication event (kind 22242) using nak for proper signing
    nak event -k 22242 -c "" \
        --tag "relay=ginxsom" \
        --tag "challenge=$challenge" \
        --sec "$privkey"
}

test_nip42_authentication() {
    # First, try to get a challenge
    test_nip42_challenge
    local challenge_result=$?
    
    if [[ $challenge_result -eq 2 ]]; then
        record_test_result "NIP-42 Authentication Flow" "DISABLED" "DISABLED" "true"
        return 0
    elif [[ $challenge_result -ne 0 ]]; then
        record_test_result "NIP-42 Authentication Flow" "SUCCESS" "NO_CHALLENGE"
        return 1
    fi
    
    local challenge=$(cat "$TEST_DIR/nip42_challenge" 2>/dev/null)
    if [[ -z "$challenge" ]]; then
        record_test_result "NIP-42 Authentication Flow" "SUCCESS" "NO_CHALLENGE"
        return 1
    fi
    
    # Create NIP-42 auth event
    local nip42_event=$(create_nip42_auth_event "$challenge" "$TEST_USER1_PRIVKEY")
    local nip42_auth_header="Nostr $(echo "$nip42_event" | base64 -w 0)"
    
    # Test upload with NIP-42 authentication
    local test_file=$(create_test_file "nip42_test.txt" "NIP-42 authentication test content")
    local response_file=$(mktemp)
    local http_status=$(curl -s -w "%{http_code}" \
        -H "Authorization: $nip42_auth_header" \
        -H "Content-Type: text/plain" \
        --data-binary "@$test_file" \
        -X PUT "$UPLOAD_ENDPOINT" \
        -o "$response_file" 2>/dev/null)
    
    # Show detailed error response if NIP-42 test fails
    if [[ "$http_status" != "200" ]]; then
        echo ""
        echo "=== DETAILED NIP-42 ERROR RESPONSE ==="
        echo "Expected HTTP Status: 200"
        echo "Actual HTTP Status: $http_status"
        echo "Challenge used: $challenge"
        echo "Response Body:"
        if [[ -f "$response_file" && -s "$response_file" ]]; then
            cat "$response_file"
        else
            echo "(No response body or empty response)"
        fi
        echo "Auth Header (first 100 chars): ${nip42_auth_header:0:100}..."
        echo "=== END DETAILED NIP-42 ERROR RESPONSE ==="
        echo ""
    fi
    
    rm -f "$response_file"
    
    # Record result
    record_test_result "NIP-42 Authentication Flow" "200" "$http_status"
}

# Test NIP-42 configuration modes
test_nip42_configuration() {
    # Check NIP-42 mode in database using unified config table (updated key name)
    local nip42_mode=$(sqlite3 "$DB_PATH" "SELECT value FROM config WHERE key = 'nip42_require_auth';" 2>/dev/null || echo "")
    
    if [[ -n "$nip42_mode" ]]; then
        case "$nip42_mode" in
            "true"|"false"|"optional"|"required"|"disabled")
                record_test_result "NIP-42 Configuration Check" "VALID" "VALID" "true"
                ;;
            *)
                record_test_result "NIP-42 Configuration Check" "VALID" "INVALID"
                ;;
        esac
    else
        record_test_result "NIP-42 Configuration Check" "VALID" "DEFAULT" "true"
    fi
    
    # Also check that the other NIP-42 config keys exist
    local timeout=$(sqlite3 "$DB_PATH" "SELECT value FROM config WHERE key = 'nip42_challenge_timeout';" 2>/dev/null || echo "")
    local tolerance=$(sqlite3 "$DB_PATH" "SELECT value FROM config WHERE key = 'nip42_time_tolerance';" 2>/dev/null || echo "")
    
    if [[ -n "$timeout" && -n "$tolerance" ]]; then
        record_test_result "NIP-42 Config Keys Check" "VALID" "VALID" "true"
    else
        record_test_result "NIP-42 Config Keys Check" "VALID" "PARTIAL" "true"
    fi
}

# Test dual authentication capability
test_dual_authentication_detection() {
    # Check if both authentication methods can be detected
    local blossom_event=$(create_auth_event "$TEST_USER1_PRIVKEY" "upload" "")
    local blossom_kind=$(echo "$blossom_event" | jq -r '.kind')
    
    local nip42_event=$(create_nip42_auth_event "test_challenge" "$TEST_USER1_PRIVKEY")
    local nip42_kind=$(echo "$nip42_event" | jq -r '.kind')
    
    if [[ "$blossom_kind" == "24242" && "$nip42_kind" == "22242" ]]; then
        record_test_result "Dual Authentication System Detection" "SUCCESS" "SUCCESS" "true"
    else
        record_test_result "Dual Authentication System Detection" "SUCCESS" "FAILED"
    fi
}

# Run NIP-42 tests
echo "Running NIP-42 authentication tests..."
test_nip42_configuration
test_dual_authentication_detection
test_nip42_challenge
test_nip42_authentication

echo
echo "=========================================="
echo "         TEST SUITE RESULTS"
echo "=========================================="
echo
echo "Total Tests: $TOTAL_TESTS"
echo "✅ Passed: $TESTS_PASSED"
echo "❌ Failed: $TESTS_FAILED"
echo
if [[ $TESTS_FAILED -eq 0 ]]; then
    echo "🎉 ALL TESTS PASSED!"
    echo "Authentication system fully operational:"
    echo "- Blossom authentication (kind 24242): Working"
    echo "- NIP-42 authentication (kind 22242): Working"
    echo "- Dual authentication support: Available"
    echo "- Challenge/response system: Ready"
else
    echo "⚠️  Some tests failed. Check output above for details."
    echo "Success rate: $(( (TESTS_PASSED * 100) / TOTAL_TESTS ))%"
fi
echo
echo "To clean up test data: rm -rf tests/auth_test_tmp/"
echo "=========================================="