package middleware

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

	"github.com/gin-gonic/gin"
	"github.com/nexuscrm/kernel/pkg/auth"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
)

func TestRequestLoggerIncludesAuthenticatedCallerFields(t *testing.T) {
	entry := captureRequestLog(t, func(c *gin.Context) {
		c.Set(constants.ContextKeyUser, auth.UserSession{
			ID:    "005xx0000000001AAA",
			Name:  "Test User",
			Email: "test.user@example.com",
		})
		c.Set(constants.ContextKeySessionID, "session-1")
		c.Next()
	})

	if entry["authenticated"] != true {
		t.Fatalf("authenticated = %#v, want true", entry["authenticated"])
	}
	if entry["user_id"] != "005xx0000000001AAA" {
		t.Fatalf("user_id = %#v, want authenticated user id", entry["user_id"])
	}
	if entry["session_id"] != "session-1" {
		t.Fatalf("session_id = %#v, want session id", entry["session_id"])
	}
	if entry["email"] != "test.user@example.com" {
		t.Fatalf("email = %#v, want authenticated user email", entry["email"])
	}
}

func TestRequestLoggerMarksUnauthenticatedRequest(t *testing.T) {
	entry := captureRequestLog(t, nil)

	if entry["authenticated"] != false {
		t.Fatalf("authenticated = %#v, want false", entry["authenticated"])
	}
	for _, field := range []string{"user_id", "session_id", "email"} {
		if _, exists := entry[field]; exists {
			t.Fatalf("unauthenticated access log includes %s", field)
		}
	}
}

func captureRequestLog(t *testing.T, authMiddleware gin.HandlerFunc) map[string]any {
	t.Helper()

	gin.SetMode(gin.TestMode)

	var buf bytes.Buffer
	previous := log.Logger
	log.Logger = zerolog.New(&buf)
	t.Cleanup(func() {
		log.Logger = previous
	})

	router := gin.New()
	router.Use(RequestLogger())
	if authMiddleware != nil {
		router.Use(authMiddleware)
	}
	router.GET("/ping", func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	req := httptest.NewRequest(http.MethodGet, "/ping", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	var entry map[string]any
	if err := json.Unmarshal(bytes.TrimSpace(buf.Bytes()), &entry); err != nil {
		t.Fatalf("failed to decode log entry %q: %v", buf.String(), err)
	}
	return entry
}
