package middleware

import (
	"context"
	"errors"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

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

type fakeAuthenticator struct {
	claims               *hostruntime.SessionClaims
	refreshedBearerToken string
	err                  error
	touched              string
	validateCalls        int
	renewalCalls         int
}

func (f *fakeAuthenticator) ValidateSession(context.Context, string) (*hostruntime.SessionClaims, error) {
	f.validateCalls++
	return f.claims, f.err
}

func (f *fakeAuthenticator) ValidateSessionWithRenewal(context.Context, string) (*hostruntime.SessionClaims, string, error) {
	f.renewalCalls++
	return f.claims, f.refreshedBearerToken, f.err
}

func (f *fakeAuthenticator) TouchSession(_ context.Context, sessionID string) {
	f.touched = sessionID
}

// legacyAuthenticator deliberately implements only SessionAuthenticator. It
// locks the fallback path for existing consumers that do not opt into renewal.
type legacyAuthenticator struct {
	delegate *fakeAuthenticator
}

// controlAuthenticator implements both renewal extensions so tests can prove
// that the control-aware seam wins and receives an admitted proof or an
// explicit empty proof.
type controlAuthenticator struct {
	delegate             *fakeAuthenticator
	claims               *hostruntime.SessionClaims
	refreshedBearerToken string
	proofCookieExpiresAt time.Time
	err                  error
	controlProof         string
	bearerToken          string
	controlCalls         int
}

var (
	_ hostruntime.SessionAuthenticator         = (*legacyAuthenticator)(nil)
	_ hostruntime.SessionRenewingAuthenticator = (*fakeAuthenticator)(nil)
	_ hostruntime.SessionControlAuthenticator  = (*controlAuthenticator)(nil)
	_ hostruntime.SessionRenewingAuthenticator = (*controlAuthenticator)(nil)
)

func (a *legacyAuthenticator) ValidateSession(ctx context.Context, bearerToken string) (*hostruntime.SessionClaims, error) {
	return a.delegate.ValidateSession(ctx, bearerToken)
}

func (a *legacyAuthenticator) TouchSession(ctx context.Context, sessionID string) {
	a.delegate.TouchSession(ctx, sessionID)
}

func (a *controlAuthenticator) ValidateSession(ctx context.Context, bearerToken string) (*hostruntime.SessionClaims, error) {
	return a.delegate.ValidateSession(ctx, bearerToken)
}

func (a *controlAuthenticator) ValidateSessionWithRenewal(ctx context.Context, bearerToken string) (*hostruntime.SessionClaims, string, error) {
	return a.delegate.ValidateSessionWithRenewal(ctx, bearerToken)
}

func (a *controlAuthenticator) ValidateSessionWithControl(_ context.Context, bearerToken, controlProof string) (hostruntime.SessionControlValidation, error) {
	a.controlCalls++
	a.bearerToken = bearerToken
	a.controlProof = controlProof
	return hostruntime.SessionControlValidation{
		Claims:               a.claims,
		RefreshedBearerToken: a.refreshedBearerToken,
		ProofCookieExpiresAt: a.proofCookieExpiresAt,
	}, a.err
}

func (a *controlAuthenticator) TouchSession(ctx context.Context, sessionID string) {
	a.delegate.TouchSession(ctx, sessionID)
}

type captureSessionWriter struct {
	user      hostruntime.UserSession
	sessionID string
	wrote     bool
}

func (w *captureSessionWriter) WriteSession(_ *gin.Context, user hostruntime.UserSession, sessionID string) {
	w.user = user
	w.sessionID = sessionID
	w.wrote = true
}

type captureFailureResponder struct {
	failure AuthFailure
	wrote   bool
}

func headerValuesEqualFold(header http.Header, name string) []string {
	var values []string
	for key, headerValues := range header {
		if strings.EqualFold(key, name) {
			values = append(values, headerValues...)
		}
	}
	return values
}

func (r *captureFailureResponder) WriteAuthFailure(c *gin.Context, failure AuthFailure) {
	r.failure = failure
	r.wrote = true
	c.Status(failure.Status)
}

func TestRequireAuthMissingTokenUsesGenericFailureWithoutProductPolicy(t *testing.T) {
	gin.SetMode(gin.TestMode)
	responder := &captureFailureResponder{}
	router := gin.New()
	router.GET("/id/:orgId/:userId", RequireAuth(nil, nil, responder), func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(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)
	}
	if !responder.wrote {
		t.Fatalf("failure responder was not called")
	}
	if responder.failure.Code != "" || responder.failure.Message != "" {
		t.Fatalf("failure = %#v, want neutral failure without wire defaults", responder.failure)
	}
	if got := rec.Header().Get(HeaderRefreshedToken); got != "" {
		t.Fatalf("%s = %q, want empty", HeaderRefreshedToken, got)
	}
}

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

	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(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 !responder.wrote {
		t.Fatalf("failure responder was not called")
	}
	if responder.failure.Code != "PRODUCT_CHALLENGE" || responder.failure.Message != "Product challenge" {
		t.Fatalf("failure = %#v, want product auth challenge", responder.failure)
	}
}

func TestRequireAuthWritesValidatedSessionWithLegacyAuthenticator(t *testing.T) {
	gin.SetMode(gin.TestMode)
	delegate := &fakeAuthenticator{claims: &hostruntime.SessionClaims{
		SessionID: "session-1",
		User:      hostruntime.UserSession{ID: "005USER", Email: "ada@example.com"},
	}}
	authSvc := &legacyAuthenticator{delegate: delegate}
	writer := &captureSessionWriter{}
	router := gin.New()
	router.GET("/ok", RequireAuth(authSvc, writer, &captureFailureResponder{}), func(c *gin.Context) {
		c.Writer.Header()["x-refreshed-token"] = []string{"spoofed-token"}
		c.Status(http.StatusNoContent)
	})

	req := httptest.NewRequest(http.MethodGet, "/ok", nil)
	req.Header.Set(headerAuthorization, "Bearer token")
	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(router).ServeHTTP(rec, req)

	if rec.Code != http.StatusNoContent {
		t.Fatalf("status = %d body=%s, want %d", rec.Code, rec.Body.String(), http.StatusNoContent)
	}
	if delegate.touched != "session-1" {
		t.Fatalf("touched session = %q, want session-1", delegate.touched)
	}
	if delegate.validateCalls != 1 || delegate.renewalCalls != 0 {
		t.Fatalf("validation calls = legacy:%d renewal:%d, want 1 and 0", delegate.validateCalls, delegate.renewalCalls)
	}
	if !writer.wrote || writer.sessionID != "session-1" || writer.user.ID != "005USER" {
		t.Fatalf("writer = %#v, want validated session", writer)
	}
	if got := headerValuesEqualFold(rec.Result().Header, HeaderRefreshedToken); len(got) != 0 {
		t.Fatalf("%s values = %q, want empty", HeaderRefreshedToken, got)
	}
}

func TestRequireAuthDoesNotExposeRefreshedTokenWhenValidationFails(t *testing.T) {
	for _, tc := range []struct {
		name                 string
		claims               *hostruntime.SessionClaims
		refreshedBearerToken string
		err                  error
	}{
		{
			name:                 "error with claims",
			claims:               &hostruntime.SessionClaims{},
			refreshedBearerToken: "must-not-leak",
			err:                  errors.New("bad token"),
		},
		{name: "nil claims"},
	} {
		t.Run(tc.name, func(t *testing.T) {
			gin.SetMode(gin.TestMode)
			authSvc := &fakeAuthenticator{claims: tc.claims, refreshedBearerToken: tc.refreshedBearerToken, err: tc.err}
			writer := &captureSessionWriter{}
			router := gin.New()
			router.GET("/ok", RequireAuth(authSvc, writer, &captureFailureResponder{}), func(c *gin.Context) {
				c.Status(http.StatusNoContent)
			})

			req := httptest.NewRequest(http.MethodGet, "/ok", nil)
			req.Header.Set(headerAuthorization, "Bearer token")
			rec := httptest.NewRecorder()
			ProtectRefreshedTokenResponses(router).ServeHTTP(rec, req)

			if rec.Code != http.StatusUnauthorized {
				t.Fatalf("status = %d, want %d", rec.Code, http.StatusUnauthorized)
			}
			if got := rec.Header().Get(HeaderRefreshedToken); got != "" {
				t.Fatalf("%s = %q, want empty", HeaderRefreshedToken, got)
			}
			if writer.wrote || authSvc.touched != "" {
				t.Fatalf("invalid session wrote=%v touched=%q", writer.wrote, authSvc.touched)
			}
		})
	}
}

func TestRequireAuthPreservesNoStoreWhenDownstreamCommitsCacheableResponse(t *testing.T) {
	gin.SetMode(gin.TestMode)
	authSvc := &fakeAuthenticator{claims: &hostruntime.SessionClaims{
		SessionID: "session-1",
		User:      hostruntime.UserSession{ID: "005USER"},
	}, refreshedBearerToken: "renewed-token"}
	router := gin.New()
	router.GET("/fails-later", RequireAuth(authSvc, &captureSessionWriter{}, &captureFailureResponder{}), func(c *gin.Context) {
		c.Writer.Header()[HeaderRefreshedToken] = []string{"spoofed-token", "second-spoof"}
		c.Writer.Header()["x-refreshed-token"] = []string{"lowercase-spoof"}
		c.Writer.Header()["Cache-Control"] = []string{"public, max-age=3600"}
		c.Writer.Header()["cache-control"] = []string{"private, max-age=3600"}
		c.JSON(http.StatusInternalServerError, gin.H{"error": "later"})
	})

	req := httptest.NewRequest(http.MethodGet, "/fails-later", nil)
	req.Header.Set(headerAuthorization, "Bearer token")
	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(router).ServeHTTP(rec, req)

	if rec.Code != http.StatusInternalServerError {
		t.Fatalf("status = %d, want %d", rec.Code, http.StatusInternalServerError)
	}
	if authSvc.renewalCalls != 1 || authSvc.validateCalls != 0 {
		t.Fatalf("validation calls = renewal:%d legacy:%d, want 1 and 0", authSvc.renewalCalls, authSvc.validateCalls)
	}
	response := rec.Result()
	defer response.Body.Close()
	if got := headerValuesEqualFold(response.Header, HeaderRefreshedToken); len(got) != 1 || got[0] != "renewed-token" {
		t.Fatalf("%s values = %q, want [renewed-token]", HeaderRefreshedToken, got)
	}
	if got := headerValuesEqualFold(response.Header, "Cache-Control"); len(got) != 1 || got[0] != "no-store" {
		t.Fatalf("Cache-Control values = %q, want [no-store]", got)
	}
}

func TestRequireAuthPreservesRefreshedTokenAfterOuterMiddleware(t *testing.T) {
	gin.SetMode(gin.TestMode)
	authSvc := &fakeAuthenticator{claims: &hostruntime.SessionClaims{
		SessionID: "session-1",
		User:      hostruntime.UserSession{ID: "005USER"},
	}, refreshedBearerToken: "renewed-token"}
	router := gin.New()
	router.Use(func(c *gin.Context) {
		c.Next()
		c.Header("Access-Control-Expose-Headers", HeaderRefreshedToken)
		c.Writer.Header()["x-refreshed-token"] = []string{"outer-spoof"}
		c.Writer.Header()["cache-control"] = []string{"public, max-age=3600"}
	})
	router.GET("/ok", RequireAuth(authSvc, &captureSessionWriter{}, &captureFailureResponder{}), func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	req := httptest.NewRequest(http.MethodGet, "/ok", nil)
	req.Header.Set(headerAuthorization, "Bearer token")
	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(router).ServeHTTP(rec, req)

	response := rec.Result()
	defer response.Body.Close()
	if response.StatusCode != http.StatusNoContent {
		t.Fatalf("status = %d, want %d", response.StatusCode, http.StatusNoContent)
	}
	if got := response.Header.Get("Access-Control-Expose-Headers"); got != HeaderRefreshedToken {
		t.Fatalf("Access-Control-Expose-Headers = %q, want %q", got, HeaderRefreshedToken)
	}
	if got := headerValuesEqualFold(response.Header, HeaderRefreshedToken); len(got) != 1 || got[0] != "renewed-token" {
		t.Fatalf("%s values = %q, want [renewed-token]", HeaderRefreshedToken, got)
	}
	if got := headerValuesEqualFold(response.Header, headerCacheControl); len(got) != 1 || got[0] != cacheControlNoStore {
		t.Fatalf("%s values = %q, want [%s]", headerCacheControl, got, cacheControlNoStore)
	}
}

func TestRequireAuthWithoutResponseGuardUsesLegacyValidation(t *testing.T) {
	gin.SetMode(gin.TestMode)
	authSvc := &fakeAuthenticator{claims: &hostruntime.SessionClaims{
		SessionID: "session-1",
		User:      hostruntime.UserSession{ID: "005USER"},
	}, refreshedBearerToken: "renewed-token"}
	router := gin.New()
	router.GET("/ok", RequireAuth(authSvc, &captureSessionWriter{}, &captureFailureResponder{}), func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	req := httptest.NewRequest(http.MethodGet, "/ok", nil)
	req.Header.Set(headerAuthorization, "Bearer token")
	rec := httptest.NewRecorder()
	router.ServeHTTP(rec, req)

	if authSvc.validateCalls != 1 || authSvc.renewalCalls != 0 {
		t.Fatalf("validation calls = legacy:%d renewal:%d, want 1 and 0", authSvc.validateCalls, authSvc.renewalCalls)
	}
	if got := rec.Header().Get(HeaderRefreshedToken); got != "" {
		t.Fatalf("%s = %q, want empty", HeaderRefreshedToken, got)
	}
}

func TestProtectRefreshedTokenResponsesIsIdempotent(t *testing.T) {
	gin.SetMode(gin.TestMode)
	authSvc := &fakeAuthenticator{claims: &hostruntime.SessionClaims{
		SessionID: "session-1",
		User:      hostruntime.UserSession{ID: "005USER"},
	}, refreshedBearerToken: "renewed-token"}
	router := gin.New()
	router.GET("/ok", RequireAuth(authSvc, &captureSessionWriter{}, &captureFailureResponder{}), func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	req := httptest.NewRequest(http.MethodGet, "/ok", nil)
	req.Header.Set(headerAuthorization, "Bearer token")
	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(ProtectRefreshedTokenResponses(router)).ServeHTTP(rec, req)

	if got := headerValuesEqualFold(rec.Result().Header, HeaderRefreshedToken); len(got) != 1 || got[0] != "renewed-token" {
		t.Fatalf("%s values = %q, want [renewed-token]", HeaderRefreshedToken, got)
	}
	if got := headerValuesEqualFold(rec.Result().Header, headerCacheControl); len(got) != 1 || got[0] != cacheControlNoStore {
		t.Fatalf("%s values = %q, want [%s]", headerCacheControl, got, cacheControlNoStore)
	}
}

func TestRequireAuthDoesNotRenewAfterResponseCommit(t *testing.T) {
	gin.SetMode(gin.TestMode)
	authSvc := &fakeAuthenticator{claims: &hostruntime.SessionClaims{
		SessionID: "session-1",
		User:      hostruntime.UserSession{ID: "005USER"},
	}, refreshedBearerToken: "renewed-token"}
	router := gin.New()
	router.Use(func(c *gin.Context) {
		c.Writer.WriteHeaderNow()
		c.Next()
	})
	reached := false
	router.GET("/ok", RequireAuth(authSvc, &captureSessionWriter{}, &captureFailureResponder{}), func(*gin.Context) {
		reached = true
	})

	req := httptest.NewRequest(http.MethodGet, "/ok", nil)
	req.Header.Set(headerAuthorization, "Bearer token")
	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(router).ServeHTTP(rec, req)

	if authSvc.validateCalls != 1 || authSvc.renewalCalls != 0 {
		t.Fatalf("validation calls = legacy:%d renewal:%d, want 1 and 0", authSvc.validateCalls, authSvc.renewalCalls)
	}
	if !reached {
		t.Fatal("downstream handler was skipped after a non-aborting response commit")
	}
	if got := rec.Header().Get(HeaderRefreshedToken); got != "" {
		t.Fatalf("%s = %q, want empty", HeaderRefreshedToken, got)
	}
}

func TestOptionalAuthDoesNotRenewAfterResponseCommitAndContinues(t *testing.T) {
	gin.SetMode(gin.TestMode)
	authSvc := &fakeAuthenticator{claims: &hostruntime.SessionClaims{
		SessionID: "session-1",
		User:      hostruntime.UserSession{ID: "005USER"},
	}, refreshedBearerToken: "renewed-token"}
	router := gin.New()
	router.Use(func(c *gin.Context) {
		c.Writer.WriteHeaderNow()
		c.Next()
	})
	reached := false
	router.GET("/ok", OptionalAuth(authSvc, &captureSessionWriter{}), func(*gin.Context) {
		reached = true
	})

	req := httptest.NewRequest(http.MethodGet, "/ok", nil)
	req.Header.Set(headerAuthorization, "Bearer token")
	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(router).ServeHTTP(rec, req)

	if authSvc.validateCalls != 1 || authSvc.renewalCalls != 0 {
		t.Fatalf("validation calls = legacy:%d renewal:%d, want 1 and 0", authSvc.validateCalls, authSvc.renewalCalls)
	}
	if !reached {
		t.Fatal("downstream handler was skipped after a non-aborting response commit")
	}
	if got := rec.Header().Get(HeaderRefreshedToken); got != "" {
		t.Fatalf("%s = %q, want empty", HeaderRefreshedToken, got)
	}
}

func TestRequireAuthOmitsUnsafeRefreshedToken(t *testing.T) {
	for _, token := range []string{" ", " renewed-token", "renewed-token\nX-Injected: yes"} {
		t.Run(token, func(t *testing.T) {
			gin.SetMode(gin.TestMode)
			authSvc := &fakeAuthenticator{
				claims: &hostruntime.SessionClaims{
					SessionID: "session-1",
					User:      hostruntime.UserSession{ID: "005USER"},
				},
				refreshedBearerToken: token,
			}
			router := gin.New()
			router.GET("/ok", RequireAuth(authSvc, &captureSessionWriter{}, &captureFailureResponder{}), func(c *gin.Context) {
				c.Writer.Header()["x-refreshed-token"] = []string{"spoofed-token"}
				c.Status(http.StatusNoContent)
			})

			req := httptest.NewRequest(http.MethodGet, "/ok", nil)
			req.Header.Set(headerAuthorization, "Bearer token")
			rec := httptest.NewRecorder()
			ProtectRefreshedTokenResponses(router).ServeHTTP(rec, req)

			if got := headerValuesEqualFold(rec.Result().Header, HeaderRefreshedToken); len(got) != 0 {
				t.Fatalf("%s values = %q, want empty", HeaderRefreshedToken, got)
			}
			if got := rec.Header().Get("Cache-Control"); got != "" {
				t.Fatalf("Cache-Control = %q, want empty", got)
			}
		})
	}
}

func TestOptionalAuthIgnoresInvalidToken(t *testing.T) {
	gin.SetMode(gin.TestMode)
	authSvc := &fakeAuthenticator{
		claims:               &hostruntime.SessionClaims{},
		refreshedBearerToken: "must-not-leak",
		err:                  errors.New("bad token"),
	}
	writer := &captureSessionWriter{}
	router := gin.New()
	router.GET("/ok", OptionalAuth(authSvc, writer), func(c *gin.Context) {
		c.Writer.Header()["x-refreshed-token"] = []string{"spoofed-token"}
		c.Status(http.StatusNoContent)
	})

	req := httptest.NewRequest(http.MethodGet, "/ok", nil)
	req.Header.Set(headerAuthorization, "Bearer token")
	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(router).ServeHTTP(rec, req)

	if rec.Code != http.StatusNoContent {
		t.Fatalf("status = %d body=%s, want %d", rec.Code, rec.Body.String(), http.StatusNoContent)
	}
	if writer.wrote {
		t.Fatalf("writer should not be called for invalid optional token")
	}
	if got := headerValuesEqualFold(rec.Result().Header, HeaderRefreshedToken); len(got) != 0 {
		t.Fatalf("%s values = %q, want empty", HeaderRefreshedToken, got)
	}
}

func TestOptionalAuthPreservesNoStoreOnImplicitResponseCommit(t *testing.T) {
	gin.SetMode(gin.TestMode)
	authSvc := &fakeAuthenticator{claims: &hostruntime.SessionClaims{
		SessionID: "session-1",
		User:      hostruntime.UserSession{ID: "005USER"},
	}, refreshedBearerToken: "renewed-token"}
	router := gin.New()
	router.GET("/ok", OptionalAuth(authSvc, &captureSessionWriter{}), func(c *gin.Context) {
		c.Header(HeaderRefreshedToken, "")
		c.Header("Cache-Control", "public, max-age=3600")
	})

	req := httptest.NewRequest(http.MethodGet, "/ok", nil)
	req.Header.Set(headerAuthorization, "Bearer token")
	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(router).ServeHTTP(rec, req)

	response := rec.Result()
	defer response.Body.Close()
	if got := headerValuesEqualFold(response.Header, HeaderRefreshedToken); len(got) != 1 || got[0] != "renewed-token" {
		t.Fatalf("%s values = %q, want [renewed-token]", HeaderRefreshedToken, got)
	}
	if got := headerValuesEqualFold(response.Header, "Cache-Control"); len(got) != 1 || got[0] != "no-store" {
		t.Fatalf("Cache-Control values = %q, want [no-store]", got)
	}
}

func TestRequireAuthDoesNotLetUpgradeHeadersSkipRenewal(t *testing.T) {
	gin.SetMode(gin.TestMode)
	authSvc := &fakeAuthenticator{claims: &hostruntime.SessionClaims{
		SessionID: "session-1",
		User:      hostruntime.UserSession{ID: "005USER"},
	}, refreshedBearerToken: "renewed-token"}
	router := gin.New()
	router.GET("/socket", RequireAuth(authSvc, &captureSessionWriter{}, &captureFailureResponder{}), func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	req := httptest.NewRequest(http.MethodGet, "/socket", nil)
	req.Header.Set(headerAuthorization, "Bearer token")
	req.Header.Set("Connection", "upgrade")
	req.Header.Set("Upgrade", "websocket")
	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(router).ServeHTTP(rec, req)

	if authSvc.validateCalls != 0 || authSvc.renewalCalls != 1 {
		t.Fatalf("validation calls = legacy:%d renewal:%d, want 0 and 1", authSvc.validateCalls, authSvc.renewalCalls)
	}
	if got := headerValuesEqualFold(rec.Result().Header, HeaderRefreshedToken); len(got) != 1 || got[0] != "renewed-token" {
		t.Fatalf("%s values = %q, want [renewed-token]", HeaderRefreshedToken, got)
	}
}

func TestRequireAuthWithoutSessionRenewalUsesLegacyValidation(t *testing.T) {
	gin.SetMode(gin.TestMode)
	authSvc := &fakeAuthenticator{claims: &hostruntime.SessionClaims{
		SessionID: "session-1",
		User:      hostruntime.UserSession{ID: "005USER"},
	}, refreshedBearerToken: "renewed-token"}
	router := gin.New()
	router.GET("/socket", WithoutSessionRenewal(), RequireAuth(authSvc, &captureSessionWriter{}, &captureFailureResponder{}), func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	req := httptest.NewRequest(http.MethodGet, "/socket", nil)
	req.Header.Set(headerAuthorization, "Bearer token")
	req.Header.Set("Connection", "upgrade")
	req.Header.Set("Upgrade", "websocket")
	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(router).ServeHTTP(rec, req)

	if authSvc.validateCalls != 1 || authSvc.renewalCalls != 0 {
		t.Fatalf("validation calls = legacy:%d renewal:%d, want 1 and 0", authSvc.validateCalls, authSvc.renewalCalls)
	}
	if got := headerValuesEqualFold(rec.Result().Header, HeaderRefreshedToken); len(got) != 0 {
		t.Fatalf("%s values = %q, want empty", HeaderRefreshedToken, got)
	}
}

func TestRequireAuthPrefersControlAuthenticatorWithAdmittedProof(t *testing.T) {
	gin.SetMode(gin.TestMode)
	claims := &hostruntime.SessionClaims{
		SessionID: "session-1",
		User:      hostruntime.UserSession{ID: "005USER"},
	}
	delegate := &fakeAuthenticator{claims: claims, refreshedBearerToken: "legacy-renewed-token"}
	authSvc := &controlAuthenticator{
		delegate:             delegate,
		claims:               claims,
		refreshedBearerToken: "controlled-renewed-token",
		proofCookieExpiresAt: time.Now().Add(2 * time.Hour),
	}
	admission, err := NewSessionControlAdmission("https://app.example")
	if err != nil {
		t.Fatalf("NewSessionControlAdmission failed: %v", err)
	}
	router := gin.New()
	router.GET("/ok", admission, RequireAuth(authSvc, &captureSessionWriter{}, &captureFailureResponder{}), func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	req := admittedSessionControlRequest(http.MethodGet)
	req.URL.Path = "/ok"
	req.Header.Set(headerAuthorization, "Bearer token")
	req.Header.Set("Cookie", SessionControlCookieName+"="+testSessionControlProof)
	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(router).ServeHTTP(rec, req)

	if rec.Code != http.StatusNoContent {
		t.Fatalf("status = %d body=%s, want %d", rec.Code, rec.Body.String(), http.StatusNoContent)
	}
	if authSvc.controlCalls != 1 || delegate.validateCalls != 0 || delegate.renewalCalls != 0 {
		t.Fatalf("validation calls = control:%d strict:%d legacy-renewal:%d, want 1, 0, 0", authSvc.controlCalls, delegate.validateCalls, delegate.renewalCalls)
	}
	if authSvc.controlProof != testSessionControlProof || authSvc.bearerToken != "token" {
		t.Fatalf("control input = bearer:%q proof:%q", authSvc.bearerToken, authSvc.controlProof)
	}
	if got := rec.Header().Get(HeaderRefreshedToken); got != "controlled-renewed-token" {
		t.Fatalf("%s = %q, want controlled-renewed-token", HeaderRefreshedToken, got)
	}
	cookies := rec.Result().Cookies()
	if len(cookies) != 1 || cookies[0].Name != SessionControlCookieName || cookies[0].Value != testSessionControlProof {
		t.Fatalf("session-control cookies = %#v, want one refreshed proof cookie", cookies)
	}
	if got := rec.Header().Get(headerCacheControl); got != cacheControlNoStore {
		t.Fatalf("%s = %q, want %q", headerCacheControl, got, cacheControlNoStore)
	}
}

func TestRequireAuthCallsControlAuthenticatorWithEmptyProofForAdmittedRequest(t *testing.T) {
	gin.SetMode(gin.TestMode)
	delegate := &fakeAuthenticator{
		claims:               &hostruntime.SessionClaims{SessionID: "legacy-session"},
		refreshedBearerToken: "unsafe-legacy-renewal",
	}
	authSvc := &controlAuthenticator{delegate: delegate, err: errors.New("control proof required")}
	admission, err := NewSessionControlAdmission("https://app.example")
	if err != nil {
		t.Fatalf("NewSessionControlAdmission failed: %v", err)
	}
	router := gin.New()
	router.GET("/ok", admission, RequireAuth(authSvc, &captureSessionWriter{}, &captureFailureResponder{}), func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	req := admittedSessionControlRequest(http.MethodGet)
	req.URL.Path = "/ok"
	req.Header.Set(headerAuthorization, "Bearer token")
	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(router).ServeHTTP(rec, req)

	if rec.Code != http.StatusUnauthorized {
		t.Fatalf("status = %d, want %d", rec.Code, http.StatusUnauthorized)
	}
	if authSvc.controlCalls != 1 || authSvc.controlProof != "" {
		t.Fatalf("control calls = %d proof=%q, want one empty-proof call", authSvc.controlCalls, authSvc.controlProof)
	}
	if delegate.validateCalls != 0 || delegate.renewalCalls != 0 {
		t.Fatalf("fallback calls = strict:%d legacy-renewal:%d, want 0, 0", delegate.validateCalls, delegate.renewalCalls)
	}
	if got := rec.Header().Get(HeaderRefreshedToken); got != "" {
		t.Fatalf("%s = %q, want empty", HeaderRefreshedToken, got)
	}
}

func TestRequireAuthRejectsInvalidOptedInProofWithoutLegacyFallback(t *testing.T) {
	tests := []struct {
		name         string
		cookieHeader string
	}{
		{
			name:         "quoted proof",
			cookieHeader: SessionControlCookieName + `="` + testSessionControlProof + `"`,
		},
		{
			name:         "duplicate proof",
			cookieHeader: SessionControlCookieName + `=` + testSessionControlProof + `; ` + SessionControlCookieName + `=` + strings.Repeat("z", minSessionControlProofBytes),
		},
		{
			name:         "malformed proof",
			cookieHeader: SessionControlCookieName + `=invalid\\proof`,
		},
		{
			name:         "valid and malformed duplicate",
			cookieHeader: SessionControlCookieName + `=` + testSessionControlProof + `; ` + SessionControlCookieName + `=invalid\\proof`,
		},
		{
			name:         "oversized proof",
			cookieHeader: SessionControlCookieName + `=` + strings.Repeat("x", maxSessionControlProofBytes+1),
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			gin.SetMode(gin.TestMode)
			delegate := &fakeAuthenticator{
				claims:               &hostruntime.SessionClaims{SessionID: "legacy-session"},
				refreshedBearerToken: "unsafe-legacy-renewal",
			}
			authSvc := &controlAuthenticator{delegate: delegate, err: errors.New("control path must not run")}
			writer := &captureSessionWriter{}
			responder := &captureFailureResponder{}
			admission, err := NewSessionControlAdmission("https://app.example")
			if err != nil {
				t.Fatalf("NewSessionControlAdmission failed: %v", err)
			}

			reached := false
			router := gin.New()
			router.GET("/ok", admission, RequireAuth(authSvc, writer, responder), func(c *gin.Context) {
				reached = true
				c.Status(http.StatusNoContent)
			})

			req := admittedSessionControlRequest(http.MethodGet)
			req.URL.Path = "/ok"
			req.Header.Set(headerAuthorization, "Bearer token")
			req.Header.Set("Cookie", tc.cookieHeader)
			rec := httptest.NewRecorder()
			ProtectSessionCredentialResponses(router).ServeHTTP(rec, req)

			if rec.Code != http.StatusUnauthorized || !responder.wrote || reached {
				t.Fatalf("result status=%d responder=%v reached=%v, want 401, true, false", rec.Code, responder.wrote, reached)
			}
			if authSvc.controlCalls != 0 || delegate.validateCalls != 0 || delegate.renewalCalls != 0 {
				t.Fatalf("validation calls = control:%d strict:%d legacy-renewal:%d, want 0, 0, 0", authSvc.controlCalls, delegate.validateCalls, delegate.renewalCalls)
			}
			if writer.wrote || delegate.touched != "" {
				t.Fatalf("invalid attempt installed session: writer=%v touched=%q", writer.wrote, delegate.touched)
			}
			if got := rec.Header().Get(HeaderRefreshedToken); got != "" {
				t.Fatalf("%s = %q, want empty", HeaderRefreshedToken, got)
			}
			if cookies := rec.Result().Cookies(); len(cookies) != 0 {
				t.Fatalf("invalid attempt exposed response cookies: %#v", cookies)
			}
		})
	}
}

func TestRequireAuthRejectsInvalidOptedInProofWhenRenewalDisabled(t *testing.T) {
	gin.SetMode(gin.TestMode)
	delegate := &fakeAuthenticator{claims: &hostruntime.SessionClaims{SessionID: "strict-session"}}
	authSvc := &controlAuthenticator{delegate: delegate, err: errors.New("control path must not run")}
	writer := &captureSessionWriter{}
	responder := &captureFailureResponder{}
	admission, err := NewSessionControlAdmission("https://app.example")
	if err != nil {
		t.Fatalf("NewSessionControlAdmission failed: %v", err)
	}

	reached := false
	router := gin.New()
	router.GET("/socket", admission, WithoutSessionRenewal(), RequireAuth(authSvc, writer, responder), func(c *gin.Context) {
		reached = true
		c.Status(http.StatusNoContent)
	})

	req := admittedSessionControlRequest(http.MethodGet)
	req.URL.Path = "/socket"
	req.Header.Set(headerAuthorization, "Bearer token")
	req.Header.Set("Cookie", SessionControlCookieName+`=invalid\\proof`)
	rec := httptest.NewRecorder()
	ProtectSessionCredentialResponses(router).ServeHTTP(rec, req)

	if rec.Code != http.StatusUnauthorized || !responder.wrote || reached {
		t.Fatalf("result status=%d responder=%v reached=%v, want 401, true, false", rec.Code, responder.wrote, reached)
	}
	if authSvc.controlCalls != 0 || delegate.validateCalls != 0 || delegate.renewalCalls != 0 {
		t.Fatalf("validation calls = control:%d strict:%d legacy-renewal:%d, want 0, 0, 0", authSvc.controlCalls, delegate.validateCalls, delegate.renewalCalls)
	}
	if writer.wrote || delegate.touched != "" {
		t.Fatalf("invalid attempt installed session: writer=%v touched=%q", writer.wrote, delegate.touched)
	}
}

func TestRequireAuthPreservesLegacyRenewalForNonOptInClient(t *testing.T) {
	gin.SetMode(gin.TestMode)
	claims := &hostruntime.SessionClaims{
		SessionID: "session-1",
		User:      hostruntime.UserSession{ID: "005USER"},
	}
	delegate := &fakeAuthenticator{claims: claims, refreshedBearerToken: "legacy-renewed-token"}
	authSvc := &controlAuthenticator{delegate: delegate, err: errors.New("control path must not run")}
	router := gin.New()
	router.GET("/ok", RequireAuth(authSvc, &captureSessionWriter{}, &captureFailureResponder{}), func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	req := httptest.NewRequest(http.MethodGet, "/ok", nil)
	req.Header.Set(headerAuthorization, "Bearer token")
	rec := httptest.NewRecorder()
	ProtectSessionCredentialResponses(router).ServeHTTP(rec, req)

	if rec.Code != http.StatusNoContent {
		t.Fatalf("status = %d body=%s, want %d", rec.Code, rec.Body.String(), http.StatusNoContent)
	}
	if authSvc.controlCalls != 0 || delegate.validateCalls != 0 || delegate.renewalCalls != 1 {
		t.Fatalf("validation calls = control:%d strict:%d legacy-renewal:%d, want 0, 0, 1", authSvc.controlCalls, delegate.validateCalls, delegate.renewalCalls)
	}
	if got := rec.Header().Get(HeaderRefreshedToken); got != "legacy-renewed-token" {
		t.Fatalf("%s = %q, want legacy-renewed-token", HeaderRefreshedToken, got)
	}
}

func TestRequireAuthRejectsPartialOrUnsafeControlCredentialResults(t *testing.T) {
	claims := &hostruntime.SessionClaims{SessionID: "session-1", User: hostruntime.UserSession{ID: "005USER"}}
	tests := []struct {
		name                 string
		includeProof         bool
		refreshedBearerToken string
		proofCookieExpiresAt time.Time
	}{
		{
			name:                 "missing proof cannot replace bearer",
			refreshedBearerToken: "replacement",
			proofCookieExpiresAt: time.Now().Add(time.Hour),
		},
		{
			name:                 "replacement requires proof expiry",
			includeProof:         true,
			refreshedBearerToken: "replacement",
		},
		{
			name:                 "proof expiry requires replacement",
			includeProof:         true,
			proofCookieExpiresAt: time.Now().Add(time.Hour),
		},
		{
			name:                 "proof expiry must be future",
			includeProof:         true,
			refreshedBearerToken: "replacement",
			proofCookieExpiresAt: time.Now().Add(-time.Second),
		},
		{
			name:                 "replacement must be header safe",
			includeProof:         true,
			refreshedBearerToken: "replacement\nX-Injected: yes",
			proofCookieExpiresAt: time.Now().Add(time.Hour),
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			gin.SetMode(gin.TestMode)
			delegate := &fakeAuthenticator{claims: claims, refreshedBearerToken: "unsafe-legacy-renewal"}
			authSvc := &controlAuthenticator{
				delegate:             delegate,
				claims:               claims,
				refreshedBearerToken: tc.refreshedBearerToken,
				proofCookieExpiresAt: tc.proofCookieExpiresAt,
			}
			writer := &captureSessionWriter{}
			admission, err := NewSessionControlAdmission("https://app.example")
			if err != nil {
				t.Fatalf("NewSessionControlAdmission failed: %v", err)
			}
			router := gin.New()
			handlers := []gin.HandlerFunc{admission}
			handlers = append(handlers, RequireAuth(authSvc, writer, &captureFailureResponder{}), func(c *gin.Context) {
				c.Status(http.StatusNoContent)
			})
			router.GET("/ok", handlers...)

			req := admittedSessionControlRequest(http.MethodGet)
			req.URL.Path = "/ok"
			req.Header.Set(headerAuthorization, "Bearer token")
			if tc.includeProof {
				req.Header.Set("Cookie", SessionControlCookieName+"="+testSessionControlProof)
			}
			rec := httptest.NewRecorder()
			ProtectSessionCredentialResponses(router).ServeHTTP(rec, req)

			if rec.Code != http.StatusUnauthorized {
				t.Fatalf("status = %d, want %d", rec.Code, http.StatusUnauthorized)
			}
			if got := rec.Header().Get(HeaderRefreshedToken); got != "" {
				t.Fatalf("%s = %q, want empty", HeaderRefreshedToken, got)
			}
			for _, cookie := range rec.Result().Cookies() {
				if cookie.Name == SessionControlCookieName {
					t.Fatalf("unsafe validation exposed session-control cookie: %#v", cookie)
				}
			}
			if writer.wrote || delegate.touched != "" || delegate.validateCalls != 0 || delegate.renewalCalls != 0 {
				t.Fatalf("unsafe validation wrote=%v touched=%q strict=%d legacy-renewal=%d", writer.wrote, delegate.touched, delegate.validateCalls, delegate.renewalCalls)
			}
		})
	}
}

func TestOptionalAuthCallsControlAuthenticatorWithEmptyProofForAdmittedRequest(t *testing.T) {
	gin.SetMode(gin.TestMode)
	delegate := &fakeAuthenticator{
		claims:               &hostruntime.SessionClaims{SessionID: "legacy-session"},
		refreshedBearerToken: "unsafe-legacy-renewal",
	}
	authSvc := &controlAuthenticator{delegate: delegate, err: errors.New("control proof required")}
	writer := &captureSessionWriter{}
	admission, err := NewSessionControlAdmission("https://app.example")
	if err != nil {
		t.Fatalf("NewSessionControlAdmission failed: %v", err)
	}
	router := gin.New()
	router.GET("/ok", admission, OptionalAuth(authSvc, writer), func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	req := admittedSessionControlRequest(http.MethodGet)
	req.URL.Path = "/ok"
	req.Header.Set(headerAuthorization, "Bearer token")
	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(router).ServeHTTP(rec, req)

	if rec.Code != http.StatusNoContent {
		t.Fatalf("status = %d, want %d", rec.Code, http.StatusNoContent)
	}
	if authSvc.controlCalls != 1 || authSvc.controlProof != "" {
		t.Fatalf("control calls = %d proof=%q, want one empty-proof call", authSvc.controlCalls, authSvc.controlProof)
	}
	if delegate.validateCalls != 0 || delegate.renewalCalls != 0 || writer.wrote {
		t.Fatalf("fallback strict=%d legacy-renewal=%d writer=%v, want 0, 0, false", delegate.validateCalls, delegate.renewalCalls, writer.wrote)
	}
}

func TestOptionalAuthTreatsInvalidOptedInProofAsUnauthenticatedWithoutLegacyFallback(t *testing.T) {
	gin.SetMode(gin.TestMode)
	delegate := &fakeAuthenticator{
		claims:               &hostruntime.SessionClaims{SessionID: "legacy-session"},
		refreshedBearerToken: "unsafe-legacy-renewal",
	}
	authSvc := &controlAuthenticator{delegate: delegate, err: errors.New("control path must not run")}
	writer := &captureSessionWriter{}
	admission, err := NewSessionControlAdmission("https://app.example")
	if err != nil {
		t.Fatalf("NewSessionControlAdmission failed: %v", err)
	}

	reached := false
	router := gin.New()
	router.GET("/ok", admission, OptionalAuth(authSvc, writer), func(c *gin.Context) {
		reached = true
		c.Status(http.StatusNoContent)
	})

	req := admittedSessionControlRequest(http.MethodGet)
	req.URL.Path = "/ok"
	req.Header.Set(headerAuthorization, "Bearer token")
	req.Header.Set("Cookie", SessionControlCookieName+`=`+testSessionControlProof+`; `+SessionControlCookieName+`=invalid\\proof`)
	rec := httptest.NewRecorder()
	ProtectSessionCredentialResponses(router).ServeHTTP(rec, req)

	if rec.Code != http.StatusNoContent || !reached {
		t.Fatalf("result status=%d reached=%v, want 204 and true", rec.Code, reached)
	}
	if authSvc.controlCalls != 0 || delegate.validateCalls != 0 || delegate.renewalCalls != 0 {
		t.Fatalf("validation calls = control:%d strict:%d legacy-renewal:%d, want 0, 0, 0", authSvc.controlCalls, delegate.validateCalls, delegate.renewalCalls)
	}
	if writer.wrote || delegate.touched != "" {
		t.Fatalf("invalid attempt installed session: writer=%v touched=%q", writer.wrote, delegate.touched)
	}
	if got := rec.Header().Get(HeaderRefreshedToken); got != "" {
		t.Fatalf("%s = %q, want empty", HeaderRefreshedToken, got)
	}
	if cookies := rec.Result().Cookies(); len(cookies) != 0 {
		t.Fatalf("invalid attempt exposed response cookies: %#v", cookies)
	}
}

func TestOptionalAuthRejectsInvalidOptedInProofWhenRenewalDisabled(t *testing.T) {
	gin.SetMode(gin.TestMode)
	delegate := &fakeAuthenticator{claims: &hostruntime.SessionClaims{SessionID: "strict-session"}}
	authSvc := &controlAuthenticator{delegate: delegate, err: errors.New("control path must not run")}
	writer := &captureSessionWriter{}
	admission, err := NewSessionControlAdmission("https://app.example")
	if err != nil {
		t.Fatalf("NewSessionControlAdmission failed: %v", err)
	}

	reached := false
	router := gin.New()
	router.GET("/socket", admission, WithoutSessionRenewal(), OptionalAuth(authSvc, writer), func(c *gin.Context) {
		reached = true
		c.Status(http.StatusNoContent)
	})

	req := admittedSessionControlRequest(http.MethodGet)
	req.URL.Path = "/socket"
	req.Header.Set(headerAuthorization, "Bearer token")
	req.Header.Set("Cookie", SessionControlCookieName+`=invalid\\proof`)
	rec := httptest.NewRecorder()
	ProtectSessionCredentialResponses(router).ServeHTTP(rec, req)

	if rec.Code != http.StatusNoContent || !reached {
		t.Fatalf("result status=%d reached=%v, want 204 and true", rec.Code, reached)
	}
	if authSvc.controlCalls != 0 || delegate.validateCalls != 0 || delegate.renewalCalls != 0 {
		t.Fatalf("validation calls = control:%d strict:%d legacy-renewal:%d, want 0, 0, 0", authSvc.controlCalls, delegate.validateCalls, delegate.renewalCalls)
	}
	if writer.wrote || delegate.touched != "" {
		t.Fatalf("invalid attempt installed session: writer=%v touched=%q", writer.wrote, delegate.touched)
	}
}

func TestOptionalAuthUsesAdmittedControlProofAndRefreshesPairedCredentials(t *testing.T) {
	gin.SetMode(gin.TestMode)
	claims := &hostruntime.SessionClaims{SessionID: "session-1", User: hostruntime.UserSession{ID: "005USER"}}
	delegate := &fakeAuthenticator{claims: claims, refreshedBearerToken: "unsafe-legacy-renewal"}
	authSvc := &controlAuthenticator{
		delegate:             delegate,
		claims:               claims,
		refreshedBearerToken: "controlled-renewal",
		proofCookieExpiresAt: time.Now().Add(time.Hour),
	}
	admission, err := NewSessionControlAdmission("https://app.example")
	if err != nil {
		t.Fatalf("NewSessionControlAdmission failed: %v", err)
	}
	writer := &captureSessionWriter{}
	router := gin.New()
	router.GET("/ok", admission, OptionalAuth(authSvc, writer), func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	req := admittedSessionControlRequest(http.MethodGet)
	req.URL.Path = "/ok"
	req.Header.Set(headerAuthorization, "Bearer token")
	req.Header.Set("Cookie", SessionControlCookieName+"="+testSessionControlProof)
	rec := httptest.NewRecorder()
	ProtectSessionCredentialResponses(router).ServeHTTP(rec, req)

	if rec.Code != http.StatusNoContent || !writer.wrote || authSvc.controlCalls != 1 || authSvc.controlProof != testSessionControlProof {
		t.Fatalf("optional control result status=%d wrote=%v calls=%d proof=%q", rec.Code, writer.wrote, authSvc.controlCalls, authSvc.controlProof)
	}
	if got := rec.Header().Get(HeaderRefreshedToken); got != "controlled-renewal" {
		t.Fatalf("%s = %q, want controlled-renewal", HeaderRefreshedToken, got)
	}
	if cookies := rec.Result().Cookies(); len(cookies) != 1 || cookies[0].Name != SessionControlCookieName || cookies[0].Value != testSessionControlProof {
		t.Fatalf("session-control cookies = %#v, want one refreshed proof cookie", cookies)
	}
	if delegate.validateCalls != 0 || delegate.renewalCalls != 0 {
		t.Fatalf("fallback calls = strict:%d legacy-renewal:%d, want 0, 0", delegate.validateCalls, delegate.renewalCalls)
	}
}

func TestAuthDoesNotExposeSessionCredentialsWhenSessionWriterAborts(t *testing.T) {
	tests := []struct {
		name       string
		middleware func(hostruntime.SessionAuthenticator, SessionContextWriter) gin.HandlerFunc
	}{
		{
			name: "required auth",
			middleware: func(authSvc hostruntime.SessionAuthenticator, writer SessionContextWriter) gin.HandlerFunc {
				return RequireAuth(authSvc, writer, &captureFailureResponder{})
			},
		},
		{
			name: "optional auth",
			middleware: func(authSvc hostruntime.SessionAuthenticator, writer SessionContextWriter) gin.HandlerFunc {
				return OptionalAuth(authSvc, writer)
			},
		},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			gin.SetMode(gin.TestMode)
			claims := &hostruntime.SessionClaims{SessionID: "session-1", User: hostruntime.UserSession{ID: "005USER"}}
			delegate := &fakeAuthenticator{claims: claims, refreshedBearerToken: "unsafe-legacy-renewal"}
			authSvc := &controlAuthenticator{
				delegate:             delegate,
				claims:               claims,
				refreshedBearerToken: "controlled-renewal",
				proofCookieExpiresAt: time.Now().Add(time.Hour),
			}
			admission, err := NewSessionControlAdmission("https://app.example")
			if err != nil {
				t.Fatalf("NewSessionControlAdmission failed: %v", err)
			}
			writer := SessionContextWriterFunc(func(c *gin.Context, _ hostruntime.UserSession, _ string) {
				c.AbortWithStatus(http.StatusInternalServerError)
			})
			reachedHandler := false
			router := gin.New()
			router.GET("/ok", admission, tc.middleware(authSvc, writer), func(c *gin.Context) {
				reachedHandler = true
				c.Status(http.StatusNoContent)
			})

			req := admittedSessionControlRequest(http.MethodGet)
			req.URL.Path = "/ok"
			req.Header.Set(headerAuthorization, "Bearer token")
			req.Header.Set("Cookie", SessionControlCookieName+"="+testSessionControlProof)
			rec := httptest.NewRecorder()
			ProtectSessionCredentialResponses(router).ServeHTTP(rec, req)

			if rec.Code != http.StatusInternalServerError || reachedHandler {
				t.Fatalf("status = %d reached=%v, want %d and no downstream handler", rec.Code, reachedHandler, http.StatusInternalServerError)
			}
			if got := rec.Header().Get(HeaderRefreshedToken); got != "" {
				t.Fatalf("%s = %q, want empty", HeaderRefreshedToken, got)
			}
			for _, cookie := range rec.Result().Cookies() {
				if cookie.Name == SessionControlCookieName {
					t.Fatalf("aborted response exposed session-control cookie: %#v", cookie)
				}
			}
			if authSvc.controlCalls != 1 || delegate.touched != "session-1" {
				t.Fatalf("control calls = %d touched=%q, want 1 and session-1", authSvc.controlCalls, delegate.touched)
			}
		})
	}
}

func TestWithoutSessionRenewalIgnoresAdmittedControlProof(t *testing.T) {
	gin.SetMode(gin.TestMode)
	claims := &hostruntime.SessionClaims{SessionID: "session-1", User: hostruntime.UserSession{ID: "005USER"}}
	delegate := &fakeAuthenticator{claims: claims, refreshedBearerToken: "unsafe-renewal"}
	authSvc := &controlAuthenticator{
		delegate:             delegate,
		claims:               claims,
		refreshedBearerToken: "controlled-renewal",
	}
	admission, err := NewSessionControlAdmission("https://app.example")
	if err != nil {
		t.Fatalf("NewSessionControlAdmission failed: %v", err)
	}
	router := gin.New()
	router.GET("/socket", admission, WithoutSessionRenewal(), RequireAuth(authSvc, &captureSessionWriter{}, &captureFailureResponder{}), func(c *gin.Context) {
		c.Status(http.StatusNoContent)
	})

	req := admittedSessionControlRequest(http.MethodGet)
	req.URL.Path = "/socket"
	req.Header.Set(headerAuthorization, "Bearer token")
	req.Header.Set("Cookie", SessionControlCookieName+"="+testSessionControlProof)
	rec := httptest.NewRecorder()
	ProtectRefreshedTokenResponses(router).ServeHTTP(rec, req)

	if rec.Code != http.StatusNoContent {
		t.Fatalf("status = %d, want %d", rec.Code, http.StatusNoContent)
	}
	if authSvc.controlCalls != 0 || delegate.renewalCalls != 0 || delegate.validateCalls != 1 {
		t.Fatalf("validation calls = control:%d legacy-renewal:%d strict:%d, want 0, 0, 1", authSvc.controlCalls, delegate.renewalCalls, delegate.validateCalls)
	}
	if got := rec.Header().Get(HeaderRefreshedToken); got != "" {
		t.Fatalf("%s = %q, want empty", HeaderRefreshedToken, got)
	}
}
