#!/bin/bash
# HTTP Matrix Test Harness
# Comprehensive HTTP/API testing for Veza Web App

set -euo pipefail

# Get script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR/.."

# Load environment
if [ -f .env ]; then
    source .env
else
    echo "Error: .env file not found. Copy env.example to .env and configure."
    exit 1
fi

# Load assertion library
source http/http_assert.sh

# Generate unique test data
RAND="${RAND:-$(date +%s)}"
TEST_EMAIL="$(generate_test_email "$RAND")"
TEST_PASSWORD="$(generate_test_password "$RAND")"
SESSION_FILE="${SESSION_FILE:-.session.json}"

# Global variables
ACCESS_TOKEN=""
REFRESH_TOKEN=""
USER_ID=""

# Cleanup function
cleanup() {
    if [ -f "$SESSION_FILE" ]; then
        rm -f "$SESSION_FILE"
    fi
}
trap cleanup EXIT

# Helper functions
save_session() {
    local access="$1"
    local refresh="$2"
    local user_id="${3:-}"
    
    jo access_token="$access" \
       refresh_token="$refresh" \
       user_id="$user_id" \
       issued_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$SESSION_FILE"
}

load_session() {
    if [ -f "$SESSION_FILE" ]; then
        ACCESS_TOKEN=$(jq -r '.access_token // empty' "$SESSION_FILE")
        REFRESH_TOKEN=$(jq -r '.refresh_token // empty' "$SESSION_FILE")
        USER_ID=$(jq -r '.user_id // empty' "$SESSION_FILE")
    fi
}

# HTTP request wrapper with timing
http_request() {
    local method="$1"
    local url="$2"
    local data="${3:-}"
    local extra_args=("${@:4}")
    
    local temp_file=$(mktemp)
    local start_time=$(date +%s.%N)
    
    # Build curl command
    local curl_cmd=(curl -sk -X "$method" "$url")
    curl_cmd+=(-w '\n%{http_code}\n%{time_total}\n')
    curl_cmd+=(-D "$temp_file")
    
    # Add authorization if available
    if [ -n "$ACCESS_TOKEN" ]; then
        curl_cmd+=(-H "Authorization: Bearer $ACCESS_TOKEN")
    fi
    
    # Add data if provided
    if [ -n "$data" ]; then
        curl_cmd+=(-H "Content-Type: application/json")
        curl_cmd+=(-d "$data")
    fi
    
    # Add extra arguments
    curl_cmd+=("${extra_args[@]}")
    
    # Execute request
    local response
    response=$("${curl_cmd[@]}" 2>/dev/null || echo "CURL_ERROR")
    
    local end_time=$(date +%s.%N)
    local duration=$(echo "$end_time - $start_time" | bc || echo "0")
    
    # Parse response
    local body=$(echo "$response" | sed -n '1,/^$/p' | head -n -3)
    local code=$(echo "$response" | tail -n 2 | head -n 1)
    local time_total=$(echo "$response" | tail -n 1)
    local headers=$(cat "$temp_file")
    
    # Convert to milliseconds
    local latency_ms=$(echo "$time_total * 1000" | bc || echo "0")
    
    # Cleanup
    rm -f "$temp_file"
    
    # Return as structured data
    echo "$body"
    echo "HTTP_STATUS:$code"
    echo "HTTP_HEADERS:$headers"
    echo "HTTP_LATENCY:$latency_ms"
}

# Parse HTTP response
parse_response() {
    local response="$1"
    local body=$(echo "$response" | sed '/^HTTP_STATUS:/,$d')
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    local headers=$(echo "$response" | sed -n '/^HTTP_HEADERS:/,/^HTTP_LATENCY:/p' | sed '1d;$d')
    local latency=$(echo "$response" | grep "^HTTP_LATENCY:" | cut -d: -f2)
    
    echo "BODY:$body"
    echo "STATUS:$status"
    echo "HEADERS:$headers"
    echo "LATENCY:$latency"
}

# Test functions
test_health_check() {
    log_info "Testing health check..."
    
    local response=$(http_request GET "$API_ORIGIN/health")
    local body=$(echo "$response" | sed '/^HTTP_STATUS:/,$d')
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    
    assert_status "$status" 200 "GET /health"
    assert_json_has "$body" '.status' "health response"
    assert_json_equals "$body" '.status' "ok" "health status"
}

