package cluster

import (
	"context"
	"errors"
	"fmt"

	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

	openapicommonv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/common/v1beta1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/repository"
)

const cleanPageSize = 100

var cleanableServicePlans = map[openapicommonv1beta1.ServicePlan]struct{}{
	openapicommonv1beta1.ServicePlan_Premium:      {},
	openapicommonv1beta1.ServicePlan_Essential_V2: {},
}

func (svc *service) CleanClusters(ctx context.Context, organizationID uint64, needFinalBackup bool) error {
	ctx, span := tracer.Start(ctx, "ClusterService.CleanClusters")
	defer span.End()

	if organizationID == 0 {
		return errors.New("organization id is invalid")
	}

	filter := &repository.ClusterFilter{
		OrganizationIds: []uint64{organizationID},
		DeployType:      entity.DeployTypeNextGen,
		ServicePlans: []string{
			openapicommonv1beta1.ServicePlan_Premium.String(),
			openapicommonv1beta1.ServicePlan_Essential_V2.String(),
		},
	}

	for {
		clusters, err := svc.clusterRepo.QueryClusterByFilterAndPage(ctx, filter, 0, cleanPageSize)
		if err != nil {
			return fmt.Errorf("list nextgen clusters in database: %w", err)
		}
		if len(clusters) == 0 {
			return nil
		}

		for _, cluster := range clusters {
			if err := validateCleanableCluster(cluster, organizationID); err != nil {
				tilog.Error(ctx, "refuse to clean unexpected nextgen cluster", zap.Error(err), zap.Any("cluster", cluster))
				return err
			}
			_, _, err := svc.deleteCluster(ctx, cluster.ClusterId, false, deleteOptions{
				needFinalBackup:           needFinalBackup,
				deleteResourcePoolIfEmpty: cluster.ServicePlan == openapicommonv1beta1.ServicePlan_Premium,
			})
			if err != nil {
				tilog.Warn(ctx, "delete nextgen cluster failed", zap.Uint64("cluster_id", cluster.ClusterId), zap.Error(err))
				return err
			}
		}
	}
}

func validateCleanableCluster(cluster *entity.Cluster, organizationID uint64) error {
	if cluster == nil {
		return errors.New("cluster is nil")
	}
	if cluster.OrganizationId != organizationID {
		return fmt.Errorf("cluster %d does not belong to organization %d", cluster.ClusterId, organizationID)
	}
	if cluster.DeployType != entity.DeployTypeNextGen {
		return fmt.Errorf("cluster %d deploy type is not nextgen", cluster.ClusterId)
	}
	if _, ok := cleanableServicePlans[cluster.ServicePlan]; !ok {
		return fmt.Errorf("cluster %d service plan %s is not cleanable", cluster.ClusterId, cluster.ServicePlan.String())
	}
	return nil
}
