package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"sync"
	"sync/atomic"
	"time"
)

// Konfiguration (über Flags steuerbar)
var (
	targetURL  string
	numClients int
	duration   time.Duration
	chunkTicks int
)

// Statistiken (Atomar für Thread-Sicherheit)
var (
	totalRequests int64
	totalErrors   int64
	totalLatency  int64 // in Millisekunden
)

// Datenstrukturen für Kommunikation (Minimal)
type StartResponse struct {
	SessionID string `json:"sessionId"`
}

type Input struct {
	Tick int    `json:"t"`
	Act  string `json:"act"`
}

type ValidateRequest struct {
	SessionID  string  `json:"sessionId"`
	Inputs     []Input `json:"inputs"`
	TotalTicks int     `json:"totalTicks"`
}

func main() {
	// 1. Argumente parsen
	flag.StringVar(&targetURL, "url", "http://localhost:8080", "URL zum Server")
	flag.IntVar(&numClients, "clients", 50, "Anzahl gleichzeitiger Spieler")
	flag.DurationVar(&duration, "time", 10*time.Second, "Dauer des Tests (z.B. 10s, 1m)")
	flag.IntVar(&chunkTicks, "ticks", 60, "Simulierte Ticks pro Anfrage (Last)")
	flag.Parse()

	fmt.Printf("🚀 Starte Benchmark auf %s\n", targetURL)
	fmt.Printf("👥 Spieler (Clients): %d\n", numClients)
	fmt.Printf("⏱️  Dauer: %s\n", duration)
	fmt.Printf("🏋️  Last pro Request: %d Ticks Physik\n", chunkTicks)
	fmt.Println("---------------------------------------------------")

	// 2. Clients starten
	var wg sync.WaitGroup
	start := time.Now()

	// Kanal um den Workern das Stop-Signal zu geben
	stopChan := make(chan struct{})

	// Timer für das Testende
	time.AfterFunc(duration, func() {
		close(stopChan)
	})

	for i := 0; i < numClients; i++ {
		wg.Add(1)
		go runClient(i, &wg, stopChan)
	}

	// Warten bis alle fertig sind
	wg.Wait()
	totalTime := time.Since(start)

	// 3. Auswertung
	printReport(totalTime)
}

func runClient(id int, wg *sync.WaitGroup, stopChan <-chan struct{}) {
	defer wg.Done()

	// A. Session Starten
	sessionID, err := startSession()
	if err != nil {
		log.Printf("[Client %d] Start Fehler: %v", id, err)
		atomic.AddInt64(&totalErrors, 1)
		return
	}

	// B. Game Loop simulieren
	client := &http.Client{Timeout: 5 * time.Second}

	for {
		select {
		case <-stopChan:
			return // Testzeit abgelaufen
		default:
			// Wir simulieren einen Chunk (z.B. Spieler rennt einfach geradeaus)
			reqData := ValidateRequest{
				SessionID:  sessionID,
				Inputs:     []Input{}, // Keine Eingaben = Nur Rennen
				TotalTicks: chunkTicks,
			}

			payload, _ := json.Marshal(reqData)

			reqStart := time.Now()
			resp, err := client.Post(targetURL+"/api/validate", "application/json", bytes.NewBuffer(payload))
			latency := time.Since(reqStart).Milliseconds()

			if err != nil || resp.StatusCode != 200 {
				// Fehler zählen
				atomic.AddInt64(&totalErrors, 1)
				if resp != nil {
					io.Copy(io.Discard, resp.Body) // Body leeren
					resp.Body.Close()
				}
			} else {
				// Erfolg zählen
				atomic.AddInt64(&totalRequests, 1)
				atomic.AddInt64(&totalLatency, latency)
				io.Copy(io.Discard, resp.Body)
				resp.Body.Close()
			}

			// Optional: Kurz warten um "echte" 60 FPS zu simulieren?
			// Für Stresstests lassen wir das weg, wir wollen sehen was MAXIMAL geht.
			// time.Sleep(100 * time.Millisecond)
		}
	}
}

func startSession() (string, error) {
	resp, err := http.Post(targetURL+"/api/start", "application/json", nil)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return "", fmt.Errorf("status %d", resp.StatusCode)
	}

	var data StartResponse
	if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
		return "", err
	}
	return data.SessionID, nil
}

func printReport(totalTime time.Duration) {
	reqs := atomic.LoadInt64(&totalRequests)
	errs := atomic.LoadInt64(&totalErrors)
	lat := atomic.LoadInt64(&totalLatency)

	avgLat := 0.0
	if reqs > 0 {
		avgLat = float64(lat) / float64(reqs)
	}

	rps := float64(reqs) / totalTime.Seconds()

	fmt.Println("\n📊 ERGEBNISSE")
	fmt.Println("---------------------------------------------------")
	fmt.Printf("✅ Erfolgreiche Requests: %d\n", reqs)
	fmt.Printf("❌ Fehler:                %d\n", errs)
	fmt.Printf("⚡ Requests pro Sekunde:  %.2f Req/s\n", rps)
	fmt.Printf("🐢 Ø Antwortzeit:         %.2f ms\n", avgLat)
	fmt.Println("---------------------------------------------------")

	if avgLat > 100 {
		fmt.Println("⚠️  WARNUNG: Server antwortet langsam (>100ms). Spieler könnten Lags spüren.")
	} else {
		fmt.Println("✨ PERFEKT: Server ist schnell genug für flüssiges Spielen!")
	}
}