test_cors() {
    log_info "Testing CORS..."
    
    # Test preflight
    local response=$(http_request OPTIONS "$API_ORIGIN/auth/login" "" \
        -H "Origin: https://app.lab.veza" \
        -H "Access-Control-Request-Method: POST" \
        -H "Access-Control-Request-Headers: Content-Type")
    
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    local headers=$(echo "$response" | sed -n '/^HTTP_HEADERS:/,/^HTTP_LATENCY:/p' | sed '1d;$d')
    
    assert_status_range "$status" 200 204 "OPTIONS /auth/login"
    assert_header_exists "$headers" "Access-Control-Allow-Origin" "CORS preflight"
    assert_header_exists "$headers" "Access-Control-Allow-Methods" "CORS preflight"
}

test_auth_register() {
    log_info "Testing user registration..."
    
    # Test invalid email
    local response=$(http_request POST "$API_ORIGIN/auth/register" \
        '{"email":"invalid-email","password":"ValidPass123!"}')
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    assert_status "$status" 400 "register with invalid email"
    
    # Test weak password
    local response=$(http_request POST "$API_ORIGIN/auth/register" \
        "{\"email\":\"$TEST_EMAIL\",\"password\":\"weak\"}")
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    assert_status "$status" 400 "register with weak password"
    
    # Test successful registration
    local response=$(http_request POST "$API_ORIGIN/auth/register" \
        "{\"email\":\"$TEST_EMAIL\",\"password\":\"$TEST_PASSWORD\"}")
    
    local body=$(echo "$response" | sed '/^HTTP_STATUS:/,$d')
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    local headers=$(echo "$response" | sed -n '/^HTTP_HEADERS:/,/^HTTP_LATENCY:/p' | sed '1d;$d')
    local latency=$(echo "$response" | grep "^HTTP_LATENCY:" | cut -d: -f2)
    
    # This is where we detect the registration bug
    assert_status "$status" 201 "register new user"
    assert_json_has "$body" '.user' "register response"
    assert_json_has "$body" '.user.id' "register response"
    assert_json_has "$body" '.user.email' "register response"
    assert_json_equals "$body" '.user.email' "$TEST_EMAIL" "registered email"
    assert_json_has "$body" '.access_token' "register response"
    assert_json_has "$body" '.refresh_token' "register response"
    assert_latency_lt "$latency" 800 "register"
    
    # Save tokens
    if [ "$status" -eq 201 ]; then
        ACCESS_TOKEN=$(echo "$body" | jq -r '.access_token')
        REFRESH_TOKEN=$(echo "$body" | jq -r '.refresh_token')
        USER_ID=$(echo "$body" | jq -r '.user.id')
        save_session "$ACCESS_TOKEN" "$REFRESH_TOKEN" "$USER_ID"
    fi
    
    # Test duplicate registration
    local response=$(http_request POST "$API_ORIGIN/auth/register" \
        "{\"email\":\"$TEST_EMAIL\",\"password\":\"$TEST_PASSWORD\"}")
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    assert_status "$status" 409 "register duplicate email"
}

test_auth_login() {
    log_info "Testing user login..."
    
    # Test login with wrong password
    local response=$(http_request POST "$API_ORIGIN/auth/login" \
        "{\"email\":\"$TEST_EMAIL\",\"password\":\"WrongPassword123!\"}")
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    assert_status "$status" 401 "login with wrong password"
    
    # Test login with unknown email
    local response=$(http_request POST "$API_ORIGIN/auth/login" \
        '{"email":"unknown@lab.veza","password":"AnyPassword123!"}')
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    assert_status_range "$status" 401 404 "login with unknown email"
    
    # Test successful login
    local response=$(http_request POST "$API_ORIGIN/auth/login" \
        "{\"email\":\"$TEST_EMAIL\",\"password\":\"$TEST_PASSWORD\"}")
    
    local body=$(echo "$response" | sed '/^HTTP_STATUS:/,$d')
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    local latency=$(echo "$response" | grep "^HTTP_LATENCY:" | cut -d: -f2)
    
    assert_status "$status" 200 "login"
    assert_json_has "$body" '.user' "login response"
    assert_json_has "$body" '.access_token' "login response"
    assert_json_has "$body" '.refresh_token' "login response"
    assert_latency_lt "$latency" "${PERF_AUTH_LOGIN_P95:-300}" "login"
    
    # Update tokens
    if [ "$status" -eq 200 ]; then
        ACCESS_TOKEN=$(echo "$body" | jq -r '.access_token')
        REFRESH_TOKEN=$(echo "$body" | jq -r '.refresh_token')
        USER_ID=$(echo "$body" | jq -r '.user.id')
        save_session "$ACCESS_TOKEN" "$REFRESH_TOKEN" "$USER_ID"
    fi
}

