package idp

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

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	"go.uber.org/zap/zaptest/observer"
)

func TestRegistrationRejectionLogsOnlySafeMetadata(t *testing.T) {
	for _, tc := range []struct {
		name, body, reason, field, issue string
	}{
		{
			name: "redirect policy",
			body: `{
				"redirect_uris": ["https://username:private-password@oauth-redirect-sandbox.googleusercontent.com/r/private-path?code=private-code#private-fragment"],
				"client_name": "private-name",
				"scope": "private-scope"
			}`,
			reason: "redirect_policy", issue: "userinfo",
		},
		{
			name:   "encoded path",
			body:   `{"redirect_uris":["https://oauth-redirect-sandbox.googleusercontent.com/r/private%2Fpath"]}`,
			reason: "redirect_policy", issue: "encoded_path",
		},
		{
			name:   "unmatched prefix",
			body:   `{"redirect_uris":["https://oauth-redirect-sandbox.googleusercontent.com/private-path"]}`,
			reason: "redirect_policy", issue: "unmatched_prefix",
		},
		{
			name: "metadata type",
			body: `{
				"scope": ["private-scope"],
				"redirect_uris": ["https://oauth-redirect-sandbox.googleusercontent.com/r/private-path?code=private-code"]
			}`,
			reason: "invalid_json", field: "scope", issue: "query",
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			gin.SetMode(gin.TestMode)
			core, logs := observer.New(zap.WarnLevel)
			idp := &IDPRouter{
				logger: zap.New(core), redirectRestricted: true,
				redirectPrefixes: "https://oauth-redirect.googleusercontent.com/r/",
			}
			router := gin.New()
			router.POST("/register", idp.handleRegister)
			req := httptest.NewRequest(http.MethodPost, "/register", strings.NewReader(tc.body))
			req.Header.Set("Content-Type", "application/json")
			req.Header.Set("Authorization", "Bearer private-bearer")
			req.Header.Set("Cookie", "session=private-cookie")
			response := httptest.NewRecorder()
			router.ServeHTTP(response, req)
			require.Equal(t, http.StatusBadRequest, response.Code)
			require.Len(t, logs.All(), 1)
			fields := logs.All()[0].ContextMap()
			require.Equal(t, tc.reason, fields["reason"])
			require.Equal(t, []interface{}{tc.issue}, fields["redirect_issues"])
			if tc.field != "" {
				require.Equal(t, tc.field, fields["field"])
				require.Equal(t, "array", fields["received_type"])
			}
			encoded, err := json.Marshal(fields)
			require.NoError(t, err)
			require.NotContains(t, string(encoded), "private-")
			require.Contains(t, string(encoded), "https://oauth-redirect-sandbox.googleusercontent.com")
		})
	}
}

func TestRegistrationRejectionBoundsLoggedOrigins(t *testing.T) {
	core, logs := observer.New(zap.WarnLevel)
	idp := &IDPRouter{logger: zap.New(core)}
	idp.logRegistrationRejection("redirect_policy", registrationRequest{RedirectURIs: make([]string, 100)}, nil)
	fields := logs.All()[0].ContextMap()
	require.Equal(t, int64(100), fields["redirect_count"])
	require.Len(t, fields["redirect_origins"], 8)
	require.Len(t, fields["redirect_issues"], 8)
}
