package service

import (
	"context"
	"time"

	"github.com/tidbcloud/serverless-service/internal/domain/constant"
	"github.com/tidbcloud/serverless-service/internal/domain/entity"
	"github.com/tidbcloud/serverless-service/internal/domain/repo"
	"github.com/tidbcloud/serverless-service/internal/utils"
	"go.uber.org/multierr"
	"go.uber.org/zap"
)

// VirtualizedBackupCleanupWorker removes data that is only used by the
// virtualized backup path. It deliberately lives outside BackupWorker so the
// legacy cleanup path remains unchanged while both implementations run.
type VirtualizedBackupCleanupWorker struct {
	historyRepo   repo.BackupScheduleHistoryRepo
	tombstoneRepo repo.BackupTombstoneRepo
	slotRepo      repo.PhysicalBackupSlotRepo
}

func NewVirtualizedBackupCleanupWorker(
	historyRepo repo.BackupScheduleHistoryRepo,
	tombstoneRepo repo.BackupTombstoneRepo,
	slotRepo repo.PhysicalBackupSlotRepo,
) *VirtualizedBackupCleanupWorker {
	return &VirtualizedBackupCleanupWorker{
		historyRepo:   historyRepo,
		tombstoneRepo: tombstoneRepo,
		slotRepo:      slotRepo,
	}
}

func (w *VirtualizedBackupCleanupWorker) Clean(ctx context.Context) error {
	logger := utils.GetWorkerLoggerFromContext(ctx)
	now := time.Now().UTC()
	physicalExpireBefore := now.Add(-time.Duration(constant.MaxBackupRetentionDays) * 24 * time.Hour)
	var errs error

	for {
		count, err := w.tombstoneRepo.Delete(ctx, entity.BackupTombstoneDeleteOptions{
			ExpireTimeLt:           &now,
			PersistedExpireTimeGte: &now,
			Limit:                  UserBackupCleanBatch,
		})
		if err != nil {
			logger.Error("clean backup tombstones failed", zap.Error(err))
			errs = multierr.Append(errs, err)
			break
		}
		if count == 0 {
			break
		}
	}

	errs = multierr.Append(errs, w.deleteExpiredHistory(ctx, now))
	errs = multierr.Append(errs, w.deleteExpiredSlots(ctx, physicalExpireBefore))
	return errs
}

func (w *VirtualizedBackupCleanupWorker) deleteExpiredHistory(ctx context.Context, now time.Time) error {
	logger := utils.GetWorkerLoggerFromContext(ctx)
	for {
		count, err := w.historyRepo.DeleteExpired(ctx, now, UserBackupCleanBatch)
		if err != nil {
			logger.Error("clean backup schedule histories failed", zap.Error(err))
			return err
		}
		if count == 0 {
			return nil
		}
	}
}

func (w *VirtualizedBackupCleanupWorker) deleteExpiredSlots(ctx context.Context, expireBefore time.Time) error {
	logger := utils.GetWorkerLoggerFromContext(ctx)
	for {
		count, err := w.slotRepo.DeleteExpired(ctx, expireBefore, UserBackupCleanBatch)
		if err != nil {
			logger.Error("clean physical backup slots failed", zap.Error(err))
			return err
		}
		if count == 0 {
			return nil
		}
	}
}