test_auth_refresh() {
    log_info "Testing token refresh..."
    
    # Test with invalid refresh token
    local response=$(http_request POST "$API_ORIGIN/auth/refresh" \
        '{"refresh_token":"invalid-token"}')
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    assert_status "$status" 401 "refresh with invalid token"
    
    # Test with valid refresh token
    local response=$(http_request POST "$API_ORIGIN/auth/refresh" \
        "{\"refresh_token\":\"$REFRESH_TOKEN\"}")
    
    local body=$(echo "$response" | sed '/^HTTP_STATUS:/,$d')
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    
    assert_status "$status" 200 "refresh token"
    assert_json_has "$body" '.access_token' "refresh response"
    assert_json_has "$body" '.refresh_token' "refresh response"
    
    # Update tokens
    if [ "$status" -eq 200 ]; then
        ACCESS_TOKEN=$(echo "$body" | jq -r '.access_token')
        REFRESH_TOKEN=$(echo "$body" | jq -r '.refresh_token')
        save_session "$ACCESS_TOKEN" "$REFRESH_TOKEN" "$USER_ID"
    fi
}

test_profile() {
    log_info "Testing user profile..."
    
    # Test without auth
    local old_token="$ACCESS_TOKEN"
    ACCESS_TOKEN=""
    local response=$(http_request GET "$API_ORIGIN/me")
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    assert_status "$status" 401 "GET /me without auth"
    ACCESS_TOKEN="$old_token"
    
    # Test with auth
    local response=$(http_request GET "$API_ORIGIN/me")
    local body=$(echo "$response" | sed '/^HTTP_STATUS:/,$d')
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    local latency=$(echo "$response" | grep "^HTTP_LATENCY:" | cut -d: -f2)
    
    assert_status "$status" 200 "GET /me"
    assert_json_has "$body" '.id' "profile response"
    assert_json_has "$body" '.email' "profile response"
    assert_json_equals "$body" '.email' "$TEST_EMAIL" "profile email"
    assert_latency_lt "$latency" "${PERF_API_ME_P95:-200}" "GET /me"
    
    # Test profile update
    local new_nickname="TestUser$RAND"
    local response=$(http_request PATCH "$API_ORIGIN/me" \
        "{\"nickname\":\"$new_nickname\"}")
    
    local body=$(echo "$response" | sed '/^HTTP_STATUS:/,$d')
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    
    assert_status "$status" 200 "PATCH /me"
    assert_json_equals "$body" '.nickname' "$new_nickname" "updated nickname"
}

test_avatar_upload() {
    log_info "Testing avatar upload..."
    
    # Create test image if not exists
    if [ ! -f "$TEST_IMAGE_FILE" ]; then
        mkdir -p data/images
        # Create a simple 100x100 JPEG
        convert -size 100x100 xc:blue "$TEST_IMAGE_FILE" 2>/dev/null || \
        echo "Warning: ImageMagick not installed, skipping avatar test"
        return
    fi
    
    # Upload avatar
    local response=$(curl -sk -X POST "$API_ORIGIN/me/avatar" \
        -H "Authorization: Bearer $ACCESS_TOKEN" \
        -F "avatar=@$TEST_IMAGE_FILE" \
        -w '\n%{http_code}\n%{time_total}\n')
    
    local body=$(echo "$response" | sed -n '1,/^$/p' | head -n -3)
    local code=$(echo "$response" | tail -n 2 | head -n 1)
    
    assert_status "$code" 200 "avatar upload"
    assert_json_has "$body" '.avatar_url' "avatar response" || true
}

