package grpcutil

import (
	"context"
	"fmt"

	"github.com/tidbcloud/devops/api"
	"github.com/tidbcloud/devops/pkg/infra/errs"
	"github.com/tidbcloud/devops/pkg/infra/log"

	"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/serverless-service/idl/pbgen/proto/common"
	"go.buf.build/protocolbuffers/go/linuxgit/tidbcloud/base"
	"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func RespToErr(resp *base.BaseResp) error {
	if resp == nil {
		return fmt.Errorf("base resp is nil")
	}

	if resp.ErrCode == 0 {
		return nil
	}

	berr := bizerr.Restore(bizerr.ErrCode(resp.ErrCode), resp.ErrMsg)
	if bizerr.IsRequestErr(berr.ErrCode()) {
		return errs.APIErrorNew(api.ErrCodeInvalidArgument, berr)
	}
	return berr
}

func RespToErrServerless(resp *common.BaseResp) error {
	if resp == nil {
		return fmt.Errorf("base resp is nil")
	}

	if resp.ErrCode == 0 {
		return nil
	}

	berr := bizerr.Restore(bizerr.ErrCode(resp.ErrCode), resp.ErrMsg)
	if bizerr.IsRequestErr(berr.ErrCode()) {
		return errs.APIErrorNew(api.ErrCodeInvalidArgument, berr)
	}
	return berr
}

type presetDailOption struct{}

func PresetDailOption() presetDailOption {
	return presetDailOption{}
}

func (o presetDailOption) Default() []grpc.DialOption {
	opts := []grpc.DialOption{
		o.WithInsecure(),
		o.WithLoadBalance(),
		o.WithTrace(),
	}
	return opts
}

func (o presetDailOption) WithInsecure() grpc.DialOption {
	return grpc.WithTransportCredentials(insecure.NewCredentials())
}

func (o presetDailOption) WithLoadBalance() grpc.DialOption {
	return grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy":"round_robin"}`)
}

func (o presetDailOption) WithTrace() grpc.DialOption {
	return grpc.WithStatsHandler(otelgrpc.NewClientHandler())
}

func (o presetDailOption) WithLog() []grpc.DialOption {
	return []grpc.DialOption{
		grpc.WithChainUnaryInterceptor(
			logging.UnaryClientInterceptor(InterceptorLogger()),
		),
		grpc.WithChainStreamInterceptor(
			logging.StreamClientInterceptor(InterceptorLogger()),
		),
	}
}

func InterceptorLogger() logging.Logger {
	return logging.LoggerFunc(func(ctx context.Context, lvl logging.Level, msg string, fields ...any) {
		l := log.FromContext(ctx, fields...)

		switch lvl {
		case logging.LevelDebug:
			l.V(4).Info(msg)
		case logging.LevelInfo:
			l.Info(msg)
		case logging.LevelWarn:
			l.Info(msg)
		case logging.LevelError:
			l.Error(fmt.Errorf("grpc error"), msg)
		default:
			panic(fmt.Sprintf("unknown level %v", lvl))
		}
	})
}
