#!/bin/bash
set -e

echo "🗄️  Database Durability Testing"
echo "=============================="

# Configuration
BASE_URL=${BASE_URL:-"http://localhost:8000"}
EVENT_ID=${EVENT_ID:-1}
DB_CONTAINER=${DB_CONTAINER:-""}
DB_SERVICE=${DB_SERVICE:-"mysql"}

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

# Logging
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }

# Check if server is available
check_server() {
    log_info "Checking server availability..."
    
    if ! curl -s --connect-timeout 5 "$BASE_URL/api/seats/availability/$EVENT_ID" >/dev/null; then
        log_error "Server not accessible at $BASE_URL"
        return 1
    fi
    
    log_info "Server is accessible"
}

# Detect database restart method
detect_db_restart_method() {
    log_info "Detecting database restart method..."
    
    # Try Docker first
    if command -v docker >/dev/null 2>&1; then
        # Look for common database container patterns
        local containers=$(docker ps --format "table {{.Names}}" | grep -E "(mysql|mariadb|postgres|database|db)" || true)
        
        if [ -n "$containers" ]; then
            DB_CONTAINER=$(echo "$containers" | head -n 1)
            log_info "Found database container: $DB_CONTAINER"
            echo "docker"
            return 0
        fi
    fi
    
    # Try systemctl
    if command -v systemctl >/dev/null 2>&1; then
        if systemctl is-active --quiet mysql || systemctl is-active --quiet mariadb; then
            log_info "Found systemd database service"
            echo "systemctl"
            return 0
        fi
    fi
    
    # Try Homebrew services (macOS)
    if command -v brew >/dev/null 2>&1; then
        local brew_services=$(brew services list | grep -E "(mysql|mariadb)" | grep started || true)
        if [ -n "$brew_services" ]; then
            DB_SERVICE=$(echo "$brew_services" | awk '{print $1}' | head -n 1)
            log_info "Found Homebrew database service: $DB_SERVICE"
            echo "brew"
            return 0
        fi
    fi
    
    log_warn "Could not detect database restart method"
    echo "manual"
}

# Restart database using detected method
restart_database() {
    local method=$1
    local action=$2  # "stop" or "start"
    
    case "$method" in
        "docker")
            log_info "${action}ping database container: $DB_CONTAINER"
            if [ "$action" = "stop" ]; then
                docker stop "$DB_CONTAINER" >/dev/null
            else
                docker start "$DB_CONTAINER" >/dev/null
            fi
            ;;
        "systemctl")
            log_info "${action}ping database service: $DB_SERVICE"
            sudo systemctl "$action" "$DB_SERVICE"
            ;;
        "brew")
            log_info "${action}ping database service: $DB_SERVICE"
            if [ "$action" = "stop" ]; then
                brew services stop "$DB_SERVICE"
            else
                brew services start "$DB_SERVICE"
            fi
            ;;
        "manual")
            log_warn "Manual database restart required"
            echo "Please $action your database service and press Enter to continue..."
            read -r
            ;;
    esac
}

# Wait for database to be available
wait_for_database() {
    log_info "Waiting for database to be available..."
    
    local max_attempts=30
    local attempt=1
    
    while [ $attempt -le $max_attempts ]; do
        if curl -s --connect-timeout 2 "$BASE_URL/api/seats/availability/$EVENT_ID" >/dev/null 2>&1; then
            log_info "Database is available (attempt $attempt)"
            return 0
        fi
        
        echo -n "."
        sleep 1
        attempt=$((attempt + 1))
    done
    
    log_error "Database did not become available within $max_attempts seconds"
    return 1
}

# Create active holds before database restart
create_active_holds() {
    log_info "Creating active holds before database restart..."
    
    local hold_tokens=()
    
    # Create multiple holds to simulate active sale
    for i in {1..5}; do
        local seat_id="A-$(printf "%02d" $i)"
        
        local response=$(curl -s -X POST \
            -H "Content-Type: application/json" \
            -d "{\"event_id\": $EVENT_ID, \"seat_ids\": [\"$seat_id\"]}" \
            "$BASE_URL/api/seats/hold")
        
        if echo "$response" | jq -e '.success' >/dev/null 2>&1; then
            local hold_token=$(echo "$response" | jq -r '.data.hold_token')
            hold_tokens+=("$hold_token")
            log_info "Created hold for seat $seat_id: $hold_token"
        else
            log_warn "Failed to create hold for seat $seat_id"
        fi
    done
    
    # Return hold tokens as space-separated string
    echo "${hold_tokens[@]}"
}

