#!/bin/bash

# NIP-50 Search Message Test - Test search functionality
# Tests search field in filter objects to verify correct event searching

set -e  # Exit on any 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"
TEST_PRIVATE_KEY="nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99"

# Print functions
print_header() {
    echo -e "${BLUE}${BOLD}=== $1 ===${RESET}"
}

print_step() {
    echo -e "${YELLOW}[STEP]${RESET} $1"
}

print_success() {
    echo -e "${GREEN}✓${RESET} $1"
}

print_error() {
    echo -e "${RED}✗${RESET} $1"
}

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

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

# Global arrays to store event IDs for search tests
declare -a SEARCH_EVENT_IDS=()

# Baseline counts from existing events in relay
BASELINE_TOTAL=0
BASELINE_BITCOIN=0
BASELINE_LIGHTNING=0
BASELINE_NOSTR=0
BASELINE_DECENTRALIZED=0
BASELINE_NETWORK=0

# Helper function to get baseline count for a search term (before publishing test events)
get_baseline_search_count() {
    local search_term="$1"

    # Create COUNT message with search
    local filter="{\"search\":\"$search_term\"}"
    local count_message="[\"COUNT\",\"baseline_search\",$filter]"

    # Send COUNT message and get response
    local response=""
    if command -v websocat &> /dev/null; then
        response=$(echo "$count_message" | timeout 3s websocat "$RELAY_URL" 2>&1 || echo "")
    fi

    # Parse COUNT response
    if [[ -n "$response" ]]; then
        local count_result=$(echo "$response" | grep '"COUNT"' | head -1)
        if [[ -n "$count_result" ]]; then
            local count=$(echo "$count_result" | jq -r '.[2].count' 2>/dev/null)
            if [[ "$count" =~ ^[0-9]+$ ]]; then
                echo "$count"
                return 0
            fi
        fi
    fi

    echo "0"  # Default to 0 if we can't get the count
}

# Helper function to publish event and extract ID
publish_event() {
    local event_json="$1"
    local description="$2"

    # Extract event ID
    local event_id=$(echo "$event_json" | jq -r '.id' 2>/dev/null)
    if [[ "$event_id" == "null" || -z "$event_id" ]]; then
        print_error "Could not extract event ID from $description"
        return 1
    fi

    print_info "Publishing $description..."

    # Create EVENT message in Nostr format
    local event_message="[\"EVENT\",$event_json]"

    # Publish to relay
    local response=""
    if command -v websocat &> /dev/null; then
        response=$(echo "$event_message" | timeout 5s websocat "$RELAY_URL" 2>&1 || echo "Connection failed")
    else
        print_error "websocat not found - required for testing"
        return 1
    fi

    # Check response
    if [[ "$response" == *"Connection failed"* ]]; then
        print_error "Failed to connect to relay for $description"
        return 1
    elif [[ "$response" == *"true"* ]]; then
        print_success "$description uploaded (ID: ${event_id:0:16}...)"
        SEARCH_EVENT_IDS+=("$event_id")
        echo # Add blank line for readability
        return 0
    else
        print_warning "$description might have failed: $response"
        echo # Add blank line for readability
        return 1
    fi
}

