package api

import (
	"dataservices/commons/constants"
	dto "dataservices/internal/dto/dataapi"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

// error return struct definition
type APIError struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

func (err APIError) Error() string {
	return err.Message
}

// middleware error handler in sqleditor package
func APIErrorJSONReporter() gin.HandlerFunc {
	return APIErrorJSONReporterHandler(gin.ErrorTypeAny)
}

func APIErrorJSONReporterHandler(errType gin.ErrorType) gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Next()
		innerErrors := c.Errors.ByType(errType)

		if len(innerErrors) > 0 {
			err := innerErrors[0].Err
			code := http.StatusInternalServerError
			httpStatus := http.StatusOK

			if cerr, ok := err.(dto.CommonError); ok {
				if te, ok := errs.AsTiError(cerr.Raw()); ok {
					code = te.HTTPCode()
				}
			} else if te, ok := errs.AsTiError(err); ok {
				code = te.HTTPCode()
			}

			if code == 401 {
				httpStatus = http.StatusUnauthorized
			}

			var response interface{}
			if strings.HasSuffix(c.FullPath(), "/orgs/:org_id/projects/:project_id/apps/:app_id/endpoints/:endpoint_id/test") {
				epRes := dto.NewEndpointResult()
				epRes.Type = constants.EndpointResTypeSQL
				epRes.Data.Result.Code = code
				epRes.Data.Result.Message = err.Error()
				response = epRes
			} else if _, ok := err.(dto.CommonError); ok {
				response = dto.FailedV2(code, err.Error())
			} else {
				response = APIError{
					Code:    code,
					Message: err.Error(),
				}
			}

			tilog.Error(c.Request.Context(), "api error",
				zap.Int("api_code", code),
				zap.Int("http_raw_status", c.Writer.Status()),
				zap.String("method", c.Request.Method),
				zap.String("path", c.Request.URL.Path),
				zap.Error(err))
			c.JSON(httpStatus, response)
			c.Abort()
			return
		}
	}
}