# Check for ghost holds after restart
check_ghost_holds() {
    local original_holds="$1"
    
    log_info "Checking for ghost holds after restart..."
    
    # Give database time to recover
    sleep 5
    
    local ghost_holds=0
    local valid_holds=0
    
    for hold_token in $original_holds; do
        local status_response=$(curl -s "$BASE_URL/api/seats/hold/$hold_token" || echo '{"error": "failed"}')
        
        if echo "$status_response" | jq -e '.success' >/dev/null 2>&1; then
            local expired=$(echo "$status_response" | jq -r '.data.expired')
            local status=$(echo "$status_response" | jq -r '.data.status')
            
            if [ "$expired" = "false" ] && [ "$status" = "held" ]; then
                valid_holds=$((valid_holds + 1))
                log_info "Hold $hold_token is still valid"
            else
                log_info "Hold $hold_token has expired (expected)"
            fi
        else
            # Hold not found - this is actually expected after restart
            log_info "Hold $hold_token not found (expected after restart)"
        fi
    done
    
    # Check seat availability to ensure no phantom holds
    local availability_response=$(curl -s "$BASE_URL/api/seats/availability/$EVENT_ID")
    
    if echo "$availability_response" | jq -e '.success' >/dev/null 2>&1; then
        local available_count=$(echo "$availability_response" | jq '.data.available_seats | length')
        log_info "Available seats after restart: $available_count"
        
        # All test seats should be available again
        local test_seats_available=0
        for i in {1..5}; do
            local seat_id="A-$(printf "%02d" $i)"
            if echo "$availability_response" | jq -e ".data.available_seats[] | select(. == \"$seat_id\")" >/dev/null 2>&1; then
                test_seats_available=$((test_seats_available + 1))
            fi
        done
        
        if [ $test_seats_available -eq 5 ]; then
            log_info "✅ All test seats are available - no ghost holds detected"
            return 0
        else
            log_error "❌ Ghost holds detected: $((5 - test_seats_available)) seats still held"
            return 1
        fi
    else
        log_error "Failed to check seat availability"
        return 1
    fi
}

# Test concurrent bookings during restart
test_concurrent_booking_stress() {
    log_info "Testing concurrent booking during database stress..."
    
    # Start background booking attempts
    local pids=()
    local success_count=0
    local conflict_count=0
    local error_count=0
    
    # Create 10 concurrent booking attempts for the same seats
    for i in {1..10}; do
        (
            local response=$(curl -s -X POST \
                -H "Content-Type: application/json" \
                -d "{\"event_id\": $EVENT_ID, \"seat_ids\": [\"B-01\", \"B-02\"]}" \
                "$BASE_URL/api/seats/hold")
            
            echo "$response" > "/tmp/booking_result_$i.json"
        ) &
        pids+=($!)
    done
    
    # Wait a moment, then restart database while bookings are happening
    sleep 2
    
    local restart_method=$(detect_db_restart_method)
    log_info "Restarting database during concurrent bookings..."
    
    restart_database "$restart_method" "stop"
    sleep 3
    restart_database "$restart_method" "start"
    
    wait_for_database
    
    # Wait for all booking attempts to complete
    for pid in "${pids[@]}"; do
        wait "$pid" 2>/dev/null || true
    done
    
    # Analyze results
    for i in {1..10}; do
        if [ -f "/tmp/booking_result_$i.json" ]; then
            local response=$(cat "/tmp/booking_result_$i.json")
            
            if echo "$response" | jq -e '.success' >/dev/null 2>&1; then
                success_count=$((success_count + 1))
            elif echo "$response" | grep -q "409\|conflict"; then
                conflict_count=$((conflict_count + 1))
            else
                error_count=$((error_count + 1))
            fi
            
            rm -f "/tmp/booking_result_$i.json"
        fi
    done
    
    log_info "Concurrent booking results:"
    log_info "- Successful: $success_count"
    log_info "- Conflicts: $conflict_count"  
    log_info "- Errors: $error_count"
    
    # Verify database consistency
    local final_holds=$(curl -s "$BASE_URL/api/seats/availability/$EVENT_ID" | jq -r '.data.available_seats[]' | grep -c "B-0[12]" || echo "0")
    
    if [ "$final_holds" -eq 2 ]; then
        log_info "✅ Seats are available - no double booking occurred"
        return 0
    elif [ "$success_count" -eq 1 ]; then
        log_info "✅ Exactly one booking succeeded - consistency maintained"
        return 0
    else
        log_error "❌ Database consistency issue detected"
        return 1
    fi
}