test_file_operations() {
    log_info "Testing file operations..."
    
    # Create test file
    local test_file="/tmp/test-upload-$RAND.txt"
    echo "Test content for file upload" > "$test_file"
    
    # Test upload
    local response=$(curl -sk -X POST "$API_ORIGIN/files/upload" \
        -H "Authorization: Bearer $ACCESS_TOKEN" \
        -F "file=@$test_file" \
        -w '\n%{http_code}\n%{time_total}\n')
    
    local body=$(echo "$response" | sed -n '1,/^$/p' | head -n -3)
    local code=$(echo "$response" | tail -n 2 | head -n 1)
    
    assert_status "$code" 201 "file upload"
    
    local file_id=""
    if [ "$code" -eq 201 ]; then
        file_id=$(echo "$body" | jq -r '.file.id // empty')
        assert_json_has "$body" '.file.id' "upload response"
        assert_json_has "$body" '.file.name' "upload response"
        assert_json_has "$body" '.file.size' "upload response"
    fi
    
    # Test file listing
    local response=$(http_request GET "$API_ORIGIN/files")
    local body=$(echo "$response" | sed '/^HTTP_STATUS:/,$d')
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    
    assert_status "$status" 200 "list files"
    assert_json_has "$body" '.files' "files list"
    assert_json_type "$body" '.files' "array" "files list"
    
    # Test file download
    if [ -n "$file_id" ]; then
        local response=$(curl -sk -X GET "$API_ORIGIN/files/$file_id/download" \
            -H "Authorization: Bearer $ACCESS_TOKEN" \
            -w '\n%{http_code}\n' \
            -o /tmp/downloaded-$RAND.txt)
        
        local code=$(echo "$response" | tail -n 1)
        assert_status "$code" 200 "file download"
    fi
    
    # Cleanup
    rm -f "$test_file" "/tmp/downloaded-$RAND.txt"
}

test_chat_http() {
    log_info "Testing chat HTTP endpoints..."
    
    # Get chat rooms
    local response=$(http_request GET "$API_ORIGIN/chat/rooms")
    local body=$(echo "$response" | sed '/^HTTP_STATUS:/,$d')
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    
    assert_status "$status" 200 "list chat rooms"
    assert_json_has "$body" '.rooms' "rooms list"
    assert_json_type "$body" '.rooms' "array" "rooms list"
    
    # Get messages history
    local response=$(http_request GET "$API_ORIGIN/chat/rooms/general/messages?limit=50")
    local body=$(echo "$response" | sed '/^HTTP_STATUS:/,$d')
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    
    assert_status "$status" 200 "get chat messages"
    assert_json_has "$body" '.messages' "messages list"
    assert_json_type "$body" '.messages' "array" "messages list"
}

test_streaming() {
    log_info "Testing streaming endpoints..."
    
    # Test stream health
    local response=$(http_request GET "$STREAM_ORIGIN/health")
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    
    assert_status "$status" 200 "stream health"
    
    # Create audio sample if not exists
    if [ ! -f "$TEST_AUDIO_FILE" ]; then
        mkdir -p data/audio
        # Generate 10 second silence MP3
        ffmpeg -f lavfi -i anullsrc=r=44100:cl=stereo -t 10 \
            -acodec mp3 "$TEST_AUDIO_FILE" 2>/dev/null || \
        echo "Warning: ffmpeg not installed, skipping audio test"
    fi
    
    # Test stream upload
    if [ -f "$TEST_AUDIO_FILE" ]; then
        local response=$(curl -sk -X POST "$STREAM_ORIGIN/upload" \
            -H "Authorization: Bearer $ACCESS_TOKEN" \
            -F "audio=@$TEST_AUDIO_FILE" \
            -w '\n%{http_code}\n')
        
        local code=$(echo "$response" | tail -n 1)
        assert_status "$code" 201 "stream upload" || true
    fi
}

