/******************************************************************************
 * Copyright (c)  2022 PingCAP                                                *
 * Licensed under the Apache License, Version 2.0 (the "License");            *
 * you may not use this file except in compliance with the License.           *
 * You may obtain a copy of the License at                                    *
 *                                                                            *
 * http://www.apache.org/licenses/LICENSE-2.0                                 *
 *                                                                            *
 * Unless required by applicable law or agreed to in writing, software        *
 * distributed under the License is distributed on an "AS IS" BASIS,          *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
 * See the License for the specific language governing permissions and        *
 * limitations under the License.                                             *
 ******************************************************************************/

package interceptor

import (
	"encoding/json"
	"io"
	"runtime"
	"strings"

	"github.com/gin-gonic/gin"
	log "github.com/sirupsen/logrus"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/errors"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/message"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/models"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/utils"
)

// ResolveRequestParameters
// @Description: resolve request params to set gin context
// @Parameter c
func ResolveRequestParameters(c *gin.Context) {
	params, err := getAllRequestParameters(c)
	if err != nil {
		c.JSON(err.(errors.Error).Code().GetHttpCode(), message.Result{
			Code:    int(err.(errors.Error).Code()),
			Message: err.(errors.Error).Error(),
		})
		c.Abort()
	} else {
		// set context
		c.Set(models.GlobalServerRequestParametersKey, params)
		c.Next()
	}
}

// getAllRequestParameters
// @Description: get all request parameter
// @Parameter c
// @return ReqParams
// @return error
func getAllRequestParameters(c *gin.Context) (models.RequestParameters, error) {
	log.Debugf("current request method %s, url: %s", c.Request.Method, c.Request.URL)
	reqParams := models.RequestParameters{}

	// define map container, filling common request parameters
	allParams := make(map[string]interface{})

	// headers
	reqParams.Headers = make(map[string]string)
	for k, v := range c.Request.Header {
		vs := strings.Join(v, ",")
		reqParams.Headers[k] = vs
		allParams[k] = vs
	}

	// query params
	reqParams.QueryParams = make(map[string]string)
	for k, v := range c.Request.URL.Query() {
		vs := strings.Join(v, ",")
		reqParams.QueryParams[k] = vs
		allParams[k] = vs
	}

	// path params
	reqParams.PathParams = make([]gin.Param, 0)
	for _, param := range c.Params {
		reqParams.PathParams = append(reqParams.PathParams, param)
		allParams[param.Key] = param.Value
	}

	// form params
	if err := c.Request.ParseForm(); err != nil {
		log.Errorf("parse form failed. error: %s", err.Error())
		return reqParams, errors.New(errors.ParamParseError, err.Error())
	}
	// if Content-Type is of type multipart/form-data, call ParseMultipartForm to parse the parameters.
	if strings.Contains(reqParams.Headers["Content-Type"], "multipart/form-data") {
		if err := c.Request.ParseMultipartForm(int64(runtime.MemProfileRate)); err != nil {
			log.Errorf("parse multipart form failed. error: %s", err.Error())
			return reqParams, errors.New(errors.ParamParseError, err.Error())
		}
	}
	reqParams.FormParams = make(map[string]string)
	for k, v := range c.Request.PostForm {
		vs := strings.Join(v, ",")
		reqParams.FormParams[k] = vs
		allParams[k] = vs
	}

	// body params
	body, err := io.ReadAll(c.Request.Body)
	if err != nil {
		log.Errorf("read request body failed. error: %s", err.Error())
		return reqParams, errors.New(errors.IOReadFileError, err.Error())
	}
	reqParams.BodyParams = string(body)

	// filling request body to all params
	if len(body) > 0 {
		if err = json.Unmarshal(body, &allParams); err != nil {
			log.Errorf("json unmarshal failed. error: %s", err.Error())
			return reqParams, errors.New(errors.UnmarshalError, err.Error())
		}
	}
	if err = utils.ConvertObj(allParams, &reqParams.CommonReqInfo); err != nil {
		log.Errorf("convert obj failed. error: %s", err.Error())
		return reqParams, errors.New(errors.ConvertError, err.Error())
	}

	return reqParams, nil
}