# Main test execution
run_durability_tests() {
    log_info "Starting database durability tests..."
    
    local restart_method=$(detect_db_restart_method)
    local test_results=0
    
    # Test 1: Ghost holds prevention
    log_info ""
    log_info "=== Test 1: Ghost Holds Prevention ==="
    
    local original_holds=$(create_active_holds)
    
    log_info "Restarting database to test ghost hold prevention..."
    restart_database "$restart_method" "stop"
    sleep 5
    restart_database "$restart_method" "start"
    
    if wait_for_database && check_ghost_holds "$original_holds"; then
        log_info "✅ Ghost holds test passed"
    else
        log_error "❌ Ghost holds test failed"
        test_results=1
    fi
    
    # Test 2: Concurrent booking during restart
    log_info ""
    log_info "=== Test 2: Concurrent Booking Under Stress ==="
    
    if test_concurrent_booking_stress; then
        log_info "✅ Concurrent booking stress test passed"
    else
        log_error "❌ Concurrent booking stress test failed"
        test_results=1
    fi
    
    # Test 3: Uniqueness constraints under stress
    log_info ""
    log_info "=== Test 3: Uniqueness Constraints ==="
    
    # Create test data
    curl -s -X POST \
        -H "Content-Type: application/json" \
        -d "{\"event_id\": $EVENT_ID, \"seat_count\": 20}" \
        "$BASE_URL/__test/seats/create" >/dev/null
    
    # Rapid restart cycles while attempting duplicates
    for cycle in {1..3}; do
        log_info "Restart cycle $cycle..."
        
        # Start duplicate booking attempts
        curl -s -X POST \
            -H "Content-Type: application/json" \
            -d "{\"event_id\": $EVENT_ID, \"seat_ids\": [\"C-01\"]}" \
            "$BASE_URL/api/seats/hold" >/dev/null &
        
        sleep 1
        restart_database "$restart_method" "stop"
        sleep 2
        restart_database "$restart_method" "start"
        wait_for_database
        
        wait 2>/dev/null || true
    done
    
    log_info "✅ Uniqueness constraints test completed"
    
    return $test_results
}

# Cleanup function
cleanup() {
    log_info "Cleaning up test data..."
    
    # Clear any remaining reservations
    curl -s -X DELETE "$BASE_URL/__test/clear-reservations" >/dev/null || true
    
    # Clean up temporary files
    rm -f /tmp/booking_result_*.json
}

# Show help
show_help() {
    echo "Database Durability Test Script"
    echo "Usage: $0 [options]"
    echo ""
    echo "Options:"
    echo "  --container NAME    Specify database container name"
    echo "  --service NAME      Specify database service name"
    echo "  --url URL           Specify base URL (default: http://localhost:8000)"
    echo "  --event-id ID       Specify event ID (default: 1)"
    echo "  -h, --help         Show this help"
    echo ""
    echo "Environment Variables:"
    echo "  BASE_URL           Base URL of the application"
    echo "  EVENT_ID           Event ID for testing"
    echo "  DB_CONTAINER       Database container name"
    echo "  DB_SERVICE         Database service name"
}

# Parse command line arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        --container)
            DB_CONTAINER="$2"
            shift 2
            ;;
        --service)
            DB_SERVICE="$2"
            shift 2
            ;;
        --url)
            BASE_URL="$2"
            shift 2
            ;;
        --event-id)
            EVENT_ID="$2"
            shift 2
            ;;
        -h|--help)
            show_help
            exit 0
            ;;
        *)
            log_error "Unknown option: $1"
            show_help
            exit 1
            ;;
    esac
done

# Main execution
main() {
    log_info "Database Durability Test Suite"
    log_info "Base URL: $BASE_URL"
    log_info "Event ID: $EVENT_ID"
    
    if ! check_server; then
        exit 1
    fi
    
    # Run the durability tests
    if run_durability_tests; then
        log_info ""
        log_info "🎉 All database durability tests passed!"
        log_info ""
        log_info "✅ Ghost holds prevention: Working"
        log_info "✅ Concurrent booking safety: Working"  
        log_info "✅ Uniqueness constraints: Working"
        exit 0
    else
        log_error ""
        log_error "❌ Some database durability tests failed"
        log_error "Check logs above for details"
        exit 1
    fi
}

# Set up cleanup trap
trap cleanup EXIT

# Run main function
main "$@"
