package privatelink

import (
	"context"
	"strconv"

	regionaldedicatedv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/regional-server/v1"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/regionserver"
	endpointrepo "github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/endpoint"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/service/workflow/common"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

func (svc *service) DeleteEndpoint(
	ctx context.Context,
	req *globaldedicatedv1.DeletePrivateLinkEndpointRequest,
) bizerr.BizError {
	ctx, span := tracer.Start(ctx, "Domain.DeletePrivateLinkEndpointSvc")
	defer span.End()

	err := svc.transaction.Transaction(ctx, func(ctx context.Context) error {
		tidbGroupId := req.GetTidbGroupId()
		if req.GetTidbGroupId() == 0 {
			tidbGroup, err := svc.tidbGroupRepo.LoadDefault(ctx, req.ClusterId)
			if err != nil {
				return err
			}
			tidbGroupId = tidbGroup.Id
		} else {
			// make sure tidbGroupId is owned by the cluster
			_, err := svc.tidbGroupRepo.Load(ctx, req.ClusterId, tidbGroupId)
			if err != nil {
				return err
			}
		}

		// Check
		cluster, err := svc.clusterRepo.Get(ctx, req.ClusterId, false)
		if err != nil {
			return err
		}

		infraEndpointID := req.EndpointId
		// `EndpointId` can be an uint64 unique ID or AWS EndpointId with format `vpce-xxxx`
		// We should get the detailed information for DB when it is an uint64 ID
		var uid uint64
		if validEndpointID.MatchString(req.EndpointId) {
			uintId, err := strconv.ParseUint(req.EndpointId, 10, 64)
			if err != nil {
				return bizerr.New(bizerr.ErrCodeParams, "invalid endpoint id %s", req.EndpointId)
			}
			endpoints, qerr := svc.privateLinkEndpointRepo.Query(ctx, &endpointrepo.PrivateLinkEndpointFilter{
				IDs: []uint64{uintId},
			})
			if qerr != nil {
				return qerr
			}
			if len(endpoints) == 0 {
				return bizerr.New(bizerr.ErrCodeRecordNotFound, "endpoint id '%s' does not exist", req.EndpointId)
			}
			if len(endpoints) > 1 {
				return bizerr.New(bizerr.ErrCodeDB, "duplicate endpoint id '%s'", req.EndpointId)
			}
			infraEndpointID = endpoints[0].EndpointID
			uid = endpoints[0].ID
		} else if !validAWSEndpointID.MatchString(req.EndpointId) {
			return bizerr.New(bizerr.ErrCodeParams, "invalid endpoint ID %s", req.EndpointId)
		}

		region, err := svc.regionRepo.SelectRegionById(ctx, cluster.RegionId, cluster.OrganizationId)
		if err != nil {
			return err
		}

		privateLinkService, err := svc.regionServerCli.GetPrivateLinkService(
			ctx,
			region,
			&regionserver.GetPrivateLinkServiceRequest{
				ClusterId:   strconv.FormatUint(cluster.ClusterId, 10),
				TidbGroupId: strconv.FormatUint(tidbGroupId, 10),
			},
		)
		if err != nil {
			return err
		}

		if err := svc.checkDeleteEndpoint(privateLinkService); err != nil {
			return err
		}

		if err := svc.privateLinkEndpointRepo.DeleteByIDOrEndpointID(ctx, req.ClusterId, uid, tidbGroupId, req.EndpointId); err != nil {
			return err.Chain("delete endpoint from MetaDB")
		}

		// Start async reconciliation workflow.
		workFlowErr := svc.workflow.RunDagWithBizId(
			ctx,
			common.DeletePrivateLinkConnection,
			strconv.FormatUint(cluster.OrganizationId, 10),
			strconv.FormatUint(cluster.ProjectId, 10),
			strconv.FormatUint(cluster.ClusterId, 10),
			map[string]string{
				workflow.KeyTraceId:             workflow.GetTraceId(ctx),
				common.KeyClusterId:             strconv.FormatUint(cluster.ClusterId, 10),
				common.KeyTidbGroupId:           strconv.FormatUint(tidbGroupId, 10),
				common.KeyPrivateLinkConnection: infraEndpointID,
			},
		)
		if workFlowErr != nil {
			return bizerr.WrapErrorWithCode(workFlowErr, bizerr.ErrCodeUnknown, "create async workflow failed")
		}

		return nil
	})
	if err != nil {
		return bizerr.WrapError(err, "delete private link endpoint failed")
	}

	return nil
}

func (svc *service) checkDeleteEndpoint(privateLinkService *regionaldedicatedv1.PrivateLinkService) bizerr.BizError {
	if privateLinkService.Status != regionaldedicatedv1.PrivateLinkService_ACTIVE {
		return bizerr.New(bizerr.ErrCodeParams,
			"unable to delete endpoint when PrivateLink service status is: %s", privateLinkService.Status.String())
	}

	return nil
}
