package service

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/serverless-service/internal/domain/constant"
	"github.com/tidbcloud/serverless-service/internal/domain/entity"
	"github.com/tidbcloud/serverless-service/internal/domain/repo/mock_repo"
	"go.uber.org/mock/gomock"
)

func TestVirtualizedBackupCleanupWorkerCleansAllVirtualizedData(t *testing.T) {
	ctrl := gomock.NewController(t)
	historyRepo := mock_repo.NewMockBackupScheduleHistoryRepo(ctrl)
	tombstoneRepo := mock_repo.NewMockBackupTombstoneRepo(ctrl)
	slotRepo := mock_repo.NewMockPhysicalBackupSlotRepo(ctrl)

	tombstoneRepo.EXPECT().Delete(gomock.Any(), gomock.Any()).Do(
		func(_ context.Context, options entity.BackupTombstoneDeleteOptions) {
			require.NotNil(t, options.ExpireTimeLt)
			require.NotNil(t, options.PersistedExpireTimeGte)
			require.Equal(t, UserBackupCleanBatch, options.Limit)
		},
	).Return(int64(1), nil)
	tombstoneRepo.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(int64(0), nil)
	historyRepo.EXPECT().DeleteExpired(gomock.Any(), gomock.Any(), UserBackupCleanBatch).Return(int64(1), nil)
	historyRepo.EXPECT().DeleteExpired(gomock.Any(), gomock.Any(), UserBackupCleanBatch).Return(int64(0), nil)
	slotRepo.EXPECT().DeleteExpired(gomock.Any(), gomock.Any(), UserBackupCleanBatch).Return(int64(1), nil)
	slotRepo.EXPECT().DeleteExpired(gomock.Any(), gomock.Any(), UserBackupCleanBatch).Return(int64(0), nil)

	worker := NewVirtualizedBackupCleanupWorker(historyRepo, tombstoneRepo, slotRepo)
	require.NoError(t, worker.Clean(context.Background()))
}

func TestVirtualizedBackupCleanupWorkerUsesPhysicalExpiryBoundary(t *testing.T) {
	ctrl := gomock.NewController(t)
	historyRepo := mock_repo.NewMockBackupScheduleHistoryRepo(ctrl)
	tombstoneRepo := mock_repo.NewMockBackupTombstoneRepo(ctrl)
	slotRepo := mock_repo.NewMockPhysicalBackupSlotRepo(ctrl)
	retention := time.Duration(constant.MaxBackupRetentionDays) * 24 * time.Hour
	now := time.Now().UTC()

	tombstoneRepo.EXPECT().Delete(gomock.Any(), gomock.Any()).Do(
		func(_ context.Context, options entity.BackupTombstoneDeleteOptions) {
			require.WithinDuration(t, time.Now().UTC(), *options.ExpireTimeLt, time.Second)
			require.WithinDuration(t, time.Now().UTC(), *options.PersistedExpireTimeGte, time.Second)
		},
	).Return(int64(0), nil)
	historyRepo.EXPECT().DeleteExpired(gomock.Any(), gomock.Cond(func(value time.Time) bool {
		return value.After(now.Add(-time.Second)) && value.Before(time.Now().UTC().Add(time.Second))
	}), UserBackupCleanBatch).Return(int64(0), nil)
	slotRepo.EXPECT().DeleteExpired(gomock.Any(), gomock.Cond(func(value time.Time) bool {
		return time.Since(value) >= retention && time.Since(value) < retention+time.Second
	}), UserBackupCleanBatch).Return(int64(0), nil)

	worker := NewVirtualizedBackupCleanupWorker(historyRepo, tombstoneRepo, slotRepo)
	require.NoError(t, worker.Clean(context.Background()))
}
