#!/bin/bash

# NIP-40 Expiration Timestamp Test Suite for C Nostr Relay
# Tests expiration timestamp handling in the relay's event processing pipeline

set -e  # Exit on error

# Color constants
RED='\033[31m'
GREEN='\033[32m'
YELLOW='\033[33m'
BLUE='\033[34m'
BOLD='\033[1m'
RESET='\033[0m'

# Test configuration
RELAY_URL="ws://127.0.0.1:8888"
HTTP_URL="http://127.0.0.1:8888"
TEST_COUNT=0
PASSED_COUNT=0
FAILED_COUNT=0

# Test results tracking
declare -a TEST_RESULTS=()

print_info() {
    echo -e "${BLUE}[INFO]${RESET} $1"
}

print_success() {
    echo -e "${GREEN}${BOLD}[SUCCESS]${RESET} $1"
}

print_warning() {
    echo -e "${YELLOW}[WARNING]${RESET} $1"
}

print_error() {
    echo -e "${RED}${BOLD}[ERROR]${RESET} $1"
}

print_test_header() {
    TEST_COUNT=$((TEST_COUNT + 1))
    echo ""
    echo -e "${BOLD}=== TEST $TEST_COUNT: $1 ===${RESET}"
}

record_test_result() {
    local test_name="$1"
    local result="$2"
    local details="$3"
    
    TEST_RESULTS+=("$test_name|$result|$details")
    
    if [ "$result" = "PASS" ]; then
        PASSED_COUNT=$((PASSED_COUNT + 1))
        print_success "PASS: $test_name"
    else
        FAILED_COUNT=$((FAILED_COUNT + 1))
        print_error "FAIL: $test_name"
        if [ -n "$details" ]; then
            echo "       Details: $details"
        fi
    fi
}

# Check if relay is running
check_relay_running() {
    print_info "Checking if relay is running..."
    
    if ! curl -s -H "Accept: application/nostr+json" "$HTTP_URL/" >/dev/null 2>&1; then
        print_error "Relay is not running or not accessible at $HTTP_URL"
        print_info "Please start the relay with: ./make_and_restart_relay.sh"
        exit 1
    fi
    
    print_success "Relay is running and accessible"
}

# Test NIP-11 relay information includes NIP-40
test_nip11_expiration_support() {
    print_test_header "NIP-11 Expiration Support Advertisement"
    
    print_info "Fetching relay information..."
    RELAY_INFO=$(curl -s -H "Accept: application/nostr+json" "$HTTP_URL/")
    
    echo "Relay Info Response:"
    echo "$RELAY_INFO" | jq '.'
    echo ""
    
    # Check if NIP-40 is in supported_nips
    if echo "$RELAY_INFO" | jq -e '.supported_nips | index(40)' >/dev/null 2>&1; then
        print_success "✓ NIP-40 found in supported_nips array"
        NIP40_SUPPORTED=true
    else
        print_error "✗ NIP-40 not found in supported_nips array"
        NIP40_SUPPORTED=false
    fi
    
    if [ "$NIP40_SUPPORTED" = true ]; then
        record_test_result "NIP-11 Expiration Support Advertisement" "PASS" "NIP-40 advertised in relay info"
        return 0
    else
        record_test_result "NIP-11 Expiration Support Advertisement" "FAIL" "NIP-40 not advertised"
        return 1
    fi
}

# Helper function to create event with expiration tag
create_event_with_expiration() {
    local content="$1"
    local expiration_timestamp="$2"
    local private_key="91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe"
    
    if ! command -v nak &> /dev/null; then
        echo ""
        return 1
    fi
    
    # Create event with expiration tag
    nak event --sec "$private_key" -c "$content" -t "expiration=$expiration_timestamp" --ts $(date +%s)
}

