package account

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

	"github.com/sercand/kuberesolver/v5"
	"github.com/tidbcloud/account/idl/pbgen/proto/acl"
	tigrpc "github.com/tidbcloud/tiinf/transport/tigrpc/client"
	"google.golang.org/grpc"

	"github.com/tidbcloud/tidb-management-service/tempo-wrapper/internal/config"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	grpcutil "github.com/tidbcloud/tidb-management-service/utilities/grpc"
)

var (
	accountACLSvcOnce sync.Once
	accountACLSvcCli  acl.AccessServiceClient
)

func getAccountACLSvcClient() acl.AccessServiceClient {
	accountACLSvcOnce.Do(func() {
		timeout := time.Duration(config.Config().Grpc.Timeout) * time.Second
		ctx, cancel := context.WithTimeout(context.Background(), timeout)
		defer cancel()

		addr := config.Config().Grpc.AccountACLSvcAddr
		interceptors := []grpc.UnaryClientInterceptor{
			grpcutil.TimeoutInterceptor(timeout),
			grpcutil.ClientPayloadLogInterceptor,
		}
		opts := []tigrpc.Option{
			tigrpc.WithDefault(),
			tigrpc.WithUnaryInterceptors(interceptors...),
		}
		if strings.HasPrefix(addr, "kubernetes://") {
			kuberesolver.RegisterInCluster()
			opts = append(opts, tigrpc.WithLoadBalance())
		}

		conn, err := tigrpc.NewClientContext(ctx, addr, opts...)
		if err != nil {
			panic(fmt.Sprintf("dial account-acl-svc: %s", err))
		}
		accountACLSvcCli = acl.NewAccessServiceClient(conn)
	})

	return accountACLSvcCli
}

func GetOpenAPIAccess(ctx context.Context, publicKey string) (*acl.GetOpenAPIAccessResp, bizerr.BizError) {
	req := &acl.GetOpenAPIAccessReq{
		PublicKey: publicKey,
	}
	return _getOpenAPIAccess(ctx, req)
}

func _getOpenAPIAccess(ctx context.Context, req *acl.GetOpenAPIAccessReq) (*acl.GetOpenAPIAccessResp, bizerr.BizError) {
	client := getAccountACLSvcClient()

	resp, e := client.GetOpenAPIAccess(ctx, req)
	if e != nil {
		return resp, bizerr.WrapGrpcError(e, "GetOpenAPIAccess")
	}
	return resp, nil
}

func HasAdminAccess(ctx context.Context, req *acl.HasAdminAccessReq) (*acl.HasAdminAccessResp, bizerr.BizError) {
	client := getAccountACLSvcClient()

	resp, e := client.HasAdminAccess(ctx, req)
	if e != nil {
		return resp, bizerr.WrapGrpcError(e, "HasAdminAccess")
	}
	return resp, nil
}