# Helper function to send COUNT message with search and check response
test_search_count() {
    local sub_id="$1"
    local filter="$2"
    local description="$3"
    local expected_count="$4"

    print_step "Testing SEARCH COUNT: $description"

    # Create COUNT message
    local count_message="[\"COUNT\",\"$sub_id\",$filter]"

    print_info "Sending filter: $filter"

    # Send COUNT message and get response
    local response=""
    if command -v websocat &> /dev/null; then
        response=$(echo "$count_message" | timeout 3s websocat "$RELAY_URL" 2>/dev/null || echo "")
    fi

    # Parse COUNT response
    local count_result=""
    if [[ -n "$response" ]]; then
        # Look for COUNT response: ["COUNT","sub_id",{"count":N}]
        count_result=$(echo "$response" | grep '"COUNT"' | head -1)
        if [[ -n "$count_result" ]]; then
            local actual_count=$(echo "$count_result" | jq -r '.[2].count' 2>/dev/null)
            if [[ "$actual_count" =~ ^[0-9]+$ ]]; then
                print_info "Received count: $actual_count"

                # Check if count matches expected
                if [[ "$expected_count" == "any" ]]; then
                    print_success "$description - Count: $actual_count"
                    return 0
                elif [[ "$actual_count" -eq "$expected_count" ]]; then
                    print_success "$description - Expected: $expected_count, Got: $actual_count"
                    return 0
                else
                    print_error "$description - Expected: $expected_count, Got: $actual_count"
                    return 1
                fi
            else
                print_error "$description - Invalid count response: $count_result"
                return 1
            fi
        else
            print_error "$description - No COUNT response received"
            print_error "Raw response: $response"
            return 1
        fi
    else
        print_error "$description - No response from relay"
        return 1
    fi
}

# Helper function to send REQ message with search and check response
test_search_req() {
    local sub_id="$1"
    local filter="$2"
    local description="$3"
    local expected_events="$4"

    print_step "Testing SEARCH REQ: $description"

    # Create REQ message
    local req_message="[\"REQ\",\"$sub_id\",$filter]"

    print_info "Sending filter: $filter"

    # Send REQ message and get response
    local response=""
    if command -v websocat &> /dev/null; then
        response=$(echo "$req_message" | timeout 5s websocat "$RELAY_URL" 2>&1 || echo "")
    fi

    # Send CLOSE message to end subscription
    local close_message="[\"CLOSE\",\"$sub_id\"]"
    echo "$close_message" | timeout 2s websocat "$RELAY_URL" >/dev/null 2>&1 || true

    # Parse response for EVENT messages
    local event_count=0
    if [[ -n "$response" ]]; then
        # Count EVENT messages in response
        event_count=$(echo "$response" | grep -c '"EVENT"')

        print_info "Received events: $event_count"

        # Check if event count matches expected
        if [[ "$expected_events" == "any" ]]; then
            print_success "$description - Events: $event_count"
            return 0
        elif [[ "$event_count" -eq "$expected_events" ]]; then
            print_success "$description - Expected: $expected_events, Got: $event_count"
            return 0
        else
            print_error "$description - Expected: $expected_events, Got: $event_count"
            return 1
        fi
    else
        print_error "$description - No response from relay"
        return 1
    fi
}

