package backup

import (
	"context"

	"github.com/samber/lo"
	core_event "github.com/tidbcloud/core-svc/event"
	"github.com/tidbcloud/core-svc/idl/pbgen/proto/core"
	"github.com/tidbcloud/dataflow-service/app/container"
	"github.com/tidbcloud/dataflow-service/app/models/domain"
	"github.com/tidbcloud/dataflow-service/app/repo/entity"
	"github.com/tidbcloud/dataflow-service/pkg/log"
	"go.uber.org/zap"
)

type BackupWithClusterDeleting struct{}

func (b *BackupWithClusterDeleting) Name() string {
	return "BackupWithClusterDeleting"
}

func (b *BackupWithClusterDeleting) WatchForEvents() []core_event.ClusterEventType {
	return []core_event.ClusterEventType{core_event.ClusterEventTypeDeleting}
}

func (b *BackupWithClusterDeleting) Match(event *core_event.ClusterEvent) bool {
	return lo.Contains(b.WatchForEvents(), event.EventType)
}

func (b *BackupWithClusterDeleting) Execute(
	ctx context.Context, event *core_event.ClusterEvent, cluster *core.Cluster,
) error {
	logger := log.GetLogger(ctx).Named("BackupWithClusterDeleting")
	logger.Info("backup event: WithClusterDeleting", zap.Uint64("clusterID", event.ClusterID))
	app := container.Global()
	req := &domain.MarkBackupScheduleAsDeletedReq{
		ClusterId: &cluster.Id,
	}
	var err error
	if cluster.IsDevTier {
		// serverless cluster
		logger.Info("serverless is no longer supported by dataflow service, skip it",
			zap.Uint64("clusterID", event.ClusterID),
			zap.String("clusterName", cluster.Name))
		return nil
	} else {
		// dedicated cluster
		err = app.BackupSettingModel.MarkBackupScheduleAsDeleted(ctx, req)
	}
	if err != nil {
		logger.Error("backup event: MarkBackupScheduleAsDeleted",
			zap.Uint64("clusterID", event.ClusterID),
			zap.Bool("isDevTier", cluster.IsDevTier),
			zap.Error(err),
		)
		return err
	}

	if !cluster.IsDevTier {
		if err := b.deleteFailedManualBackup(ctx, cluster); err != nil {
			return err
		}
	}

	return nil
}

func (b *BackupWithClusterDeleting) deleteFailedManualBackup(
	ctx context.Context, cluster *core.Cluster,
) error {
	app := container.Global()
	backups, err := app.BackupRepo.List(ctx, func(qs entity.BackupQuerySet) entity.BackupQuerySet {
		return qs.PreloadBackupSchedule().
			ClusterIDEq(entity.ID(cluster.Id)).
			StatusEq(entity.BackupStatusFailed).
			CleanedUpEq(false).
			DeletedAtIsNull()
	})
	if err != nil {
		log.GetLogger(ctx).Error("list failed manual backups", zap.Error(err))
		return err
	}
	for _, backupItem := range backups {
		if backupItem.BackupSchedule == nil || backupItem.BackupSchedule.Type == entity.BackupTypeAuto {
			log.GetLogger(ctx).Info("skip auto backup", zap.Any("backupItem", backupItem))
			continue
		}
		resp, err := app.SnapshotBackupModel.AsyncDelete(ctx, &domain.AsyncDeleteBackupReq{
			BackupID: uint64(backupItem.ID),
		})
		if err != nil {
			log.GetLogger(ctx).Error("delete failed manual backup", zap.Error(err))
			return err
		}
		log.GetLogger(ctx).Info("delete failed manual backup", zap.Any("resp", resp))
	}
	return nil
}
