package cli

import (
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/sercand/kuberesolver/v5"
	"github.com/tidbcloud/serverless-service/idl/pbgen/proto/serverless"
	tigrpc "github.com/tidbcloud/tiinf/transport/tigrpc/client"

	"github.com/tidbcloud/tidb-management-service/serverless-global-service/pkg/config"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	grpcutil "github.com/tidbcloud/tidb-management-service/utilities/grpc"
)

var (
	serverlessBillingSvcOnce sync.Once
	serverlessBillingSvcCli  serverless.ServerlessBillingServiceClient
)

func getServerlessBillingSvcClient() serverless.ServerlessBillingServiceClient {
	serverlessBillingSvcOnce.Do(func() {
		addr := config.Global().ServerConfig.DependentSvcAddresses.Serverless
		timeoutMs := config.Global().ServerConfig.DependentSvcAddresses.WaitTimeoutInMilliSec
		opts := []tigrpc.Option{
			tigrpc.WithPrometheus(),
			tigrpc.WithTracing(),
			tigrpc.WithUnaryInterceptors(
				grpcutil.ClientPayloadLogInterceptor,
				grpcutil.TimeoutInterceptor(time.Duration(timeoutMs)*time.Millisecond),
			),
		}
		if strings.HasPrefix(addr, "kubernetes://") {
			kuberesolver.RegisterInCluster()
			opts = append(opts, tigrpc.WithLoadBalance())
		}
		conn, err := tigrpc.NewClientConn(addr, opts...)
		if err != nil {
			panic(fmt.Sprintf("dial serverless-svc: %s", err))
		}
		serverlessBillingSvcCli = serverless.NewServerlessBillingServiceClient(conn)
	})

	return serverlessBillingSvcCli
}

func SetSpendLimit(ctx context.Context, req *serverless.SetSpendLimitReq) bizerr.BizError {
	client := getServerlessBillingSvcClient()
	resp, err := client.SetSpendLimit(ctx, req)
	if err != nil {
		return bizerr.WrapErrorWithCode(
			err,
			bizerr.ErrCodeSpendLimitsSetting,
			"Failed to set spending limit for serverless tier, please try again later.",
		)
	}
	if resp.GetBaseResp().GetErrCode() != 0 {
		return bizerr.New(bizerr.ErrCode(resp.BaseResp.ErrCode), "%s", resp.BaseResp.ErrMsg)
	}
	return nil
}

func GetSpendLimit(
	ctx context.Context,
	req *serverless.GetSpendLimitReq,
) (*serverless.GetSpendLimitResp, bizerr.BizError) {
	client := getServerlessBillingSvcClient()
	resp, err := client.GetSpendLimit(ctx, req)
	if err != nil {
		return nil, bizerr.WrapErrorWithCode(
			err,
			bizerr.ErrCodeSpendLimitsSetting,
			"Failed to get spending limit for serverless tier, please try again later.",
		)
	}
	if resp.GetBaseResp().GetErrCode() != 0 {
		return nil, bizerr.New(bizerr.ErrCode(resp.BaseResp.ErrCode), "%s", resp.BaseResp.ErrMsg)
	}
	return resp, nil
}

func DeleteSpendLimit(ctx context.Context, req *serverless.DeleteSpendLimitReq) bizerr.BizError {
	client := getServerlessBillingSvcClient()
	resp, err := client.DeleteSpendLimit(ctx, req)
	if err != nil {
		return bizerr.WrapErrorWithCode(
			err,
			bizerr.ErrCodeSpendLimitsSetting,
			"Failed to delete spending limit for serverless tier, please try again later.",
		)
	}
	if resp.GetBaseResp().GetErrCode() != 0 {
		return bizerr.New(bizerr.ErrCode(resp.BaseResp.ErrCode), "%s", resp.BaseResp.ErrMsg)
	}
	return nil
}
