package cron

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"
	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	dplanemgrv1 "github.com/tidbcloud/cluster-service-ng/api/dataplane-manager/v1"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	dpmgrmocks "github.com/tidbcloud/cluster-service-ng/pkg/dataplanemgr/mocks"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/mysql/schema"
	repomocks "github.com/tidbcloud/cluster-service-ng/pkg/storage/repo/mocks"
	"google.golang.org/protobuf/types/known/emptypb"
)

func TestCleanManualBackupDeletionRulesJobBlocksActivePausedPool(t *testing.T) {
	states := []struct {
		name    string
		state   entity.PauseResumeState
		desired bool
	}{
		{name: "pausing", state: entity.PauseResumeStatePausing},
		{name: "paused", state: entity.PauseResumeStatePaused, desired: true},
		{name: "resuming", state: entity.PauseResumeStateResuming},
	}

	for _, tt := range states {
		t.Run(tt.name, func(t *testing.T) {
			sharedPool := &entity.SharedPool{
				SharedPool:            apiv1.SharedPool{Id: "sp-1", DataplaneName: "dp-1"},
				PauseResumeState:      tt.state,
				SuspendComputeDesired: tt.desired,
			}
			manualBackupRepo := repomocks.NewManualBackupRepo(t)
			sharedPoolRepo := repomocks.NewSharedPoolRepo(t)
			objectStoreRepo := repomocks.NewSharedPoolObjectStoreRepo(t)
			bucketSvc := dpmgrmocks.NewClient(t)
			sharedPoolRepo.EXPECT().Load(mock.Anything, sharedPool.GetId(), true).Return(sharedPool, nil).Once()

			job := NewCleanManualBackupDeletionRulesJob("@daily", manualBackupRepo, sharedPoolRepo, objectStoreRepo, bucketSvc)
			job.processSharedPool(context.Background(), sharedPool.GetId(), []*entity.ManualBackup{{}})

			objectStoreRepo.AssertNotCalled(t, "Load", mock.Anything, mock.Anything, mock.Anything, mock.Anything)
			bucketSvc.AssertNotCalled(t, "GetBucket", mock.Anything, mock.Anything)
			bucketSvc.AssertNotCalled(t, "DeleteManualBackupLifecycleRules", mock.Anything, mock.Anything)
			manualBackupRepo.AssertNotCalled(t, "Save", mock.Anything, mock.Anything)
		})
	}
}

func TestCleanManualBackupDeletionRulesJobAllowsDeletedPausedPoolTeardown(t *testing.T) {
	ctx := context.Background()
	deletedAt := time.Now()
	sharedPool := &entity.SharedPool{
		SharedPool:            apiv1.SharedPool{Id: "sp-1", DataplaneName: "dp-1"},
		DeletedAt:             &deletedAt,
		PauseResumeState:      entity.PauseResumeStatePaused,
		SuspendComputeDesired: true,
	}
	backup := &entity.ManualBackup{ManualBackup: schema.ManualBackup{
		SharedPoolID: sharedPool.GetId(),
		ClusterID:    "cluster-1",
		BackupID:     "BACKUP-1",
	}}
	manualBackupRepo := repomocks.NewManualBackupRepo(t)
	sharedPoolRepo := repomocks.NewSharedPoolRepo(t)
	objectStoreRepo := repomocks.NewSharedPoolObjectStoreRepo(t)
	bucketSvc := dpmgrmocks.NewClient(t)
	sharedPoolRepo.EXPECT().Load(mock.Anything, sharedPool.GetId(), true).Return(sharedPool, nil).Once()
	objectStoreRepo.EXPECT().Load(mock.Anything, sharedPool.GetId(), schema.SharedPoolObjectStoreTypeData, true).
		Return(&entity.SharedPoolObjectStore{SharedPoolObjectStore: schema.SharedPoolObjectStore{StoreBucket: "bucket-1"}}, nil).Once()
	bucketSvc.EXPECT().GetBucket(mock.Anything, &dplanemgrv1.GetBucketRequest{
		DataplaneId: sharedPool.GetDataplaneName(),
		BucketId:    "bucket-1",
	}).Return(&dplanemgrv1.Bucket{}, nil).Once()
	bucketSvc.EXPECT().DeleteManualBackupLifecycleRules(mock.Anything, mock.MatchedBy(func(req *dplanemgrv1.DeleteManualBackupLifecycleRulesRequest) bool {
		return req.GetDataplaneId() == sharedPool.GetDataplaneName() && req.GetBucketId() == "bucket-1" &&
			assert.Equal(t, []string{"manual-backup-del-cluster-1-backup-1"}, req.GetRuleIds())
	})).Return(&emptypb.Empty{}, nil).Once()
	manualBackupRepo.EXPECT().Save(mock.Anything, backup).Return(nil).Once()

	job := NewCleanManualBackupDeletionRulesJob("@daily", manualBackupRepo, sharedPoolRepo, objectStoreRepo, bucketSvc)
	job.processSharedPool(ctx, sharedPool.GetId(), []*entity.ManualBackup{backup})

	require.NotNil(t, backup.LifecycleRuleDeleted)
}
