package middleware

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/pingcap-inc/nexuscrm-api-host/hostruntime"
)

func TestBackendSessionContextWriterPublishesNonSecretSessionSelector(
	t *testing.T,
) {
	gin.SetMode(gin.TestMode)
	c, _ := gin.CreateTestContext(httptest.NewRecorder())
	c.Request = httptest.NewRequest(http.MethodGet, "/", nil)

	(backendSessionContextWriter{}).WriteSession(
		c,
		hostruntime.UserSession{ID: "user-1"},
		"session-1",
	)

	if got := c.GetString(constants.ContextKeySessionID); got != "session-1" {
		t.Fatalf("gin session selector = %q", got)
	}
	if got, _ := c.Request.Context().
		Value(constants.ContextKeySessionID).(string); got != "session-1" {
		t.Fatalf("request session selector = %q", got)
	}
}

func TestRequireAuthMissingTokenUsesNeutralDefaultWithoutProjectorOrPolicy(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	// nil authFailureProjector + no challenge policy: app-blind L0 emits its
	// neutral auth-failure envelope (never Salesforce wire skin).
	router.GET("/id/:orgId/:userId", RequireAuth(nil, nil), func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	rec := httptest.NewRecorder()
	router.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/id/00D000000000001/005000000000123AAA", nil))

	if rec.Code != http.StatusUnauthorized {
		t.Fatalf("status = %d body=%s, want %d", rec.Code, rec.Body.String(), http.StatusUnauthorized)
	}
	body := rec.Body.String()
	if !strings.Contains(body, "UNAUTHENTICATED") {
		t.Fatalf("body = %s, want neutral UNAUTHENTICATED auth-failure code", body)
	}
	// The Salesforce error vocabulary is L1-owned; app-blind L0's neutral default
	// must not emit Salesforce wire codes.
	if strings.Contains(body, "INVALID_SESSION_ID") || strings.Contains(body, "Missing_OAuth_Token") {
		t.Fatalf("body = %s, app-blind middleware must not emit Salesforce auth-failure codes", body)
	}
}

func TestRequireAuthMissingTokenUsesProjectorWireBody(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	// The L1 projector owns the auth-failure wire vocabulary/shape. Here it stands
	// in for the CRM distribution's Salesforce v63 error-array projection.
	projector := func(c *gin.Context, status int, code, message string) (int, any) {
		if code == "" {
			code = "INVALID_SESSION_ID"
		}
		return status, []map[string]string{{"errorCode": code, "message": message}}
	}
	router.GET("/id/:orgId/:userId", RequireAuth(nil, projector), func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	rec := httptest.NewRecorder()
	router.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/id/00D000000000001/005000000000123AAA", nil))

	if rec.Code != http.StatusUnauthorized {
		t.Fatalf("status = %d body=%s, want %d", rec.Code, rec.Body.String(), http.StatusUnauthorized)
	}
	body := rec.Body.String()
	if !strings.Contains(body, "INVALID_SESSION_ID") || !strings.Contains(body, "errorCode") {
		t.Fatalf("body = %s, want L1 projector wire body", body)
	}
}

func TestRequireAuthMissingTokenUsesProductChallengePolicy(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.GET("/id/:orgId/:userId", RequireAuth(nil, nil, AuthChallengePolicyFunc(func(ctx AuthChallengeContext) (AuthChallenge, bool) {
		if !ctx.MissingToken || !strings.HasPrefix(ctx.Path, "/id/") {
			return AuthChallenge{}, false
		}
		return AuthChallenge{
			Status:  http.StatusForbidden,
			Code:    "Missing_OAuth_Token",
			Message: "Missing OAuth token",
		}, true
	})), func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	rec := httptest.NewRecorder()
	router.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/id/00D000000000001/005000000000123AAA", nil))

	if rec.Code != http.StatusForbidden {
		t.Fatalf("status = %d body=%s, want %d", rec.Code, rec.Body.String(), http.StatusForbidden)
	}
	if !strings.Contains(rec.Body.String(), "Missing_OAuth_Token") {
		t.Fatalf("body = %s, want product auth challenge code", rec.Body.String())
	}
}
