package httpapi

import (
	"fmt"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/validator/v10"
	"github.com/tidbcloud/observability/pkg/common/errors"
)

// HandleJsonRequestWithBuiltReq
// @Description: handle common json request with a built request
// validate request using validator.New().Struct(req)
// serialize request using json.Marshal(req)
// @Parameter c
// @Parameter req
// @Parameter appenders append request data out of http body, such as id in path
// @return bool
// @return requestBody
func HandleJsonRequestWithBuiltReq(c *gin.Context, req interface{}) (s bool) {
	return HandleRequest(c,
		req,
		func(c *gin.Context, req interface{}) error {
			return nil
		},
		func(req interface{}) error {
			return validator.New().Struct(req)
		},
	)
}

// HandleJsonRequestFromQuery
// @Description: handle common json request from query
// build request using c.ShouldBindQuery(req, binding.JSON)
// validate request using validator.New().Struct(req)
// @Parameter c
// @Parameter req
// @return ok
// @return requestBody
func HandleJsonRequestFromQuery(c *gin.Context,
	req interface{},
) (ok bool) {
	return HandleRequest(c,
		req,
		func(c *gin.Context, req interface{}) error {
			err := c.ShouldBindQuery(req)
			if err != nil {
				return err
			}
			return nil
		},
		func(req interface{}) error {
			return validator.New().Struct(req)
		},
	)
}

// HandleJsonRequestFromBody
// @Description: handle common json request from body
// build request using c.ShouldBindBodyWith(req, binding.JSON)
// validate request using validator.New().Struct(req)
// @Parameter c
// @Parameter req
// @return err
// @return requestBody
func HandleJsonRequestFromBody(c *gin.Context,
	req interface{},
) (ok bool) {
	return HandleRequest(c,
		req,
		func(c *gin.Context, req interface{}) error {
			err := c.ShouldBindBodyWith(req, binding.JSON)
			if err != nil {
				return err
			}

			return nil
		},
		func(req interface{}) error {
			return validator.New().Struct(req)
		},
	)
}

// HandleRequest
// @Description: handle request
// @Parameter c
// @Parameter req
// @Parameter builder build request from gin.Context
// @Parameter validator validate request
// @Parameter serializer serialize request to string
// @return err
// @return requestBody
func HandleRequest(c *gin.Context,
	req interface{},
	builder func(c *gin.Context, req interface{}) error,
	validator func(req interface{}) error) bool {
	ok := errors.OfNullable(nil).
		BreakIf(func() error {
			return errors.OfNullable(builder(c, req)).
				Map(func(err error) error {
					return errors.New(errors.MarshalError, err.Error())
				}).
				Present()
		}).
		BreakIf(func() error {
			return errors.OfNullable(validator(req)).
				Map(func(err error) error {
					return errors.New(errors.ParameterInvalid, fmt.Sprintf("Invalid params: %s", err.Error()))
				}).
				Present()
		}).
		If(func(err error) {
			Fail(c, err)
		}).
		IfNil()
	return ok
}