# Helper function to send event and check response
send_event_and_check() {
    local event_json="$1"
    local expected_result="$2"  # "accept" or "reject"
    local description="$3"
    
    if [ -z "$event_json" ]; then
        return 1
    fi
    
    # Create EVENT message
    local event_message="[\"EVENT\",$event_json]"
    
    # Send to relay
    if command -v websocat &> /dev/null; then
        local response=$(echo "$event_message" | timeout 5s websocat "$RELAY_URL" 2>&1 || echo "Connection failed")
        
        print_info "Relay response: $response"
        
        if [[ "$response" == *"Connection failed"* ]]; then
            print_error "✗ Failed to connect to relay"
            return 1
        elif [[ "$expected_result" == "accept" && "$response" == *"true"* ]]; then
            print_success "✓ $description accepted as expected"
            return 0
        elif [[ "$expected_result" == "reject" && "$response" == *"false"* ]]; then
            print_success "✓ $description rejected as expected"
            return 0
        elif [[ "$expected_result" == "accept" && "$response" == *"false"* ]]; then
            print_error "✗ $description unexpectedly rejected: $response"
            return 1
        elif [[ "$expected_result" == "reject" && "$response" == *"true"* ]]; then
            print_error "✗ $description unexpectedly accepted: $response"
            return 1
        else
            print_warning "? Unclear response for $description: $response"
            return 1
        fi
    else
        print_error "websocat not found - required for testing"
        return 1
    fi
}

# Test event without expiration tag
test_event_without_expiration() {
    print_test_header "Event Submission Without Expiration Tag"
    
    if ! command -v nak &> /dev/null; then
        print_warning "nak command not found - skipping expiration tests"
        record_test_result "Event Submission Without Expiration Tag" "SKIP" "nak not available"
        return 0
    fi
    
    print_info "Creating event without expiration tag..."
    
    local private_key="91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe"
    local event_json=$(nak event --sec "$private_key" -c "Test event without expiration" --ts $(date +%s))
    
    print_info "Generated event:"
    echo "$event_json" | jq '.'
    echo ""
    
    if send_event_and_check "$event_json" "accept" "Event without expiration tag"; then
        record_test_result "Event Submission Without Expiration Tag" "PASS" "Non-expiring event accepted"
        return 0
    else
        record_test_result "Event Submission Without Expiration Tag" "FAIL" "Non-expiring event handling failed"
        return 1
    fi
}

# Test event with future expiration (should be accepted)
test_event_with_future_expiration() {
    print_test_header "Event Submission With Future Expiration"
    
    if ! command -v nak &> /dev/null; then
        record_test_result "Event Submission With Future Expiration" "SKIP" "nak not available"
        return 0
    fi
    
    print_info "Creating event with future expiration (1 hour from now)..."
    
    local future_timestamp=$(($(date +%s) + 3600))  # 1 hour from now
    local event_json=$(create_event_with_expiration "Test event expiring in 1 hour" "$future_timestamp")
    
    if [ -z "$event_json" ]; then
        record_test_result "Event Submission With Future Expiration" "FAIL" "Failed to create event"
        return 1
    fi
    
    print_info "Generated event (expires at $future_timestamp):"
    echo "$event_json" | jq '.'
    echo ""
    
    if send_event_and_check "$event_json" "accept" "Event with future expiration"; then
        record_test_result "Event Submission With Future Expiration" "PASS" "Future-expiring event accepted"
        return 0
    else
        record_test_result "Event Submission With Future Expiration" "FAIL" "Future-expiring event rejected"
        return 1
    fi
}

# Test event with past expiration (should be rejected in strict mode)
test_event_with_past_expiration() {
    print_test_header "Event Submission With Past Expiration"
    
    if ! command -v nak &> /dev/null; then
        record_test_result "Event Submission With Past Expiration" "SKIP" "nak not available"
        return 0
    fi
    
    print_info "Creating event with past expiration (1 hour ago)..."
    
    local past_timestamp=$(($(date +%s) - 3600))  # 1 hour ago
    local event_json=$(create_event_with_expiration "Test event expired 1 hour ago" "$past_timestamp")
    
    if [ -z "$event_json" ]; then
        record_test_result "Event Submission With Past Expiration" "FAIL" "Failed to create event"
        return 1
    fi
    
    print_info "Generated event (expired at $past_timestamp):"
    echo "$event_json" | jq '.'
    echo ""
    
    # In strict mode (default), this should be rejected
    if send_event_and_check "$event_json" "reject" "Event with past expiration"; then
        record_test_result "Event Submission With Past Expiration" "PASS" "Expired event correctly rejected in strict mode"
        return 0
    else
        record_test_result "Event Submission With Past Expiration" "FAIL" "Expired event handling failed"
        return 1
    fi
}

