package adapter

import (
	"context"
	"sync"
	"time"

	globalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	regionalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/regional/serverless/v1"
	"github.com/tidbcloud/tidb-management-service/serverless-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

var (
	// Do not use: should rely on outgoing client timeout.
	defaultTimeout                   = 60 * time.Second
	defaultPerRegionResultBufferSize = 100
)

//go:generate go run go.uber.org/mock/mockgen@latest -source=$GOFILE -package=mock_$GOPACKAGE -destination=./mock_$GOPACKAGE/mock_$GOFILE
type RegionalService interface {
	Describe(
		ctx context.Context,
		region *entity.Region,
	) (*regionalserverlessv1.ServerlessRegionalServiceDescRegionResponse, error)
	CreateCluster(
		ctx context.Context,
		region *entity.Region,
		createReq *globalserverlessv1.ServerlessServiceCreateRequest,
	) (*regionalserverlessv1.ServerlessRegionalServiceClusterInfo, error)
	DeleteCluster(ctx context.Context, region *entity.Region, clusterId string, tenantId, projectId uint64) error
	UpdateCluster(
		ctx context.Context,
		region *entity.Region,
		regionalCluster *regionalserverlessv1.ServerlessRegionalServiceClusterInfo,
	) (*regionalserverlessv1.ServerlessRegionalServiceClusterInfo, error)
	ListCluster(
		ctx context.Context,
		region *entity.Region,
		listReq *globalserverlessv1.ServerlessServiceListRequest,
	) ([]*regionalserverlessv1.ServerlessRegionalServiceClusterInfo, error)
	CheckImportPermission(ctx context.Context,
		region *entity.Region,
		r *regionalserverlessv1.CheckImportPermissionRequest) (
		*regionalserverlessv1.CheckImportPermissionResponse, error)
	Precheck(ctx context.Context,
		region *entity.Region,
		r *regionalserverlessv1.PrecheckRequest) (
		*regionalserverlessv1.PrecheckResponse, error)
	DMPrecheck(ctx context.Context,
		region *entity.Region,
		r *regionalserverlessv1.ServerlessDMRegionalServicePrecheckRequest) (
		*regionalserverlessv1.ServerlessDMRegionalServicePrecheckResponse, error)
	CreateVpcEndpoint(ctx context.Context,
		region *entity.Region,
		r *regionalserverlessv1.CreateVpcEndpointRequest) (
		*regionalserverlessv1.CreateVpcEndpointResponse, error)
	DescribeVpcEndpoint(ctx context.Context,
		region *entity.Region,
		r *regionalserverlessv1.DescribeVpcEndpointRequest) (
		*regionalserverlessv1.DescribeVpcEndpointResponse, error)
	DeleteVpcEndpoint(ctx context.Context,
		region *entity.Region,
		r *regionalserverlessv1.DeleteVpcEndpointRequest) (
		*regionalserverlessv1.DeleteVpcEndpointResponse, error)
	CheckCloudStoragePermission(ctx context.Context,
		region *entity.Region,
		r *regionalserverlessv1.CheckCloudStoragePermissionRequest) (
		*regionalserverlessv1.CheckCloudStoragePermissionResponse, error)
	UpdateAuditLogConfig(
		ctx context.Context,
		region *entity.Region,
		regionalCluster *regionalserverlessv1.ServerlessRegionalServiceUpdateClusterRequest,
	) (*regionalserverlessv1.ServerlessRegionalServiceClusterInfo, error)
	ListVpcEndpointZones(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.ListVpcEndpointZonesRequest,
	) (*regionalserverlessv1.ListVpcEndpointZonesResponse, error)
	RemoveZoneFromVpcEndpoint(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.RemoveZoneFromVpcEndpointRequest,
	) (*regionalserverlessv1.RemoveZoneFromVpcEndpointResponse, error)
	// --- PrivateLinkConnection regional methods ---
	CreateAWSVpcEndpoint(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.CreateAWSVpcEndpointRequest,
	) (*regionalserverlessv1.CreateAWSVpcEndpointResponse, error)
	DescribeAWSVpcEndpoint(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.DescribeAWSVpcEndpointRequest,
	) (*regionalserverlessv1.DescribeAWSVpcEndpointResponse, error)
	DeleteAWSVpcEndpoint(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.DeleteAWSVpcEndpointRequest,
	) (*regionalserverlessv1.DeleteAWSVpcEndpointResponse, error)
	CreateAliCloudVpcEndpoint(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.CreateAliCloudVpcEndpointRequest,
	) (*regionalserverlessv1.CreateAliCloudVpcEndpointResponse, error)
	DescribeAliCloudVpcEndpoint(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.DescribeAliCloudVpcEndpointRequest,
	) (*regionalserverlessv1.DescribeAliCloudVpcEndpointResponse, error)
	DeleteAliCloudVpcEndpoint(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.DeleteAliCloudVpcEndpointRequest,
	) (*regionalserverlessv1.DeleteAliCloudVpcEndpointResponse, error)
	GetZoneIds(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.GetZoneIdsRequest,
	) (*regionalserverlessv1.GetZoneIdsResponse, error)
	ListAliCloudVpcEndpointZones(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.ListAliCloudVpcEndpointZonesRequest,
	) (*regionalserverlessv1.ListAliCloudVpcEndpointZonesResponse, error)
	RemoveAliCloudZoneFromVpcEndpoint(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.RemoveAliCloudZoneFromVpcEndpointRequest,
	) (*regionalserverlessv1.RemoveAliCloudZoneFromVpcEndpointResponse, error)
	CreateAliCloudDNSRecord(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.CreateAliCloudDNSRecordRequest,
	) (*regionalserverlessv1.CreateAliCloudDNSRecordResponse, error)
	DeleteAliCloudDNSRecord(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.DeleteAliCloudDNSRecordRequest,
	) (*regionalserverlessv1.DeleteAliCloudDNSRecordResponse, error)
	CreateAWSRoute53Records(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.CreateAWSRoute53RecordsRequest,
	) (*regionalserverlessv1.CreateAWSRoute53RecordsResponse, error)
	DeleteAWSRoute53Records(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.DeleteAWSRoute53RecordsRequest,
	) (*regionalserverlessv1.DeleteAWSRoute53RecordsResponse, error)
	GetAWSRoute53Change(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.GetAWSRoute53ChangeRequest,
	) (*regionalserverlessv1.GetAWSRoute53ChangeResponse, error)
	CreateAWSRoute53PrivateHostedZone(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.CreateAWSRoute53PrivateHostedZoneRequest,
	) (*regionalserverlessv1.CreateAWSRoute53PrivateHostedZoneResponse, error)
	DeleteAWSRoute53PrivateHostedZone(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.DeleteAWSRoute53PrivateHostedZoneRequest,
	) (*regionalserverlessv1.DeleteAWSRoute53PrivateHostedZoneResponse, error)
	CreateAliCloudDNSPrivateHostedZone(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.CreateAliCloudDNSPrivateHostedZoneRequest,
	) (*regionalserverlessv1.CreateAliCloudDNSPrivateHostedZoneResponse, error)
	DeleteAliCloudDNSPrivateHostedZone(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.DeleteAliCloudDNSPrivateHostedZoneRequest,
	) (*regionalserverlessv1.DeleteAliCloudDNSPrivateHostedZoneResponse, error)
	AttachVpcToAliCloudDNSPrivateHostedZone(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.AttachVpcToAliCloudDNSPrivateHostedZoneRequest,
	) (*regionalserverlessv1.AttachVpcToAliCloudDNSPrivateHostedZoneResponse, error)
	DetachVpcFromAliCloudDNSPrivateHostedZone(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.DetachVpcFromAliCloudDNSPrivateHostedZoneRequest,
	) (*regionalserverlessv1.DetachVpcFromAliCloudDNSPrivateHostedZoneResponse, error)
	CreateMSKVPCConnection(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.CreateMSKVPCConnectionRequest,
	) (*regionalserverlessv1.CreateMSKVPCConnectionResponse, error)
	DeleteMSKVPCConnection(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.DeleteMSKVPCConnectionRequest,
	) (*regionalserverlessv1.DeleteMSKVPCConnectionResponse, error)
	GetMSKVPCConnection(ctx context.Context,
		region *entity.Region,
		req *regionalserverlessv1.GetMSKVPCConnectionRequest,
	) (*regionalserverlessv1.GetMSKVPCConnectionResponse, error)
	// --- PrivateLinkConnection regional methods ---
}

type RegionalResult[T any] struct {
	Items []T
	Err   bizerr.BizError
}

func QueryMultiRegional[T any](
	regions []*entity.Region,
	queryFunc func(*entity.Region, *sync.WaitGroup, chan<- *RegionalResult[T]),
) <-chan *RegionalResult[T] {
	var wg sync.WaitGroup
	regionalResultCh := make(chan *RegionalResult[T], len(regions)*defaultPerRegionResultBufferSize)
	doneCh := make(chan struct{})

	for _, region := range regions {
		wg.Add(1)
		go func(region *entity.Region) {
			defer wg.Done()
			queryFunc(region, &wg, regionalResultCh)
		}(region)
	}

	go func() {
		defer close(regionalResultCh)
		wg.Wait()
		close(doneCh)
	}()

	go func() {
		select {
		case <-time.After(defaultTimeout):
			regionalResultCh <- &RegionalResult[T]{
				Err: bizerr.New(bizerr.ErrCodeTimeout, "multi call regional timeout, regions: %v", regions),
			}
		case <-doneCh:
			// Completed within timeout, no action needed
		}
	}()

	return regionalResultCh
}
