package globalserverlesscli

import (
	"errors"
	"net/http"
	"strings"
	"time"

	"github.com/sercand/kuberesolver/v5"
	tigrpcclient "github.com/tidbcloud/tiinf/transport/tigrpc/client"
	"google.golang.org/grpc"

	globalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/serverlessglobal"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/pkg/config"
	grpcutil "github.com/tidbcloud/tidb-management-service/utilities/grpc"
)

const dataMigrationHTTPTimeout = 30 * time.Second

type TimeoutCallOption struct {
	grpc.EmptyCallOption
}

func NewClient(cfg *config.ServerlessGlobalService) (serverlessglobal.ServerlessGlobal, error) {
	if cfg == nil {
		return nil, errors.New("config is nil")
	}

	if cfg.Local {
		return &fakeImpl{}, nil
	}

	var (
		conn *grpc.ClientConn
		err  error
	)

	interceptors := tigrpcclient.WithUnaryInterceptors(
		grpcutil.AppendRequestMetaInterceptor,
		grpcutil.ClientPayloadLogInterceptor,
		grpcutil.TimeoutInterceptor(time.Duration(cfg.WaitTimeoutInMilliSec)*time.Millisecond),
	)

	opts := []tigrpcclient.Option{tigrpcclient.WithPrometheus(), tigrpcclient.WithTracing(), interceptors}
	if strings.HasPrefix(cfg.Url, "kubernetes://") {
		kuberesolver.RegisterInCluster()
		opts = append(opts, tigrpcclient.WithLoadBalance())
	}

	// More about timeout:
	// We have a server option to set a timeout on connection establishment: https://pkg.go.dev/google.golang.org/grpc#ConnectionTimeout
	//
	// We have ways to configure keepalive parameters on the server: https://pkg.go.dev/google.golang.org/grpc@v1.43.0/keepalive#ServerParameters
	//
	// And if we wish to set timeouts on a per-RPC level, you could register a server interceptor using https://pkg.go.dev/google.golang.org/grpc#UnaryInterceptor and create a new context in there with a different timeout and pass it to the handler.

	if conn, err = tigrpcclient.NewClientConn(cfg.Url, opts...); err != nil {
		return nil, err
	}
	clusterCli := globalserverlessv1.NewServerlessServiceClient(conn)
	sqlUserCli := globalserverlessv1.NewSqlUserServiceClient(conn)
	providerCli := globalserverlessv1.NewCloudProviderServiceClient(conn)
	regionCli := globalserverlessv1.NewRegionServiceClient(conn)
	backupCli := globalserverlessv1.NewServerlessPGBackupProxyServiceClient(conn)

	c := clientImpl{
		clusterCli:           clusterCli,
		sqlUserCli:           sqlUserCli,
		providerCli:          providerCli,
		regionCli:            regionCli,
		backupCli:            backupCli,
		httpClient:           &http.Client{Timeout: dataMigrationHTTPTimeout},
		dataMigrationBaseURL: cfg.DataMigrationURL,
	}
	return &c, nil
}
