package invocation

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

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"
)

func TestCreateJSONIsStrictAndBounded(t *testing.T) {
	gin.SetMode(gin.TestMode)
	for _, test := range []struct {
		name   string
		body   string
		status int
		code   string
	}{
		{name: "valid arbitrary input", body: `{"input":{"word":"画蛇添足","nested":{"n":1}}}`},
		{name: "valid exact version", body: `{"planVersionId":"plv_x","input":{}}`},
		{name: "empty exact version", body: `{"planVersionId":" ","input":{}}`, status: http.StatusBadRequest, code: "invocation_invalid_json"},
		{name: "null exact version", body: `{"planVersionId":null,"input":{}}`, status: http.StatusBadRequest, code: "invocation_invalid_json"},
		{name: "identity cannot be supplied", body: `{"input":{},"principal":{"userId":"usr_x"}}`, status: http.StatusBadRequest, code: "invocation_invalid_json"},
		{name: "binding cannot be supplied", body: `{"input":{},"bindingId":"wbd_x"}`, status: http.StatusBadRequest, code: "invocation_invalid_json"},
		{name: "duplicate root key", body: `{"input":{},"input":{}}`, status: http.StatusBadRequest, code: "invocation_invalid_json"},
		{name: "duplicate nested input key", body: `{"input":{"word":"a","word":"b"}}`, status: http.StatusBadRequest, code: "invocation_invalid_json"},
		{name: "multiple values", body: `{"input":{}} {}`, status: http.StatusBadRequest, code: "invocation_invalid_json"},
		{name: "object required", body: `null`, status: http.StatusBadRequest, code: "invocation_invalid_json"},
		{name: "oversized", body: `{"input":{"x":"` + strings.Repeat("x", int(maxWorkflowInvocationRequestBytes)) + `"}}`, status: http.StatusRequestEntityTooLarge, code: "invocation_request_too_large"},
	} {
		t.Run(test.name, func(t *testing.T) {
			recorder := httptest.NewRecorder()
			ctx, _ := gin.CreateTestContext(recorder)
			ctx.Request = httptest.NewRequest(http.MethodPost, "/", strings.NewReader(test.body))
			_, err := decodeWorkflowInvocationCreateJSON(ctx)
			if test.status == 0 {
				require.NoError(t, err)
				return
			}
			requireInvocationHTTPError(t, err, test.status, test.code)
		})
	}
}

func TestCreateJSONRejectsExcessiveNesting(t *testing.T) {
	payload := `{"input":` + strings.Repeat("[", maxWorkflowInvocationJSONNesting+1) + strings.Repeat("]", maxWorkflowInvocationJSONNesting+1) + `}`
	ctx, _ := gin.CreateTestContext(httptest.NewRecorder())
	ctx.Request = httptest.NewRequest(http.MethodPost, "/", strings.NewReader(payload))
	_, err := decodeWorkflowInvocationCreateJSON(ctx)
	requireInvocationHTTPError(t, err, http.StatusBadRequest, "invocation_invalid_json")
}
