package server

import (
	"context"
	"net/http"

	spb "google.golang.org/genproto/googleapis/rpc/status"

	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

var errCode2tiErrCodeMapping = map[codes.Code]errs.Error{
	codes.Internal:                     errs.ErrInternalUnknown,
	codes.NotFound:                     errs.ErrNotFound,
	codes.Unauthenticated:              errs.ErrUnauthorized,
	codes.PermissionDenied:             errs.ErrForbidden,
	codes.InvalidArgument:              errs.ErrBadRequest,
	codes.Code(errs.ErrCodeBadRequest): errs.ErrBadRequest,
	codes.Code(errs.ErrCodeForbidden):  errs.ErrForbidden,
	codes.Code(errs.ErrCodeDB):         errs.ErrInternalUnknown,
}

func OpenAPIHTTPErrorHandler(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, r *http.Request, err error) {
	s := status.Convert(err)
	if s == nil {
		runtime.DefaultHTTPErrorHandler(ctx, mux, marshaler, w, r, err)
	}
	if isOpenApiGrpcGatewayReq(r) {
		if s.Code() >= 400 && s.Code() < 600 {
			// return type is tierror
			newerr := runtime.HTTPStatusError{
				HTTPStatus: int(s.Code()),
				Err:        err,
			}
			w.WriteHeader(int(s.Code()))
			runtime.DefaultHTTPErrorHandler(ctx, mux, marshaler, w, r, &newerr)
		} else {
			// return type from grpc framework, update grpc code to http code
			st := runtime.HTTPStatusFromCode(s.Code())
			newerr := runtime.HTTPStatusError{
				HTTPStatus: st,
				Err: status.ErrorProto(&spb.Status{
					Code:    int32(st),
					Message: s.Message(),
				}),
			}
			runtime.DefaultHTTPErrorHandler(ctx, mux, marshaler, w, r, &newerr)
		}
		return
	}

	code := errs.ErrCodeUnknown
	httpStatus := http.StatusInternalServerError
	message := s.Message()

	tilog.Info(ctx, "Converting gRPC error to OpenAPI format")
	errCode := s.Code()
	tiErr, ok := errCode2tiErrCodeMapping[errCode]
	if ok {
		code = tiErr.BizCode()
	} else if errCode > 100 {
		code = int64(errCode)
	}
	if tiErr != nil {
		httpStatus = tiErr.HTTPCode()
	}

	newError := runtime.HTTPStatusError{
		HTTPStatus: httpStatus,
		Err:        status.Error(codes.Code(code), message),
	}
	runtime.DefaultHTTPErrorHandler(ctx, mux, marshaler, w, r, &newError)
}
