import asyncio
import httpx
import websockets
import json
import time
import random

BASE_URL = "http://localhost:8000"
WS_URL = "ws://localhost:8000"

async def simulate_user(user_id):
    """Simulates a single user behavior"""
    email = f"testuser_{user_id}@example.com"
    password = "password123"
    
    async with httpx.AsyncClient() as client:
        # 0. Try to register first (in case users don't exist)
        try:
            await client.post(f"{BASE_URL}/auth/register", json={
                "email": email,
                "password": password,
                "first_name": "Test",
                "last_name": "User",
                "phone": "123456"
            })
        except: pass

        # 1. Try to login
        print(f"[User {user_id}] Attempting login...")
        for i in range(5):
            try:
                resp = await client.post(f"{BASE_URL}/auth/login", json={
                    "email": email,
                    "password": password
                })
                if resp.status_code == 200:
                    token = resp.json().get("access_token")
                    print(f"[User {user_id}] Login successful on attempt {i+1}")
                    break
                elif resp.status_code == 403:
                    print(f"[User {user_id}] Login failed: Captcha required or rate limited")
                    # If captcha is required, this user stops login attempts in this simple script
                    return 
            except Exception as e:
                print(f"[User {user_id}] Login error: {e}")
                return
        else:
            print(f"[User {user_id}] Failed to login after 5 attempts")
            return

        # 2. Connect to global websocket
        ws_uri = f"{WS_URL}/auth/ws/global?token={token}"
        try:
            async with websockets.connect(ws_uri) as ws:
                print(f"[User {user_id}] WebSocket connected")
                
                # 3. Simulate order creation (to test 1-min rate limit)
                # We try twice rapidly
                for i in range(2):
                    order_data = {
                        "first_name": "Test",
                        "last_name": "User",
                        "phone": "123456",
                        "email": email,
                        "shipping_address": "Test Load Street",
                        "material_id": 1,
                        "quantity": 1
                    }
                    # Need to use form-data with file
                    files = {'file': ('model.stl', b'binary content stl', 'application/octet-stream')}
                    resp = await client.post(f"{BASE_URL}/orders", data=order_data, files=files)
                    
                    if resp.status_code == 200:
                        print(f"[User {user_id}] Order {i+1} created successfully")
                    elif resp.status_code == 429:
                        print(f"[User {user_id}] Order {i+1} REJECTED (Rate Limit Working!)")
                    else:
                        print(f"[User {user_id}] Order {i+1} failed: {resp.status_code}")
                
                # 4. Stay connected for a bit to receive potential updates
                # and send pings
                for _ in range(5):
                    await ws.send("ping")
                    try:
                        # Wait for a message with timeout
                        msg = await asyncio.wait_for(ws.recv(), timeout=2.0)
                        print(f"[User {user_id}] WS Received: {msg[:50]}...")
                    except asyncio.TimeoutError:
                        pass
                    await asyncio.sleep(1)
                    
        except Exception as e:
            print(f"[User {user_id}] WS/Order error: {e}")

async def main():
    print("--- Starting Load Test ---")
    start_time = time.time()
    
    # Run 10 concurrent users
    tasks = [simulate_user(i) for i in range(10)]
    await asyncio.gather(*tasks)
    
    end_time = time.time()
    print(f"--- Load Test Finished in {end_time - start_time:.2f}s ---")

if __name__ == "__main__":
    asyncio.run(main())
