package grpcli

import (
	"context"
	"fmt"
	"sync"

	"github.com/tidbcloud/pkgv2/bizerr"
	"github.com/tidbcloud/pkgv2/log"
	"github.com/tidbcloud/serverless-service/internal/config"
	"github.com/tidbcloud/serverless-service/internal/domain/facade"
	globalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	regionalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/regional/serverless/v1"
	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	tigrpc "github.com/tidbcloud/tiinf/transport/tigrpc/client"
	"go.uber.org/zap"
	"google.golang.org/protobuf/proto"
)

var (
	mgmtSvcOnce sync.Once
	mgmtSvcCli  *mgmtSvcClient
)

type mgmtSvcClient struct {
	clusterSvc             tidbmgmtservicev1.ClusterServiceClient
	clusterCustomStatusSvc tidbmgmtservicev1.ClusterCustomStatusServiceClient
	importSvc              globalserverlessv1.ServerlessImportServiceClient
	globalClusterSvc       globalserverlessv1.ServerlessServiceClient
	dmSvc                  globalserverlessv1.ServerlessDMServiceClient
	plcSvc                 globalserverlessv1.ServerlessPrivateLinkConnectionServiceClient
	regionSvc              tidbmgmtservicev1.RegionServiceClient
}

func getMgmtSvcClient() *mgmtSvcClient {
	mgmtSvcOnce.Do(func() {
		ctx := context.Background()
		addr := config.Global().SvcAddrConfig.ManagementSvcAddr

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

		globalAddr := config.Global().SvcAddrConfig.ServerlessGlobalSvcAddr
		globalConn, err := tigrpc.NewClientConn(globalAddr, tigrpc.WithDefault())
		log.Info(
			ctx,
			"Initializing serverless global service client",
			zap.Any("err", err),
			zap.String("addr", globalAddr),
		)
		if err != nil {
			panic(fmt.Sprintf("dial global serverless svc: %s", err))
		}

		mgmtSvcCli = &mgmtSvcClient{
			clusterSvc:             tidbmgmtservicev1.NewClusterServiceClient(conn),
			clusterCustomStatusSvc: tidbmgmtservicev1.NewClusterCustomStatusServiceClient(conn),
			importSvc:              globalserverlessv1.NewServerlessImportServiceClient(globalConn),
			globalClusterSvc:       globalserverlessv1.NewServerlessServiceClient(globalConn),
			dmSvc:                  globalserverlessv1.NewServerlessDMServiceClient(globalConn),
			plcSvc:                 globalserverlessv1.NewServerlessPrivateLinkConnectionServiceClient(globalConn),
			regionSvc:              tidbmgmtservicev1.NewRegionServiceClient(conn),
		}
	})
	return mgmtSvcCli
}

func GetCluster(ctx context.Context, clusterId uint64) (*tidbmgmtservicev1.Cluster, bizerr.BizError) {
	req := &tidbmgmtservicev1.ListClustersRequest{
		Filter: &tidbmgmtservicev1.ClusterFilter{
			ClusterIds:    []uint64{clusterId},
			ExcludeStates: facade.DeletedClusterStatus,
			View:          tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_LOCAL,
			ClusterType:   tidbmgmtservicev1.DeployType_DEPLOY_TYPE_SERVERLESS.Enum(),
		},
	}
	resp, err := ListClusters(ctx, req)
	if err != nil {
		return nil, err
	}
	if len(resp.Clusters) == 0 {
		return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "cluster %d is not found", clusterId)
	}
	return resp.Clusters[0], nil
}

func GetClusterIncludeDeleted(ctx context.Context, clusterId uint64) (*tidbmgmtservicev1.Cluster, bizerr.BizError) {
	req := &tidbmgmtservicev1.ListClustersRequest{
		Filter: &tidbmgmtservicev1.ClusterFilter{
			ClusterIds:  []uint64{clusterId},
			View:        tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_LOCAL,
			ClusterType: tidbmgmtservicev1.DeployType_DEPLOY_TYPE_SERVERLESS.Enum(),
			Deleted:     proto.Bool(true),
		},
	}
	resp, err := ListClusters(ctx, req)
	if err != nil {
		return nil, err
	}
	if len(resp.Clusters) == 0 {
		return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "cluster %d is not found", clusterId)
	}
	return resp.Clusters[0], nil
}

