package grpcli

import (
	"context"
	"fmt"
	"sync"

	"github.com/tidbcloud/account/idl/pbgen/proto/acl"
	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/log"
	"github.com/tidbcloud/serverless-service/internal/config"
	"github.com/tidbcloud/serverless-service/internal/errorcode"
	tigrpc "github.com/tidbcloud/tiinf/transport/tigrpc/client"
	"go.uber.org/zap"
)

var (
	aclSvcOnce sync.Once
	aclSvcCli  acl.AccessServiceClient
)

func getAclSvcClient() acl.AccessServiceClient {
	aclSvcOnce.Do(func() {
		ctx := context.Background()
		addr := config.Global().SvcAddrConfig.AclSvcAddr

		conn, err := tigrpc.NewClientConn(addr, tigrpc.WithDefault())
		log.Info(ctx, "Initializing acl service client", zap.Any("err", err), zap.String("addr", addr))
		if err != nil {
			panic(fmt.Sprintf("dial acl-svc: %s", err))
		}

		aclSvcCli = acl.NewAccessServiceClient(conn)
	})
	return aclSvcCli
}

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

	resp, err := client.GetOpenAPIAccess(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

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

	resp, err := client.HasAdminAccess(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func GetOpenAPIKeyByToken(
	ctx context.Context,
	req *acl.GetOpenAPIKeyByTokenReq,
) (*acl.GetOpenAPIKeyByTokenResp, bizerr.BizError) {
	client := getAclSvcClient()

	resp, err := client.GetOpenAPIKeyByToken(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func GetJwtByToken(ctx context.Context, req *acl.GetJwtByTokenReq) (*acl.GetJwtByTokenResp, bizerr.BizError) {
	client := getAclSvcClient()

	resp, err := client.GetJwtByToken(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	if err := parseBaseResp(resp.BaseResp); err != nil {
		return nil, err
	}

	if resp.Jwt == "" || resp.DbUser == "" {
		return nil, bizerr.New(errorcode.ErrCodeSvcAccount, "cluster user/jwt is empty")
	}

	return resp, nil
}

func GetJwtByUser(ctx context.Context, req *acl.GetJwtByUserReq) (*acl.GetJwtByUserResp, bizerr.BizError) {
	client := getAclSvcClient()

	resp, err := client.GetJwtByUser(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	if err := parseBaseResp(resp.BaseResp); err != nil {
		return nil, err
	}

	if resp.Jwt == "" || resp.DbUser == "" {
		return nil, bizerr.New(errorcode.ErrCodeSvcAccount, "cluster user/jwt is empty")
	}

	return resp, nil
}

func GetJwtByApiKey(ctx context.Context, req *acl.GetJwtByApiKeyReq) (*acl.GetJwtByApiKeyResp, bizerr.BizError) {
	client := getAclSvcClient()

	resp, err := client.GetJwtByApiKey(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	if err := parseBaseResp(resp.BaseResp); err != nil {
		return nil, err
	}

	if resp.Jwt == "" || resp.DbUser == "" {
		return nil, bizerr.New(errorcode.ErrCodeSvcAccount, "cluster user/jwt is empty")
	}

	return resp, nil
}