test_docs() {
    log_info "Testing documentation..."
    
    # Test docs home
    local response=$(http_request GET "$DOCS_ORIGIN/")
    local body=$(echo "$response" | sed '/^HTTP_STATUS:/,$d')
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    
    assert_status "$status" 200 "docs home"
    assert_body_contains "$body" "Veza" "docs content"
    
    # Test docs assets
    local response=$(http_request GET "$DOCS_ORIGIN/img/logo.svg")
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    
    # Logo might be optional
    if [ "$status" -ne 404 ]; then
        assert_status "$status" 200 "docs logo"
    fi
}

test_security_headers() {
    log_info "Testing security headers..."
    
    local response=$(http_request GET "$API_ORIGIN/health")
    local headers=$(echo "$response" | sed -n '/^HTTP_HEADERS:/,/^HTTP_LATENCY:/p' | sed '1d;$d')
    
    assert_security_headers "$headers" "API security headers"
    
    # Test specific headers
    assert_header_equals "$headers" "X-Content-Type-Options" "nosniff" "X-CTO header"
    assert_header_exists "$headers" "X-Frame-Options" "X-Frame-Options"
    assert_header_exists "$headers" "Referrer-Policy" "Referrer-Policy"
}

test_error_handling() {
    log_info "Testing error handling..."
    
    # Test 404
    local response=$(http_request GET "$API_ORIGIN/nonexistent/endpoint")
    local body=$(echo "$response" | sed '/^HTTP_STATUS:/,$d')
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    
    assert_status "$status" 404 "404 error"
    assert_json_has "$body" '.error' "404 error response" || \
    assert_json_has "$body" '.message' "404 error response"
    
    # Test malformed JSON
    local response=$(http_request POST "$API_ORIGIN/auth/login" \
        "{invalid-json}" \
        -H "Content-Type: application/json")
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    
    assert_status "$status" 400 "malformed JSON"
}

test_rate_limiting() {
    log_info "Testing rate limiting..."
    
    # Make multiple rapid requests
    local rate_limited=false
    for i in {1..15}; do
        local response=$(http_request POST "$API_ORIGIN/auth/login" \
            "{\"email\":\"test$i@lab.veza\",\"password\":\"wrong\"}")
        local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
        local headers=$(echo "$response" | sed -n '/^HTTP_HEADERS:/,/^HTTP_LATENCY:/p' | sed '1d;$d')
        
        if [ "$status" -eq 429 ]; then
            rate_limited=true
            assert_header_exists "$headers" "Retry-After" "rate limit headers" || true
            assert_header_exists "$headers" "X-RateLimit-Limit" "rate limit headers" || true
            break
        fi
    done
    
    if [ "$rate_limited" = true ]; then
        pass "Rate limiting is active"
    else
        log_warn "Rate limiting might not be configured"
    fi
}

test_auth_logout() {
    log_info "Testing logout..."
    
    local response=$(http_request POST "$API_ORIGIN/auth/logout" \
        "{\"refresh_token\":\"$REFRESH_TOKEN\"}")
    
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    assert_status_range "$status" 200 204 "logout"
    
    # Test that old tokens are invalidated
    local response=$(http_request GET "$API_ORIGIN/me")
    local status=$(echo "$response" | grep "^HTTP_STATUS:" | cut -d: -f2)
    
    # Might still work if no token blacklisting
    if [ "$status" -eq 401 ]; then
        pass "Tokens invalidated after logout"
    else
        log_warn "Tokens might still be valid after logout"
    fi
}

# Main test execution
main() {
    log_info "Starting HTTP Matrix Tests"
    log_info "Environment: LAB"
    log_info "Test user: $TEST_EMAIL"
    echo
    
    # Run all tests
    test_health_check
    test_cors
    test_auth_register
    
    # Only continue if registration worked
    if [ -n "$ACCESS_TOKEN" ]; then
        test_auth_login
        test_auth_refresh
        test_profile
        test_avatar_upload
        test_file_operations
        test_chat_http
        test_streaming
        test_docs
        test_security_headers
        test_error_handling
        test_rate_limiting
        test_auth_logout
    else
        log_error "Registration failed - skipping authenticated tests"
        log_error "This indicates a critical bug in the registration endpoint"
    fi
    
    # Print summary
    print_test_summary
}

# Run tests
main "$@"