# Main test function
run_search_test() {
    print_header "NIP-50 Search Message Test"

    # Check dependencies
    print_step "Checking dependencies..."
    if ! command -v nak &> /dev/null; then
        print_error "nak command not found"
        print_info "Please install nak: go install github.com/fiatjaf/nak@latest"
        return 1
    fi
    if ! command -v websocat &> /dev/null; then
        print_error "websocat command not found"
        print_info "Please install websocat for testing"
        return 1
    fi
    if ! command -v jq &> /dev/null; then
        print_error "jq command not found"
        print_info "Please install jq for JSON processing"
        return 1
    fi
    print_success "All dependencies found"

    print_header "PHASE 0: Establishing Baseline Search Counts"

    # Get baseline counts BEFORE publishing any test events
    print_step "Getting baseline search counts from existing events in relay..."

    BASELINE_TOTAL=$(get_baseline_search_count "")
    BASELINE_BITCOIN=$(get_baseline_search_count "Bitcoin")
    BASELINE_LIGHTNING=$(get_baseline_search_count "Lightning")
    BASELINE_NOSTR=$(get_baseline_search_count "Nostr")
    BASELINE_DECENTRALIZED=$(get_baseline_search_count "decentralized")
    BASELINE_NETWORK=$(get_baseline_search_count "network")

    print_info "Initial baseline search counts established:"
    print_info "  Total events: $BASELINE_TOTAL"
    print_info "  'Bitcoin' matches: $BASELINE_BITCOIN"
    print_info "  'Lightning' matches: $BASELINE_LIGHTNING"
    print_info "  'Nostr' matches: $BASELINE_NOSTR"
    print_info "  'decentralized' matches: $BASELINE_DECENTRALIZED"
    print_info "  'network' matches: $BASELINE_NETWORK"

    print_header "PHASE 1: Publishing Test Events with Searchable Content"

    # Create events with searchable content
    print_step "Creating events with searchable content..."

    # Events with "Bitcoin" in content
    local bitcoin1=$(nak event --sec "$TEST_PRIVATE_KEY" -c "Bitcoin is a decentralized digital currency" -k 1 --ts $(($(date +%s) - 100)) -t "topic=crypto" 2>/dev/null)
    local bitcoin2=$(nak event --sec "$TEST_PRIVATE_KEY" -c "The Bitcoin network is secure and decentralized" -k 1 --ts $(($(date +%s) - 90)) -t "topic=blockchain" 2>/dev/null)

    # Events with "Lightning" in content
    local lightning1=$(nak event --sec "$TEST_PRIVATE_KEY" -c "Lightning Network enables fast Bitcoin transactions" -k 1 --ts $(($(date +%s) - 80)) -t "topic=lightning" 2>/dev/null)
    local lightning2=$(nak event --sec "$TEST_PRIVATE_KEY" -c "Lightning channels are bidirectional payment channels" -k 1 --ts $(($(date +%s) - 70)) -t "topic=scaling" 2>/dev/null)

    # Events with "Nostr" in content
    local nostr1=$(nak event --sec "$TEST_PRIVATE_KEY" -c "Nostr is a decentralized social network protocol" -k 1 --ts $(($(date +%s) - 60)) -t "topic=nostr" 2>/dev/null)
    local nostr2=$(nak event --sec "$TEST_PRIVATE_KEY" -c "Nostr relays store and distribute events" -k 1 --ts $(($(date +%s) - 50)) -t "topic=protocol" 2>/dev/null)

    # Events with searchable content in tags
    local tag_event=$(nak event --sec "$TEST_PRIVATE_KEY" -c "This event has searchable tags" -k 1 --ts $(($(date +%s) - 40)) -t "search=bitcoin" -t "category=crypto" 2>/dev/null)

    # Event with no searchable content
    local no_match=$(nak event --sec "$TEST_PRIVATE_KEY" -c "This event has no matching content" -k 1 --ts $(($(date +%s) - 30)) -t "topic=other" 2>/dev/null)

    # Publish all test events
    publish_event "$bitcoin1" "Bitcoin event #1"
    publish_event "$bitcoin2" "Bitcoin event #2"
    publish_event "$lightning1" "Lightning event #1"
    publish_event "$lightning2" "Lightning event #2"
    publish_event "$nostr1" "Nostr event #1"
    publish_event "$nostr2" "Nostr event #2"
    publish_event "$tag_event" "Event with searchable tags"
    publish_event "$no_match" "Non-matching event"

    # Brief pause to let events settle
    sleep 2

    print_header "PHASE 2: Testing SEARCH Functionality"

    local test_failures=0

    # Test 1: Search for "Bitcoin" - should find baseline + 4 new events (2 in content + 1 in tags + 1 with search=bitcoin tag)
    local expected_bitcoin=$((BASELINE_BITCOIN + 4))
    if ! test_search_count "search_bitcoin_count" '{"search":"Bitcoin"}' "COUNT search for 'Bitcoin'" "$expected_bitcoin"; then
        ((test_failures++))
    fi

    if ! test_search_req "search_bitcoin_req" '{"search":"Bitcoin"}' "REQ search for 'Bitcoin'" "$expected_bitcoin"; then
        ((test_failures++))
    fi

    # Test 2: Search for "Lightning" - should find baseline + 2 new events
    local expected_lightning=$((BASELINE_LIGHTNING + 2))
    if ! test_search_count "search_lightning_count" '{"search":"Lightning"}' "COUNT search for 'Lightning'" "$expected_lightning"; then
        ((test_failures++))
    fi

    if ! test_search_req "search_lightning_req" '{"search":"Lightning"}' "REQ search for 'Lightning'" "$expected_lightning"; then
        ((test_failures++))
    fi

    # Test 3: Search for "Nostr" - should find baseline + 2 new events
    local expected_nostr=$((BASELINE_NOSTR + 2))
    if ! test_search_count "search_nostr_count" '{"search":"Nostr"}' "COUNT search for 'Nostr'" "$expected_nostr"; then
        ((test_failures++))
    fi

    if ! test_search_req "search_nostr_req" '{"search":"Nostr"}' "REQ search for 'Nostr'" "$expected_nostr"; then
        ((test_failures++))
    fi

    # Test 4: Search for "decentralized" - should find baseline + 3 new events (Bitcoin #1, Bitcoin #2, Nostr #1)
    local expected_decentralized=$((BASELINE_DECENTRALIZED + 3))
    if ! test_search_count "search_decentralized_count" '{"search":"decentralized"}' "COUNT search for 'decentralized'" "$expected_decentralized"; then
        ((test_failures++))
    fi

    if ! test_search_req "search_decentralized_req" '{"search":"decentralized"}' "REQ search for 'decentralized'" "$expected_decentralized"; then
        ((test_failures++))
    fi

    # Test 5: Search for "network" - should find baseline + 3 new events (Bitcoin2, Lightning1, Nostr1)
    local expected_network=$((BASELINE_NETWORK + 3))
    if ! test_search_count "search_network_count" '{"search":"network"}' "COUNT search for 'network'" "$expected_network"; then
        ((test_failures++))
    fi

    # Test 6: Search for non-existent term - should find 0 events
    if ! test_search_count "search_nonexistent_count" '{"search":"xyzzy"}' "COUNT search for non-existent term" "0"; then
        ((test_failures++))
    fi

    # Test 7: Search combined with other filters
    local expected_combined=$((BASELINE_BITCOIN + 4))
    if ! test_search_count "search_combined_count" '{"search":"Bitcoin","kinds":[1]}' "COUNT search 'Bitcoin' with kind filter" "$expected_combined"; then
        ((test_failures++))
    fi

    # Test 8: Search with time range
    local recent_timestamp=$(($(date +%s) - 60))
    if ! test_search_count "search_time_count" "{\"search\":\"Bitcoin\",\"since\":$recent_timestamp}" "COUNT search 'Bitcoin' with time filter" "any"; then
        ((test_failures++))
    fi

    # Test 9: Empty search string - should return all events
    local expected_empty=$((BASELINE_TOTAL + 8))
    if ! test_search_count "search_empty_count" '{"search":""}' "COUNT with empty search string" "$expected_empty"; then
        ((test_failures++))
    fi

    # Test 10: Case insensitive search (SQLite LIKE is case insensitive by default)
    local expected_case=$((BASELINE_BITCOIN + 4))
    if ! test_search_count "search_case_count" '{"search":"BITCOIN"}' "COUNT case-insensitive search for 'BITCOIN'" "$expected_case"; then
        ((test_failures++))
    fi

    # Report test results
    if [[ $test_failures -gt 0 ]]; then
        print_error "SEARCH TESTS FAILED: $test_failures test(s) failed"
        return 1
    else
        print_success "All SEARCH tests passed"
    fi

    return 0
}

# Run the SEARCH test
print_header "Starting NIP-50 Search Message Test Suite"
echo

if run_search_test; then
    echo
    print_success "All NIP-50 SEARCH tests completed successfully!"
    print_info "The C-Relay SEARCH functionality is working correctly"
    print_info "✅ Search field in filter objects works for both REQ and COUNT messages"
    print_info "✅ Search works across event content and tag values"
    print_info "✅ Search is case-insensitive and supports partial matches"
    echo
    exit 0
else
    echo
    print_error "❌ NIP-50 SEARCH TESTS FAILED!"
    print_error "The SEARCH functionality has issues that need to be fixed"
    echo
    exit 1
fi