# Test event with expiration within grace period
test_event_within_grace_period() {
    print_test_header "Event Submission Within Grace Period"
    
    if ! command -v nak &> /dev/null; then
        record_test_result "Event Submission Within Grace Period" "SKIP" "nak not available"
        return 0
    fi
    
    print_info "Creating event with expiration within grace period (2 minutes ago, grace period is 5 minutes)..."
    
    local grace_timestamp=$(($(date +%s) - 120))  # 2 minutes ago (within 5 minute grace period)
    local event_json=$(create_event_with_expiration "Test event within grace period" "$grace_timestamp")
    
    if [ -z "$event_json" ]; then
        record_test_result "Event Submission Within Grace Period" "FAIL" "Failed to create event"
        return 1
    fi
    
    print_info "Generated event (expired at $grace_timestamp, within grace period):"
    echo "$event_json" | jq '.'
    echo ""
    
    # Should be accepted due to grace period
    if send_event_and_check "$event_json" "accept" "Event within grace period"; then
        record_test_result "Event Submission Within Grace Period" "PASS" "Event within grace period accepted"
        return 0
    else
        record_test_result "Event Submission Within Grace Period" "FAIL" "Grace period handling failed"
        return 1
    fi
}

# Test event filtering in subscriptions
test_expiration_filtering_in_subscriptions() {
    print_test_header "Expiration Filtering in Subscriptions"
    
    if ! command -v nak &> /dev/null || ! command -v websocat &> /dev/null; then
        record_test_result "Expiration Filtering in Subscriptions" "SKIP" "Required tools not available"
        return 0
    fi
    
    print_info "Setting up short-lived events for proper expiration filtering test..."
    
    local private_key="91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe"
    
    # Event 1: No expiration (should always be returned)
    local event1=$(nak event --sec "$private_key" -c "Event without expiration for filtering test" --ts $(date +%s))
    
    # Event 2: Future expiration (should be returned)
    local future_timestamp=$(($(date +%s) + 1800))  # 30 minutes from now
    local event2=$(create_event_with_expiration "Event with future expiration for filtering test" "$future_timestamp")
    
    # Event 3: SHORT-LIVED EVENT - expires in 3 seconds
    local short_expiry=$(($(date +%s) + 3))  # 3 seconds from now
    local event3=$(create_event_with_expiration "Short-lived event for filtering test" "$short_expiry")
    
    print_info "Publishing test events (including one that expires in 3 seconds)..."
    
    # Submit all events - they should all be accepted initially
    local response1=$(echo "[\"EVENT\",$event1]" | timeout 5s websocat "$RELAY_URL" 2>&1)
    local response2=$(echo "[\"EVENT\",$event2]" | timeout 5s websocat "$RELAY_URL" 2>&1)
    local response3=$(echo "[\"EVENT\",$event3]" | timeout 5s websocat "$RELAY_URL" 2>&1)
    
    print_info "Event submission responses:"
    echo "Event 1 (no expiry): $response1"
    echo "Event 2 (future expiry): $response2"
    echo "Event 3 (expires in 3s): $response3"
    echo ""
    
    # Verify all events were accepted
    if [[ "$response1" != *"true"* ]] || [[ "$response2" != *"true"* ]] || [[ "$response3" != *"true"* ]]; then
        record_test_result "Expiration Filtering in Subscriptions" "FAIL" "Events not properly accepted during submission"
        return 1
    fi
    
    print_success "✓ All events accepted during submission"
    
    # Test 1: Query immediately - all events should be present
    print_info "Testing immediate subscription (before expiration)..."
    local req_message='["REQ","filter_immediate",{"kinds":[1],"limit":10}]'
    local immediate_response=$(echo -e "$req_message\n[\"CLOSE\",\"filter_immediate\"]" | timeout 5s websocat "$RELAY_URL" 2>/dev/null || echo "")
    
    local immediate_count=0
    if echo "$immediate_response" | grep -q "Event without expiration for filtering test"; then
        immediate_count=$((immediate_count + 1))
    fi
    if echo "$immediate_response" | grep -q "Event with future expiration for filtering test"; then
        immediate_count=$((immediate_count + 1))
    fi
    if echo "$immediate_response" | grep -q "Short-lived event for filtering test"; then
        immediate_count=$((immediate_count + 1))
    fi
    
    print_info "Immediate response found $immediate_count/3 events"
    
    # Wait for the short-lived event to expire (5 seconds total wait)
    print_info "Waiting 5 seconds for short-lived event to expire..."
    sleep 5
    
    # Test 2: Query after expiration - short-lived event should be filtered out
    print_info "Testing subscription after expiration (short-lived event should be filtered)..."
    req_message='["REQ","filter_after_expiry",{"kinds":[1],"limit":10}]'
    local expired_response=$(echo -e "$req_message\n[\"CLOSE\",\"filter_after_expiry\"]" | timeout 5s websocat "$RELAY_URL" 2>/dev/null || echo "")
    
    print_info "Post-expiration subscription response:"
    echo "$expired_response"
    echo ""
    
    # Count events in the expired response
    local no_exp_count=0
    local future_exp_count=0
    local expired_event_count=0
    
    if echo "$expired_response" | grep -q "Event without expiration for filtering test"; then
        no_exp_count=1
        print_success "✓ Event without expiration found in post-expiration results"
    fi
    
    if echo "$expired_response" | grep -q "Event with future expiration for filtering test"; then
        future_exp_count=1
        print_success "✓ Event with future expiration found in post-expiration results"
    fi
    
    if echo "$expired_response" | grep -q "Short-lived event for filtering test"; then
        expired_event_count=1
        print_error "✗ EXPIRED short-lived event found in subscription results (should be filtered!)"
    else
        print_success "✓ Expired short-lived event properly filtered from subscription results"
    fi
    
    # Evaluate results
    local expected_active_events=$((no_exp_count + future_exp_count))
    if [ $expected_active_events -ge 2 ] && [ $expired_event_count -eq 0 ]; then
        record_test_result "Expiration Filtering in Subscriptions" "PASS" "Expired events properly filtered from subscriptions"
        return 0
    else
        local details="Found $expected_active_events active events, $expired_event_count expired events (should be 0)"
        record_test_result "Expiration Filtering in Subscriptions" "FAIL" "Expiration filtering not working properly in subscriptions - $details"
        return 1
    fi
}