func GetClusterBasicView(ctx context.Context, clusterId uint64) (*tidbmgmtservicev1.Cluster, bizerr.BizError) {
	req := &tidbmgmtservicev1.ListClustersRequest{
		Filter: &tidbmgmtservicev1.ClusterFilter{
			ClusterIds:    []uint64{clusterId},
			ExcludeStates: facade.DeletedClusterStatus,
			View:          tidbmgmtservicev1.ClusterFilter_CLUSTER_VIEW_BASIC,
			ClusterType:   tidbmgmtservicev1.DeployType_DEPLOY_TYPE_SERVERLESS.Enum(),
		},
	}
	resp, err := ListClusters(ctx, req)
	if err != nil {
		return nil, err
	}
	if len(resp.Clusters) == 0 {
		return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "cluster %d is not found", clusterId)
	}
	return resp.Clusters[0], nil
}

func ListClusters(
	ctx context.Context,
	req *tidbmgmtservicev1.ListClustersRequest,
) (*tidbmgmtservicev1.ListClustersResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.clusterSvc.ListClusters(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func SetClusterCustomStatus(
	ctx context.Context,
	req *tidbmgmtservicev1.SetClusterCustomStatusRequest,
) (*tidbmgmtservicev1.ClusterCustomStatus, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.clusterCustomStatusSvc.SetClusterCustomStatus(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func DeleteClusterCustomStatus(
	ctx context.Context,
	req *tidbmgmtservicev1.DeleteClusterCustomStatusRequest,
) bizerr.BizError {
	client := getMgmtSvcClient()
	_, err := client.clusterCustomStatusSvc.DeleteClusterCustomStatus(ctx, req)
	if err != nil {
		return wrapGrpcError(err)
	}
	return nil
}

func ListClusterCustomStatus(
	ctx context.Context,
	req *tidbmgmtservicev1.ListClusterCustomStatusRequest,
) (*tidbmgmtservicev1.ListClusterCustomStatusResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.clusterCustomStatusSvc.ListClusterCustomStatus(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func GetClusterCustomStatusByName(
	ctx context.Context,
	req *tidbmgmtservicev1.GetClusterCustomStatusByNameRequest,
) (*tidbmgmtservicev1.ClusterCustomStatus, bizerr.BizError) {
	client := getMgmtSvcClient()
	status, err := client.clusterCustomStatusSvc.GetClusterCustomStatusByName(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return status, nil
}

func RestoreServerlessCluster(
	ctx context.Context,
	req *tidbmgmtservicev1.RestoreServerlessClusterRequest,
) (*tidbmgmtservicev1.Cluster, bizerr.BizError) {
	client := getMgmtSvcClient()
	cluster, err := client.clusterSvc.RestoreServerlessCluster(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return cluster, nil
}

func UpdateServerlessRegionalClusterAnnotations(
	ctx context.Context,
	req *tidbmgmtservicev1.UpdateServerlessRegionalClusterAnnotationsRequest,
) (*tidbmgmtservicev1.Cluster, bizerr.BizError) {
	client := getMgmtSvcClient()
	cluster, err := client.clusterSvc.UpdateServerlessRegionalClusterAnnotations(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return cluster, nil
}

func CheckImportPermission(
	ctx context.Context,
	req *globalserverlessv1.CheckImportPermissionRequest,
) bizerr.BizError {
	client := getMgmtSvcClient()
	_, err := client.importSvc.CheckImportPermission(ctx, req)
	if err != nil {
		return wrapGrpcError(err)
	}
	return nil
}

func Precheck(
	ctx context.Context,
	req *globalserverlessv1.PrecheckRequest,
) (*globalserverlessv1.PrecheckResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.importSvc.Precheck(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func UpdateAuditLogConfig(
	ctx context.Context,
	clusterId string,
	req *regionalserverlessv1.ServerlessRegionalServiceClusterInfo_AuditLogConfig,
) bizerr.BizError {
	request := &globalserverlessv1.UpdateAuditLogConfigRequest{
		ClusterId:      clusterId,
		AuditLogConfig: req,
	}
	client := getMgmtSvcClient()
	_, err := client.globalClusterSvc.UpdateAuditLogConfig(ctx, request)
	if err != nil {
		return wrapGrpcError(err)
	}
	return nil
}

func CheckCloudStoragePermission(
	ctx context.Context,
	req *globalserverlessv1.CheckCloudStoragePermissionRequest,
) bizerr.BizError {
	client := getMgmtSvcClient()
	_, err := client.globalClusterSvc.CheckCloudStoragePermission(ctx, req)
	if err != nil {
		return wrapGrpcError(err)
	}
	return nil
}

func DMPrecheck(
	ctx context.Context,
	req *globalserverlessv1.ServerlessDMServicePrecheckRequest,
) (*globalserverlessv1.ServerlessDMServicePrecheckResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.dmSvc.Precheck(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func CreateVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.CreateVpcEndpointRequest,
) (*globalserverlessv1.CreateVpcEndpointResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.dmSvc.CreateVpcEndpoint(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func DescribeVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DescribeVpcEndpointRequest,
) (*globalserverlessv1.DescribeVpcEndpointResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.dmSvc.DescribeVpcEndpoint(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func DeleteVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DeleteVpcEndpointRequest,
) bizerr.BizError {
	client := getMgmtSvcClient()
	_, err := client.dmSvc.DeleteVpcEndpoint(ctx, req)
	if err != nil {
		return wrapGrpcError(err)
	}
	return nil
}

func ListVpcEndpointZones(
	ctx context.Context,
	req *globalserverlessv1.ListVpcEndpointZonesRequest,
) (*globalserverlessv1.ListVpcEndpointZonesResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.dmSvc.ListVpcEndpointZones(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func RemoveZoneFromVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.RemoveZoneFromVpcEndpointRequest,
) bizerr.BizError {
	client := getMgmtSvcClient()
	_, err := client.dmSvc.RemoveZoneFromVpcEndpoint(ctx, req)
	if err != nil {
		return wrapGrpcError(err)
	}
	return nil
}

// ------- Private Link Connection -------
func CreateAWSVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.CreateAWSVpcEndpointRequest,
) (*globalserverlessv1.CreateAWSVpcEndpointResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.CreateAWSVpcEndpoint(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func DescribeAWSVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DescribeAWSVpcEndpointRequest,
) (*globalserverlessv1.DescribeAWSVpcEndpointResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.DescribeAWSVpcEndpoint(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func DeleteAWSVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DeleteAWSVpcEndpointRequest,
) bizerr.BizError {
	client := getMgmtSvcClient()
	_, err := client.plcSvc.DeleteAWSVpcEndpoint(ctx, req)
	if err != nil {
		return wrapGrpcError(err)
	}
	return nil
}

func CreateAliCloudVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.CreateAliCloudVpcEndpointRequest,
) (*globalserverlessv1.CreateAliCloudVpcEndpointResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.CreateAliCloudVpcEndpoint(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func DescribeAliCloudVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DescribeAliCloudVpcEndpointRequest,
) (*globalserverlessv1.DescribeAliCloudVpcEndpointResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.DescribeAliCloudVpcEndpoint(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func DeleteAliCloudVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.DeleteAliCloudVpcEndpointRequest,
) bizerr.BizError {
	client := getMgmtSvcClient()
	_, err := client.plcSvc.DeleteAliCloudVpcEndpoint(ctx, req)
	if err != nil {
		return wrapGrpcError(err)
	}
	return nil
}

func GetZoneIds(
	ctx context.Context,
	req *globalserverlessv1.GetZoneIdsRequest,
) (*globalserverlessv1.GetZoneIdsResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.GetZoneIds(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func ListAliCloudVpcEndpointZones(
	ctx context.Context,
	req *globalserverlessv1.ListAliCloudVpcEndpointZonesRequest,
) (*globalserverlessv1.ListAliCloudVpcEndpointZonesResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.ListAliCloudVpcEndpointZones(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func RemoveAliCloudZoneFromVpcEndpoint(
	ctx context.Context,
	req *globalserverlessv1.RemoveAliCloudZoneFromVpcEndpointRequest,
) bizerr.BizError {
	client := getMgmtSvcClient()
	_, err := client.plcSvc.RemoveAliCloudZoneFromVpcEndpoint(ctx, req)
	if err != nil {
		return wrapGrpcError(err)
	}
	return nil
}

func CreateAliCloudDNSRecord(
	ctx context.Context,
	req *globalserverlessv1.CreateAliCloudDNSRecordRequest,
) (*globalserverlessv1.CreateAliCloudDNSRecordResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.CreateAliCloudDNSRecord(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func DeleteAliCloudDNSRecord(
	ctx context.Context,
	req *globalserverlessv1.DeleteAliCloudDNSRecordRequest,
) bizerr.BizError {
	client := getMgmtSvcClient()
	_, err := client.plcSvc.DeleteAliCloudDNSRecord(ctx, req)
	if err != nil {
		return wrapGrpcError(err)
	}
	return nil
}

func CreateAWSRoute53Records(
	ctx context.Context,
	req *globalserverlessv1.CreateAWSRoute53RecordsRequest,
) (*globalserverlessv1.CreateAWSRoute53RecordsResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.CreateAWSRoute53Records(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func DeleteAWSRoute53Records(
	ctx context.Context,
	req *globalserverlessv1.DeleteAWSRoute53RecordsRequest,
) (*globalserverlessv1.DeleteAWSRoute53RecordsResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.DeleteAWSRoute53Records(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func GetAWSRoute53Change(
	ctx context.Context,
	req *globalserverlessv1.GetAWSRoute53ChangeRequest,
) (*globalserverlessv1.GetAWSRoute53ChangeResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.GetAWSRoute53Change(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func CreateAWSRoute53PrivateHostedZone(
	ctx context.Context,
	req *globalserverlessv1.CreateAWSRoute53PrivateHostedZoneRequest,
) (*globalserverlessv1.CreateAWSRoute53PrivateHostedZoneResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.CreateAWSRoute53PrivateHostedZone(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func DeleteAWSRoute53PrivateHostedZone(
	ctx context.Context,
	req *globalserverlessv1.DeleteAWSRoute53PrivateHostedZoneRequest,
) (*globalserverlessv1.DeleteAWSRoute53PrivateHostedZoneResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.DeleteAWSRoute53PrivateHostedZone(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func CreateAliCloudDNSPrivateHostedZone(
	ctx context.Context,
	req *globalserverlessv1.CreateAliCloudDNSPrivateHostedZoneRequest,
) (*globalserverlessv1.CreateAliCloudDNSPrivateHostedZoneResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.CreateAliCloudDNSPrivateHostedZone(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func DeleteAliCloudDNSPrivateHostedZone(
	ctx context.Context,
	req *globalserverlessv1.DeleteAliCloudDNSPrivateHostedZoneRequest,
) (*globalserverlessv1.DeleteAliCloudDNSPrivateHostedZoneResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.DeleteAliCloudDNSPrivateHostedZone(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func AttachVpcToAliCloudDNSPrivateHostedZone(
	ctx context.Context,
	req *globalserverlessv1.AttachVpcToAliCloudDNSPrivateHostedZoneRequest,
) (*globalserverlessv1.AttachVpcToAliCloudDNSPrivateHostedZoneResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.AttachVpcToAliCloudDNSPrivateHostedZone(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func DetachVpcFromAliCloudDNSPrivateHostedZone(
	ctx context.Context,
	req *globalserverlessv1.DetachVpcFromAliCloudDNSPrivateHostedZoneRequest,
) (*globalserverlessv1.DetachVpcFromAliCloudDNSPrivateHostedZoneResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.DetachVpcFromAliCloudDNSPrivateHostedZone(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func CreateMSKVPCConnection(
	ctx context.Context,
	req *globalserverlessv1.CreateMSKVPCConnectionRequest,
) (*globalserverlessv1.CreateMSKVPCConnectionResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.CreateMSKVPCConnection(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func DeleteMSKVPCConnection(
	ctx context.Context,
	req *globalserverlessv1.DeleteMSKVPCConnectionRequest,
) (*globalserverlessv1.DeleteMSKVPCConnectionResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.DeleteMSKVPCConnection(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

func GetMSKVPCConnection(
	ctx context.Context,
	req *globalserverlessv1.GetMSKVPCConnectionRequest,
) (*globalserverlessv1.GetMSKVPCConnectionResponse, bizerr.BizError) {
	client := getMgmtSvcClient()
	resp, err := client.plcSvc.GetMSKVPCConnection(ctx, req)
	if err != nil {
		return nil, wrapGrpcError(err)
	}
	return resp, nil
}

// ------- Private Link Connection -------
