package rest_api_test

// Docs:
// - Endpoint: https://help.salesforce.com/s/articleView?id=sf.remoteaccess_using_userinfo_endpoint.htm&type=5
// - Request: https://help.salesforce.com/s/articleView?id=sf.remoteaccess_using_userinfo_endpoint.htm&type=5
// - Response: https://help.salesforce.com/s/articleView?id=sf.remoteaccess_using_userinfo_endpoint.htm&type=5
// - Sub-schema: https://help.salesforce.com/s/articleView?id=sf.remoteaccess_using_userinfo_endpoint.htm&type=5
// - Errors: https://help.salesforce.com/s/articleView?id=sf.remoteaccess_using_userinfo_endpoint.htm&type=5

import (
	"net/http"
	"testing"

	"github.com/nexuscrm/tests/contract/internal/contracttest"
	"github.com/nexuscrm/tests/contract/internal/liveapi"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestGetUserInfo(t *testing.T) {
	contracttest.EnsureLocalRESTBaseline(t)

	type userInfoResponse struct {
		Sub               string          `json:"sub"`
		UserID            string          `json:"user_id"`
		OrganizationID    string          `json:"organization_id"`
		PreferredUsername string          `json:"preferred_username"`
		Nickname          string          `json:"nickname"`
		Name              string          `json:"name"`
		Email             string          `json:"email"`
		EmailVerified     bool            `json:"email_verified"`
		GivenName         string          `json:"given_name"`
		FamilyName        string          `json:"family_name"`
		Profile           string          `json:"profile"`
		Picture           string          `json:"picture"`
		Address           map[string]any  `json:"address"`
		PhoneNumber       string          `json:"phone_number"`
		UserType          string          `json:"user_type"`
		Language          string          `json:"language"`
		ProfileID         string          `json:"profile_id"`
		IsActive          bool            `json:"is_active"`
		Permissions       map[string]bool `json:"permissions"`
	}

	testCases := []struct {
		name    string
		arrange func(t *testing.T) string
		act     func(t *testing.T, path string) *http.Response
		assert  func(t *testing.T, resp *http.Response)
	}{
		{
			// Doc: https://help.salesforce.com/s/articleView?id=sf.remoteaccess_using_userinfo_endpoint.htm&type=5
			name: "authenticated request returns oidc-compatible user claims",
			arrange: func(t *testing.T) string {
				return "/services/oauth2/userinfo"
			},
			act: func(t *testing.T, path string) *http.Response {
				client := liveapi.NewAuthenticatedClient(t)
				resp, err := client.DoJSON(http.MethodGet, path, nil)
				require.NoError(t, err)
				return resp
			},
			assert: func(t *testing.T, resp *http.Response) {
				require.Equal(t, http.StatusOK, resp.StatusCode)
				contracttest.AssertJSONContentType(t, resp)

				var body userInfoResponse
				liveapi.DecodeJSON(t, resp, &body)

				require.NotEmpty(t, body.Sub)
				contracttest.AssertSalesforceID(t, body.UserID)
				contracttest.AssertSalesforceID(t, body.OrganizationID)
				assert.NotEmpty(t, body.PreferredUsername)
				assert.NotEmpty(t, body.Name)
				assert.NotEmpty(t, body.Email)
				assert.True(t, body.EmailVerified)
				assert.NotEmpty(t, body.GivenName)
				assert.NotEmpty(t, body.Profile)
				assert.Contains(t, body.Profile, body.UserID)
				assert.NotNil(t, body.Address)
				assert.NotEmpty(t, body.UserType)
				assert.NotEmpty(t, body.Language)
				assert.True(t, body.IsActive)
				assert.NotNil(t, body.Permissions)
			},
		},
		{
			// Doc: https://help.salesforce.com/s/articleView?id=sf.remoteaccess_using_userinfo_endpoint.htm&type=5
			name: "missing bearer token returns invalid token error",
			arrange: func(t *testing.T) string {
				return "/services/oauth2/userinfo"
			},
			act: func(t *testing.T, path string) *http.Response {
				return contracttest.DoAnonymousRequest(t, http.MethodGet, path, nil)
			},
			assert: func(t *testing.T, resp *http.Response) {
				require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
				contracttest.AssertJSONContentType(t, resp)

				var body map[string]any
				liveapi.DecodeJSON(t, resp, &body)

				assert.Equal(t, "invalid_token", body["error"])
				assert.Len(t, body, 1)
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			path := tc.arrange(t)
			resp := tc.act(t, path)
			tc.assert(t, resp)
		})
	}
}
