package http

import (
	"io"
	"net/http"
	"net/http/httptest"
	"strconv"
	"sync"
	"testing"

	"github.com/golang-jwt/jwt/v5"
	"hyapp/pkg/logx"
	"hyapp/services/gateway-service/internal/auth"
)

var benchmarkLogOnce sync.Once

type benchmarkResponseWriter struct {
	header http.Header
	status int
}

func newBenchmarkResponseWriter() *benchmarkResponseWriter {
	return &benchmarkResponseWriter{header: make(http.Header)}
}

func (w *benchmarkResponseWriter) Header() http.Header {
	return w.header
}

func (w *benchmarkResponseWriter) Write(payload []byte) (int, error) {
	return len(payload), nil
}

func (w *benchmarkResponseWriter) WriteHeader(statusCode int) {
	w.status = statusCode
}

func (w *benchmarkResponseWriter) reset() {
	for key := range w.header {
		delete(w.header, key)
	}
	w.status = 0
}

func benchmarkGatewayRouter() http.Handler {
	benchmarkLogOnce.Do(func() {
		_ = logx.Init(logx.Config{Service: "gateway-service", Env: "bench", Level: "error", Output: io.Discard})
	})
	handler := NewHandlerWithClients(&fakeRoomClient{}, nil, &fakeUserIdentityClient{
		resolveByDisplay: map[string]int64{"10001": 10001},
	}, &fakeUserProfileClient{})
	handler.SetMessageInboxClient(&fakeMessageInboxClient{})
	handler.SetUserSocialClient(&fakeUserSocialClient{})
	return handler.Routes(auth.NewVerifier("secret"))
}

func benchmarkGatewayToken(b *testing.B, userID int64) string {
	b.Helper()
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"user_id":           strconv.FormatInt(userID, 10),
		"app_code":          "lalu",
		"sid":               "bench-session",
		"profile_completed": true,
		"onboarding_status": "completed",
	})
	signed, err := token.SignedString([]byte("secret"))
	if err != nil {
		b.Fatalf("sign benchmark token failed: %v", err)
	}
	return signed
}

func benchmarkGatewayRoute(b *testing.B, router http.Handler, method string, path string, token string) {
	b.Helper()
	request := httptest.NewRequest(method, path, nil)
	request.Header.Set("X-App-Code", "lalu")
	if token != "" {
		request.Header.Set("Authorization", "Bearer "+token)
	}
	writer := newBenchmarkResponseWriter()

	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		writer.reset()
		router.ServeHTTP(writer, request)
		if writer.status != http.StatusOK {
			b.Fatalf("status = %d, want 200", writer.status)
		}
	}
}

func BenchmarkGatewayResolveDisplayUserIDRoute(b *testing.B) {
	benchmarkGatewayRoute(b, benchmarkGatewayRouter(), http.MethodGet, "/api/v1/users/by-display-user-id/10001", "")
}

func BenchmarkGatewayMessageReadRoute(b *testing.B) {
	token := benchmarkGatewayToken(b, 42)
	benchmarkGatewayRoute(b, benchmarkGatewayRouter(), http.MethodPost, "/api/v1/messages/msg-10001/read", token)
}

func BenchmarkGatewayUserSocialRoute(b *testing.B) {
	token := benchmarkGatewayToken(b, 42)
	benchmarkGatewayRoute(b, benchmarkGatewayRouter(), http.MethodPost, "/api/v1/users/10002/friend-requests/accept", token)
}
