package cluster

import (
	"context"

	"github.com/samber/lo"
	"github.com/tidbcloud/pkgv2/db"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/entity"
	clusterrepo "github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/cluster"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/tidbgroup"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/idgenerator"
)

type CreateClusterSnapshotSvc interface {
	Execute(ctx context.Context, clusterID uint64) (uint64, bizerr.BizError)
}

type createClusterSnapshotImp struct {
	clusterRepo       clusterrepo.Repo
	snapshotRepo      clusterrepo.ComponentSnapshotRepo
	tidbGroupRepo     tidbgroup.Repo
	idGeneratorFacade idgenerator.IDGenerator
	transaction       db.Transactioner
}

func NewCreateClusterSnapshotSvc(
	clusterRepo clusterrepo.Repo,
	snapshotRepo clusterrepo.ComponentSnapshotRepo,
	tidbGroupRepo tidbgroup.Repo,
	idGeneratorFacade idgenerator.IDGenerator,
	transaction db.Transactioner,
) CreateClusterSnapshotSvc {
	return &createClusterSnapshotImp{
		clusterRepo:       clusterRepo,
		snapshotRepo:      snapshotRepo,
		tidbGroupRepo:     tidbGroupRepo,
		idGeneratorFacade: idGeneratorFacade,
		transaction:       transaction,
	}
}

func (svc *createClusterSnapshotImp) Execute(ctx context.Context, clusterID uint64) (uint64, bizerr.BizError) {
	ctx, span := tracer.Start(ctx, "Domain.CreateClusterSnapshotSvc")
	defer span.End()

	var snapshot *entity.ClusterSnapshot
	err := svc.transaction.Transaction(ctx, func(ctx context.Context) error {
		cluster, err := svc.clusterRepo.GetAndLoadComponents(ctx, clusterID)
		if err != nil {
			return err
		}

		defaultGroup, err := svc.tidbGroupRepo.LoadDefault(ctx, clusterID)
		if err != nil {
			return err
		}

		defaultGroupComps := lo.Filter(
			cluster.ClusterComponents,
			func(comp *globaldedicatedv1.Cluster_ClusterComponent, _ int) bool {
				return comp.GetTidbGroupId() == defaultGroup.Id ||
					comp.GetComponentType() != globaldedicatedv1.ComponentType_TIDB
			},
		)

		snapshotID := svc.idGeneratorFacade.MustNew()
		snapshot = &entity.ClusterSnapshot{
			ClusterSnapshot: &globaldedicatedv1.ClusterSnapshot{
				Id:         snapshotID,
				TenantId:   cluster.OrganizationId,
				ProjectId:  cluster.ProjectId,
				ClusterId:  cluster.ClusterId,
				Components: defaultGroupComps,
			},
			Cluster: cluster,
		}
		if err := svc.snapshotRepo.Save(ctx, snapshot); err != nil {
			return err
		}

		return nil
	})
	if err != nil {
		return 0, bizerr.WrapError(err, "failed to create cluster snapshot")
	}

	return snapshot.Id, nil
}
