package privatelink

import (
	"context"
	"fmt"
	"strconv"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	openapicommonv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/common/v1beta1"
	openapidedicatedv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/dedicated/v1beta1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/pkg/apimiddleware/bizerror"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/pkg/common"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/enum"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
)

func (s serverImpl) GetPrivateLinkService(
	ctx context.Context,
	req *openapidedicatedv1beta1.GetPrivateLinkServiceRequest,
) (*openapidedicatedv1beta1.PrivateLinkService, error) {
	ctx, span := tracer.Start(ctx, "GetPrivateLinkService")
	defer span.End()

	// Extract request meta.
	reqMeta, err := metadata.ExtractRequestMetaWithCheckPermission(ctx)
	if err != nil {
		return nil, bizerr.WrapErrorWithCode(err, bizerr.ErrCodeParams, "failed to extract request metadata")
	}

	// Parse cluster ID.
	clusterId, err := strconv.ParseUint(req.GetClusterId(), 10, 64)
	if err != nil {
		return nil, bizerr.WrapErrorWithCode(err, bizerr.ErrCodeParams, "failed to parse cluster ID")
	}

	// Parse tidb group ID.
	tidbGroupId, err := strconv.ParseUint(req.GetTidbNodeGroupId(), 10, 64)
	if err != nil {
		return nil, bizerr.WrapErrorWithCode(err, bizerr.ErrCodeParams, "failed to parse TiDB group ID")
	}

	tidbGroupResp, err := s.dedicatedCli.ListTidbNodeGroups(ctx, &globaldedicatedv1.ListTidbNodeGroupsRequest{
		ClusterIds: []uint64{clusterId},
		Ids:        []uint64{tidbGroupId},
	})
	if err != nil {
		return nil, err
	}
	if len(tidbGroupResp.GetTidbNodeGroups()) == 0 {
		return nil, bizerror.NewNotFound(fmt.Errorf("no private link service found"), "no private link service found")
	}
	if len(tidbGroupResp.GetTidbNodeGroups()) > 1 {
		return nil, bizerror.NewInternal(
			fmt.Errorf("multiple private link services found"),
			"multiple private link services found",
		)
	}

	// Get the cluster.
	cluster, err := s.clusterSvc.GetCluster(ctx, clusterId, entity.ClusterViewClusterViewBasic, false, false)
	if err != nil {
		return nil, bizerror.NewInternal(err, "failed to get cluster")
	}

	_, regionId, cloudProvider, err := common.BuildOpenApiRegionName(cluster.CloudProvider.String(), cluster.Region)
	if err != nil {
		return nil, bizerror.NewInternal(err, "failed to get region")
	}

	// Get the region.
	globalRegion, err := regionCodeToGlobal(cluster.Region)
	if err != nil {
		return nil, bizerror.NewInternal(err, "failed to get region")
	}
	region, err := s.dedicatedCli.SelectRegionByOrgId(ctx, &globaldedicatedv1.SelectRegionByOrgIdRequest{
		CloudProviderId: common.OpenApiProviderToGlobal(cluster.CloudProvider),
		RegionId:        globalRegion,
		OrganizationId:  reqMeta.OrgID,
	})
	if err != nil {
		return nil, bizerror.NewInternal(err, "failed to get region")
	}

	endpointService, err := s.dedicatedCli.ListTidbNodeGroupPrivateLinkEndpoints(
		ctx,
		&globaldedicatedv1.ListTidbNodeGroupPrivateLinkEndpointsRequest{
			ClusterId:   cluster.ClusterId,
			TidbGroupId: tidbGroupId,
		},
	)
	if err != nil {
		return nil, bizerror.NewInternal(err, "failed to get private link service")
	}

	return privateLinkServiceFromGlobalToOpenApi(
		tidbGroupResp.GetTidbNodeGroups()[0],
		endpointService.GetPrivateLinkService(),
		regionId,
		region.GetDisplayName(),
		cloudProvider,
	), nil
}

func regionCodeToGlobal(regionCode string) (globalcommonv1.Region_Name, error) {
	r, err := enum.FromString[globalcommonv1.Region_Name](regionCode)
	if err != nil {
		return globalcommonv1.Region_NAME_UNSPECIFIED, err
	}
	return globalcommonv1.Region_Name(r), nil
}

func privateLinkServiceFromGlobalToOpenApi(
	tidbGroup *globaldedicatedv1.TidbNodeGroup,
	privateLinkService *globaldedicatedv1.PrivateLinkService,
	regionId, regionDisplay string,
	cloudProvider openapicommonv1beta1.Region_CloudProvider,
) *openapidedicatedv1beta1.PrivateLinkService {
	res := &openapidedicatedv1beta1.PrivateLinkService{
		Name:              fmt.Sprintf("tidbNodeGroups/%d/privateLinkService", tidbGroup.GetId()),
		TidbNodeGroupId:   strconv.FormatUint(tidbGroup.GetId(), 10),
		ServiceName:       privateLinkService.GetName(),
		ServiceDnsName:    privateLinkService.GetDnsName(),
		AvailableZones:    privateLinkService.GetAzIds(),
		State:             serviceStateFromGlobalToOpenApi(privateLinkService.GetStatus()),
		RegionId:          regionId,
		RegionDisplayName: regionDisplay,
		CloudProvider:     cloudProvider,
	}
	return res
}

func serviceStateFromGlobalToOpenApi(
	state globaldedicatedv1.PrivateLinkService_Status,
) openapidedicatedv1beta1.PrivateLinkService_State {
	switch state {
	case globaldedicatedv1.PrivateLinkService_CREATING:
		return openapidedicatedv1beta1.PrivateLinkService_CREATING
	case globaldedicatedv1.PrivateLinkService_ACTIVE:
		return openapidedicatedv1beta1.PrivateLinkService_ACTIVE
	case globaldedicatedv1.PrivateLinkService_DELETING:
		return openapidedicatedv1beta1.PrivateLinkService_DELETING
	default:
		return openapidedicatedv1beta1.PrivateLinkService_STATE_UNSPECIFIED
	}
}