# Test malformed expiration tags
test_malformed_expiration_tags() {
    print_test_header "Handling of Malformed Expiration Tags"
    
    if ! command -v nak &> /dev/null; then
        record_test_result "Handling of Malformed Expiration Tags" "SKIP" "nak not available"
        return 0
    fi
    
    print_info "Testing events with malformed expiration tags..."
    
    local private_key="91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe"
    
    # Test 1: Non-numeric expiration value
    local event1=$(nak event --sec "$private_key" -c "Event with non-numeric expiration" -t "expiration=not_a_number" --ts $(date +%s))
    
    # Test 2: Empty expiration value  
    local event2=$(nak event --sec "$private_key" -c "Event with empty expiration" -t "expiration=" --ts $(date +%s))
    
    print_info "Testing non-numeric expiration value..."
    if send_event_and_check "$event1" "accept" "Event with non-numeric expiration (should be treated as no expiration)"; then
        print_success "✓ Non-numeric expiration handled gracefully"
        malformed_test1=true
    else
        malformed_test1=false
    fi
    
    print_info "Testing empty expiration value..."
    if send_event_and_check "$event2" "accept" "Event with empty expiration (should be treated as no expiration)"; then
        print_success "✓ Empty expiration handled gracefully"
        malformed_test2=true
    else
        malformed_test2=false
    fi
    
    if [ "$malformed_test1" = true ] && [ "$malformed_test2" = true ]; then
        record_test_result "Handling of Malformed Expiration Tags" "PASS" "Malformed expiration tags handled gracefully"
        return 0
    else
        record_test_result "Handling of Malformed Expiration Tags" "FAIL" "Malformed expiration tag handling failed"
        return 1
    fi
}

