package log

import (
	"context"
	"fmt"
	"time"

	"github.com/pingcap/errcode"
	pkg_http "github.com/tidbcloud/account/pkg/grpc/http"
	"github.com/tidbcloud/account/pkg/redact"
	"github.com/tidbcloud/pkgv2/auth/auth0"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"google.golang.org/grpc"
)

// LogUnaryServerInterceptor returns a new unary server interceptors that performs per-request tilog.
func LogUnaryServerInterceptor() grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
		startTime := time.Now()
		method := pkg_http.GetHttpMethod(ctx)
		ret, err := handler(ctx, req)
		if time.Since(startTime).Milliseconds() > 5000 {
			tilog.Warn(ctx, "slow api call", zap.String("url", pkg_http.GetHttpURI(ctx)), zap.Int64("time", time.Since(startTime).Milliseconds()))
		}
		// wangletian httpcode test
		//httpCode := http.StatusInternalServerError
		httpCode := 0
		if ec, ok := err.(errcode.ErrorCode); ok {
			httpCode = ec.Code().HTTPCode()
		}
		if e, ok := errs.AsTiError(err); ok {
			httpCode = e.HTTPCode()
		}

		//if ec, ok := err.(*status.Error); ok {
		//	grpcStatus := ec.GRPCStatus()
		//	httpCode = runtime.HTTPStatusFromCode(grpcStatus.Code())
		//}

		fields := func() []zap.Field {
			redaction := redact.PasswordFields
			payload, reqErr := redact.RedactKeysToJSON(redaction, req)
			if reqErr != nil {
				tilog.Error(ctx, "error during request redaction", zap.Error(reqErr))
			}
			resp, respErr := redact.RedactKeysToJSON(redaction, ret)
			if respErr != nil {
				tilog.Error(ctx, "error during response redaction", zap.Error(respErr))
			}
			return []zap.Field{
				zap.String("method", pkg_http.GetHttpMethod(ctx)),
				zap.Int("httpCode", httpCode),
				zap.String("url", pkg_http.GetHttpURI(ctx)),
				zap.Any("user", ctx.Value(auth0.AuthUserIDKey)),
				zap.String("msg", fmt.Sprintf("%T", req)),
				zap.String("request", string(payload)),
				zap.String("response", string(resp)),
			}
		}

		if err != nil {
			if httpCode == 0 {
				httpCode = 500
			}

			if httpCode >= 500 {
				tilog.Warn(ctx, "call central API failed", append(fields(), zap.Error(err))...)
			} else {
				tilog.Info(ctx, "call central API failed", append(fields(), zap.Error(err))...)
			}
		} else {
			if httpCode == 0 {
				httpCode = 200
			}
			if method != "" && method != "GET" && method != "OPTION" {
				tilog.Info(ctx, "mutation api call", fields()...)
			} else {
				tilog.Debug(ctx, "query api call", fields()...)
			}
		}
		return ret, err
	}
}
