package log

import (
	"context"
	"time"

	"github.com/pingcap/errcode"
	"github.com/tidbcloud/pkgv2/auth/auth0"
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/log"
	"go.uber.org/zap"
	"google.golang.org/grpc"
)

// UnaryServerInterceptor returns a new unary server interceptor that performs per-request logging.
func UnaryServerInterceptor() grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
		start := time.Now()
		logger := log.WithContextL(ctx).
			With(zap.String("method", info.FullMethod)).
			WithOptions(zap.AddStacktrace(zap.PanicLevel))

		logger.Info("rpc begin",
			zap.Any("req", req),
			zap.Any("user", ctx.Value(auth0.AuthUserIDKey)),
		)

		resp, err := handler(ctx, req)
		cost := time.Since(start)
		httpCode := getHTTPCode(err)

		if err != nil {
			logger.Error("rpc failed",
				zap.String("elapsed", cost.String()),
				zap.Int("httpCode", httpCode),
				zap.String("error", err.Error()),
				zap.Any("resp", resp),
			)
		} else {
			logger.Info("rpc succeed",
				zap.String("elapsed", cost.String()),
				zap.Int("httpCode", httpCode),
			)
		}

		return resp, err
	}
}

func getHTTPCode(err error) int {
	if err == nil {
		return 200
	}

	if berr, ok := err.(bizerr.BizError); ok {
		if code := berr.HttpStatus(); code != 0 {
			return code
		}
	}
	if ec, ok := err.(errcode.ErrorCode); ok {
		if code := ec.Code().HTTPCode(); code != 0 {
			return code
		}
	}

	return 500
}
