package utils

import (
	"context"
	"fmt"
	"net/http"
	"runtime/debug"

	"google.golang.org/grpc/codes"

	"github.com/tidbcloud/account/idl/pbgen/proto/central"
	"github.com/tidbcloud/tiinf/errs"
	"google.golang.org/grpc/status"

	"github.com/getsentry/sentry-go"
	"github.com/pingcap/errcode"
	"github.com/pingcap/errors"
	"github.com/tidbcloud/tiinf/monitor/tilog"
)

var httpCode2GrpcCodeMap = map[int]codes.Code{
	http.StatusBadRequest:     codes.InvalidArgument,
	http.StatusForbidden:      codes.PermissionDenied,
	http.StatusNotFound:       codes.NotFound,
	http.StatusRequestTimeout: codes.DeadlineExceeded,
	http.StatusUnauthorized:   codes.Unauthenticated,
}

func PanicHandler(ctx context.Context, p interface{}) error {
	logger := tilog.WithContextL(ctx)
	err, ok := p.(error)
	if ok && errors.HasStack(err) {
		logger.Sugar().Warnf("panic, err: %s, stack: %s", err, errors.GetStackTracer(err).StackTrace())
	} else {
		logger.Sugar().Warnf("panic, err: %s, stack: %s", p, debug.Stack())
	}

	// I don't know why the usual stack trace is not working
	// So putting debug.Stack() into the message for now
	if !ok {
		err = fmt.Errorf("%+v\n%s", p, debug.Stack())
		return errcode.NewInternalErr(err)
	}

	// TODO(chenfei): Should be replaced by grpc_sentry.UnaryServerInterceptor
	// report panic to sentry
	sentry.CurrentHub().Recover(err)

	return errcode.NewInternalErr(err)
}

func ConvertErr2GrpcErr(err error) error {
	if err == nil {
		return err
	}
	e, ok := err.(errs.Error)
	if !ok {
		return err
	}
	httpCode := e.HTTPCode()
	grpcCode, ok := httpCode2GrpcCodeMap[httpCode]
	if !ok {
		return e
	}
	return status.Error(grpcCode, e.ErrMsg())
}

func ConvertMapToProto(data map[string]string) []*central.KVMap {
	var res []*central.KVMap
	for k, v := range data {
		item := central.KVMap{Key: k, Value: v}
		res = append(res, &item)
	}
	return res
}
