package rest_api_test

// Docs:
// - Endpoint: https://developer.salesforce.com/docs/atlas.en-us.254.0.api_rest.meta/api_rest/resources_query.htm
// - Request: https://developer.salesforce.com/docs/atlas.en-us.254.0.api_rest.meta/api_rest/resources_query.htm
// - Response: https://developer.salesforce.com/docs/atlas.en-us.254.0.api_rest.meta/api_rest/resources_query.htm
// - Sub-schema: https://developer.salesforce.com/docs/atlas.en-us.254.0.api_rest.meta/api_rest/resources_query_more_results.htm
// - Errors: https://developer.salesforce.com/docs/atlas.en-us.254.0.api_rest.meta/api_rest/intro_rest.htm

import (
	"fmt"
	"net/http"
	"net/url"
	"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 TestGetQuery(t *testing.T) {
	contracttest.EnsureLocalRESTBaseline(t)

	testCases := []struct {
		name    string
		arrange func(t *testing.T) queryFixture
		act     func(t *testing.T, fixture queryFixture) *http.Response
		assert  func(t *testing.T, fixture queryFixture, resp *http.Response)
	}{
		{
			name: "returns matching records with documented query response shape",
			// Doc: https://developer.salesforce.com/docs/atlas.en-us.254.0.api_rest.meta/api_rest/resources_query.htm
			arrange: func(t *testing.T) queryFixture {
				return createAccountFixture(t, true)
			},
			act: func(t *testing.T, fixture queryFixture) *http.Response {
				client := liveapi.NewAuthenticatedClient(t)
				path := "/query?q=" + url.QueryEscape(fmt.Sprintf(
					"SELECT Id, Name FROM Account WHERE Id = '%s'",
					fixture.recordID,
				))
				resp, err := client.DoJSON(http.MethodGet, path, nil)
				require.NoError(t, err)
				return resp
			},
			assert: func(t *testing.T, fixture queryFixture, resp *http.Response) {
				require.Equal(t, http.StatusOK, resp.StatusCode)

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

				assert.GreaterOrEqual(t, body.TotalSize, 1)
				assert.True(t, body.Done)
				assert.Empty(t, body.NextRecordsURL)
				require.NotEmpty(t, body.Records)

				record := body.Records[0]
				assertQueryRecord(t, record, fixture.objectName, fixture.recordID, fixture.recordName)
			},
		},
		{
			name: "missing q parameter is rejected",
			// Doc: https://developer.salesforce.com/docs/atlas.en-us.254.0.api_rest.meta/api_rest/resources_query.htm
			arrange: func(t *testing.T) queryFixture {
				return queryFixture{path: "/query"}
			},
			act: func(t *testing.T, fixture queryFixture) *http.Response {
				client := liveapi.NewAuthenticatedClient(t)
				resp, err := client.DoJSON(http.MethodGet, fixture.path, nil)
				require.NoError(t, err)
				return resp
			},
			assert: func(t *testing.T, fixture queryFixture, resp *http.Response) {
				assertSalesforceErrorMessageContains(t, resp, http.StatusBadRequest, "MALFORMED_QUERY", "query string has to be specified")
			},
		},
		{
			name: "malformed SOQL is rejected",
			// Doc: https://developer.salesforce.com/docs/atlas.en-us.254.0.api_rest.meta/api_rest/resources_query.htm
			arrange: func(t *testing.T) queryFixture {
				return queryFixture{
					path: "/query?q=" + url.QueryEscape("SELECT FROM Account"),
				}
			},
			act: func(t *testing.T, fixture queryFixture) *http.Response {
				client := liveapi.NewAuthenticatedClient(t)
				resp, err := client.DoJSON(http.MethodGet, fixture.path, nil)
				require.NoError(t, err)
				return resp
			},
			assert: func(t *testing.T, fixture queryFixture, resp *http.Response) {
				assertSalesforceErrorMessageContains(t, resp, http.StatusBadRequest, "MALFORMED_QUERY", "unexpected token")
			},
		},
		{
			name: "missing bearer token is rejected",
			// Doc: https://developer.salesforce.com/docs/atlas.en-us.254.0.api_rest.meta/api_rest/resources_query.htm
			arrange: func(t *testing.T) queryFixture {
				return queryFixture{
					path: "/services/data/v63.0/query?q=" + url.QueryEscape("SELECT Id FROM Account LIMIT 1"),
				}
			},
			act: func(t *testing.T, fixture queryFixture) *http.Response {
				return contracttest.DoAnonymousRequest(t, http.MethodGet, fixture.path, nil)
			},
			assert: func(t *testing.T, fixture queryFixture, resp *http.Response) {
				contracttest.AssertSalesforceError(t, resp, http.StatusUnauthorized, "INVALID_SESSION_ID")
			},
		},
	}

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