package controller

import (
	"encoding/json"

	"github.com/gin-gonic/gin"

	"github.com/pingcap-inc/security/baseline/api/v1/controller/errors"
)

type RpcRequest struct {
	Request interface{}
}

type RpcResponse struct {
	Code     errors.BL_ERROR_CODE
	Message  string
	Response string
	Page     *Page
}

func (rsp *RpcResponse) GetCode() errors.BL_ERROR_CODE {
	return rsp.Code
}

func (rsp *RpcResponse) GetMessage() string {
	return rsp.Message
}

func NewRpcRequest(requestBody string, request interface{}) (*RpcRequest, *RpcResponse, error) {
	err := json.Unmarshal([]byte(requestBody), request)
	if err != nil {
		return nil, &RpcResponse{
			Code:    errors.BL_UNMARSHAL_ERROR,
			Message: err.Error(),
			Page:    nil,
		}, nil
	} else {
		return &RpcRequest{
			Request: request,
		}, nil, err
	}
}

func InvokeHandler(
	ctx *gin.Context,
	handler func(ctx *gin.Context, req *RpcRequest) (*RpcResponse, error),
	requestBody string,
	request interface{},
	response interface{}) {

	var rpcRequest *RpcRequest
	var rpcResponse *RpcResponse
	var withPage func() Page = nil

	rpcRequest, rpcResponse, err := NewRpcRequest(requestBody, request)
	if rpcRequest != nil {
		rpcResponse, err = handler(ctx, rpcRequest)
	}

	if err == nil && rpcResponse.Page != nil {
		withPage = func() Page {
			return Page{
				PageRequest: rpcResponse.Page.PageRequest,
				Total:       int64(rpcResponse.Page.Total)}
		}
	}

	HandleHttpResponse(ctx,
		err,
		func() (errors.BL_ERROR_CODE, string) {
			return errors.BL_ERROR_CODE(rpcResponse.GetCode()), rpcResponse.GetMessage()
		},
		func() (interface{}, error) {
			marshalError := json.Unmarshal([]byte(rpcResponse.Response), response)
			if marshalError != nil {
				return nil, errors.NewError(errors.BL_UNMARSHAL_ERROR, marshalError.Error())
			} else {
				return response, nil
			}
		},
		withPage,
	)
}

type DefaultReq struct{}
type DefaultRsp struct{}

func defaultHandler(ctx *gin.Context, rpcReq *RpcRequest) (*RpcResponse, error) {
	if response, err := json.Marshal(&DefaultRsp{}); err != nil {
		return nil, err
	} else {
		return &RpcResponse{
			Code:     errors.BL_SUCCESS,
			Response: string(response),
		}, err
	}
}

func DefaultHandler(ctx *gin.Context) {
	if requestBody, ok := HandleJsonRequestFromBody(ctx, &DefaultReq{}); ok {
		InvokeHandler(ctx, defaultHandler, requestBody, &DefaultReq{}, &DefaultRsp{})
	}
}
