package handlers

import (
	"bytes"
	"encoding/json"
	"net/http/httptest"
	"sync"
	"testing"

	"github.com/go-chi/chi/v5"
	"github.com/orchard9/rdev/internal/adapter/kubernetes"
)

// setupBenchHandler creates a handler for benchmarking.
func setupBenchHandler() (*ProjectsHandler, chi.Router) {
	repo := kubernetes.NewProjectRepository("test-namespace")
	exec := kubernetes.NewExecutor("test-namespace")
	h := NewProjectsHandler(repo, exec)

	router := chi.NewRouter()
	h.Mount(router)

	return h, router
}

// BenchmarkRunClaude benchmarks the RunClaude endpoint.
// This measures the handler overhead excluding actual command execution.
func BenchmarkRunClaude(b *testing.B) {
	_, router := setupBenchHandler()

	body := ClaudeRequest{Prompt: "test prompt"}
	bodyBytes, _ := json.Marshal(body)

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		req := httptest.NewRequest("POST", "/projects/pantheon/claude",
			bytes.NewReader(bodyBytes))
		req.Header.Set("Content-Type", "application/json")
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)
	}
}

// BenchmarkRunShell benchmarks the RunShell endpoint.
func BenchmarkRunShell(b *testing.B) {
	_, router := setupBenchHandler()

	body := ShellRequest{Command: "ls -la"}
	bodyBytes, _ := json.Marshal(body)

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		req := httptest.NewRequest("POST", "/projects/pantheon/shell",
			bytes.NewReader(bodyBytes))
		req.Header.Set("Content-Type", "application/json")
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)
	}
}

// BenchmarkRunGit benchmarks the RunGit endpoint.
func BenchmarkRunGit(b *testing.B) {
	_, router := setupBenchHandler()

	body := GitRequest{Args: []string{"status"}}
	bodyBytes, _ := json.Marshal(body)

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		req := httptest.NewRequest("POST", "/projects/pantheon/git",
			bytes.NewReader(bodyBytes))
		req.Header.Set("Content-Type", "application/json")
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)
	}
}

// BenchmarkList benchmarks the List endpoint.
func BenchmarkList(b *testing.B) {
	_, router := setupBenchHandler()

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		req := httptest.NewRequest("GET", "/projects", nil)
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)
	}
}

// BenchmarkGet benchmarks the Get endpoint.
func BenchmarkGet(b *testing.B) {
	_, router := setupBenchHandler()

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		req := httptest.NewRequest("GET", "/projects/pantheon", nil)
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)
	}
}

// BenchmarkSSEStreaming benchmarks the SSE event throughput.
// This measures how fast events can be written through the stream manager.
func BenchmarkSSEStreaming(b *testing.B) {
	h, _ := setupBenchHandler()

	// Subscribe to a stream
	streamID := "bench-stream"
	events := h.streams.Subscribe(streamID)
	defer h.streams.Unsubscribe(streamID, events)

	// Drain events in background
	done := make(chan struct{})
	go func() {
		for range events {
		}
		close(done)
	}()

	eventData := map[string]any{
		"line":   "benchmark output line",
		"stream": "stdout",
	}

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		h.streams.Send(streamID, "output", eventData)
	}

	// Cleanup
	h.streams.Close(streamID)
	<-done
}

// BenchmarkSSEParallelStreaming benchmarks concurrent SSE event throughput.
func BenchmarkSSEParallelStreaming(b *testing.B) {
	h, _ := setupBenchHandler()

	streamID := "bench-parallel-stream"
	events := h.streams.Subscribe(streamID)
	defer h.streams.Unsubscribe(streamID, events)

	// Drain events in background
	done := make(chan struct{})
	go func() {
		for range events {
		}
		close(done)
	}()

	eventData := map[string]any{
		"line":   "benchmark output line",
		"stream": "stdout",
	}

	b.ResetTimer()
	b.ReportAllocs()

	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			h.streams.Send(streamID, "output", eventData)
		}
	})

	// Cleanup
	h.streams.Close(streamID)
	<-done
}

// BenchmarkJSONSerialization benchmarks response JSON serialization.
func BenchmarkJSONSerialization(b *testing.B) {
	response := map[string]any{
		"id":         "cmd-test-001",
		"project":    "pantheon",
		"type":       "claude",
		"status":     "running",
		"stream_url": "/projects/pantheon/events?stream_id=cmd-test-001",
	}

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		_, _ = json.Marshal(response)
	}
}

// BenchmarkConcurrentRequests benchmarks concurrent request handling.
func BenchmarkConcurrentRequests(b *testing.B) {
	_, router := setupBenchHandler()

	body := ClaudeRequest{Prompt: "test"}
	bodyBytes, _ := json.Marshal(body)

	b.ResetTimer()
	b.ReportAllocs()

	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			req := httptest.NewRequest("POST", "/projects/pantheon/claude",
				bytes.NewReader(bodyBytes))
			req.Header.Set("Content-Type", "application/json")
			rec := httptest.NewRecorder()
			router.ServeHTTP(rec, req)
		}
	})
}

// BenchmarkRouteMatching benchmarks chi router pattern matching.
func BenchmarkRouteMatching(b *testing.B) {
	_, router := setupBenchHandler()

	paths := []string{
		"/projects",
		"/projects/pantheon",
		"/projects/pantheon/claude",
		"/projects/pantheon/shell",
		"/projects/pantheon/git",
		"/projects/pantheon/events",
	}

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		path := paths[i%len(paths)]
		req := httptest.NewRequest("GET", path, nil)
		rec := httptest.NewRecorder()
		router.ServeHTTP(rec, req)
	}
}

// BenchmarkMultipleSubscribers benchmarks event fanout to multiple subscribers.
func BenchmarkMultipleSubscribers(b *testing.B) {
	h, _ := setupBenchHandler()

	streamID := "bench-multi-stream"
	const numSubscribers = 10

	// Create multiple subscribers
	subscribers := make([]chan streamEvent, numSubscribers)
	var wg sync.WaitGroup

	for i := 0; i < numSubscribers; i++ {
		subscribers[i] = h.streams.Subscribe(streamID)
		wg.Add(1)
		go func(ch chan streamEvent) {
			defer wg.Done()
			for range ch {
			}
		}(subscribers[i])
	}

	eventData := map[string]any{
		"line":   "benchmark output line",
		"stream": "stdout",
	}

	b.ResetTimer()
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		h.streams.Send(streamID, "output", eventData)
	}

	// Cleanup
	h.streams.Close(streamID)
	wg.Wait()
}