# Test configuration via environment variables
test_expiration_configuration() {
    print_test_header "Expiration Configuration Via Environment Variables"
    
    print_info "Testing expiration configuration from relay logs..."
    
    if [ -f "relay.log" ]; then
        print_info "Current configuration from logs:"
        grep "Expiration Configuration:" relay.log | tail -1 || print_warning "No expiration configuration found in logs"
    else
        print_warning "No relay.log found"
    fi
    
    # The relay should be running with default configuration
    print_info "Default configuration should be:"
    print_info "  enabled=true"
    print_info "  strict_mode=true (rejects expired events on submission)"
    print_info "  filter_responses=true (filters expired events from responses)"
    print_info "  grace_period=300 seconds (5 minutes)"
    
    # Test current behavior matches expected default configuration
    print_info "Configuration test based on observed behavior:"
    
    # Check if NIP-40 is advertised (indicates enabled=true)
    if curl -s -H "Accept: application/nostr+json" "$HTTP_URL/" | jq -e '.supported_nips | index(40)' >/dev/null 2>&1; then
        print_success "✓ NIP-40 support advertised (enabled=true)"
        config_test=true
    else
        print_error "✗ NIP-40 not advertised (may be disabled)"
        config_test=false
    fi
    
    if [ "$config_test" = true ]; then
        record_test_result "Expiration Configuration Via Environment Variables" "PASS" "Expiration configuration is accessible and working"
        return 0
    else
        record_test_result "Expiration Configuration Via Environment Variables" "FAIL" "Expiration configuration issues detected"
        return 1
    fi
}

# Print test summary
print_test_summary() {
    echo ""
    echo -e "${BOLD}=== TEST SUMMARY ===${RESET}"
    echo "Total tests run: $TEST_COUNT"
    echo -e "${GREEN}Passed: $PASSED_COUNT${RESET}"
    echo -e "${RED}Failed: $FAILED_COUNT${RESET}"
    
    if [ $FAILED_COUNT -gt 0 ]; then
        echo ""
        echo -e "${RED}${BOLD}Failed tests:${RESET}"
        for result in "${TEST_RESULTS[@]}"; do
            IFS='|' read -r name status details <<< "$result"
            if [ "$status" = "FAIL" ]; then
                echo -e "  ${RED}✗ $name${RESET}"
                if [ -n "$details" ]; then
                    echo "    $details"
                fi
            fi
        done
    fi
    
    echo ""
    if [ $FAILED_COUNT -eq 0 ]; then
        echo -e "${GREEN}${BOLD}🎉 ALL TESTS PASSED!${RESET}"
        echo -e "${GREEN}✅ NIP-40 Expiration Timestamp support is working correctly in the relay${RESET}"
        return 0
    else
        echo -e "${RED}${BOLD}❌ SOME TESTS FAILED${RESET}"
        echo "Please review the output above and check relay logs for more details."
        return 1
    fi
}

# Main test execution
main() {
    echo -e "${BOLD}=== NIP-40 Expiration Timestamp Relay Test Suite ===${RESET}"
    echo "Testing NIP-40 Expiration Timestamp support in the C Nostr Relay"
    echo "Relay URL: $RELAY_URL"
    echo ""
    
    # Check prerequisites
    if ! command -v curl &> /dev/null; then
        print_error "curl is required but not installed"
        exit 1
    fi
    
    if ! command -v jq &> /dev/null; then
        print_error "jq is required but not installed"
        exit 1
    fi
    
    if ! command -v websocat &> /dev/null; then
        print_warning "websocat not found - WebSocket tests will be skipped"
    fi
    
    if ! command -v nak &> /dev/null; then
        print_warning "nak not found - Event generation tests will be skipped"
        print_info "Install with: go install github.com/fiatjaf/nak@latest"
    fi
    
    # Run tests
    check_relay_running
    test_nip11_expiration_support
    test_event_without_expiration
    test_event_with_future_expiration
    test_event_with_past_expiration
    test_event_within_grace_period
    test_expiration_filtering_in_subscriptions
    test_malformed_expiration_tags
    test_expiration_configuration
    
    # Print summary
    print_test_summary
    exit $?
}

# Run main function
main "$@"