package server

import (
	"context"
	"testing"
	"time"

	"github.com/samber/lo"
	"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"
	"github.com/tidbcloud/cluster-service-ng/config"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"github.com/tidbcloud/cluster-service-ng/domain/workflow"
	"github.com/tidbcloud/cluster-service-ng/domain/workflow/dag"
	dbmocks "github.com/tidbcloud/cluster-service-ng/pkg/externalmocks/dbmocks"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/actions"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/executor"
	executormocks "github.com/tidbcloud/cluster-service-ng/pkg/runtime/executor/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/grpc/codes"
	"google.golang.org/grpc/status"
)

func TestDoUpdateBackupGcSettingValidation(t *testing.T) {
	tests := []struct {
		name              string
		curSetting        *entity.BackupGcSetting
		req               *apiv1.BackupGcSetting
		sharedPoolDeleted bool
		expectError       bool
		errorCode         codes.Code
		errorMsg          string
	}{
		{
			name: "should fail when shared pool is creating",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:           schema.BackupGcStatusSPCreating,
					MinT2DataAgeDays: 30,
					MinT3DataAgeDays: 90,
				},
			},
			req: &apiv1.BackupGcSetting{
				MinT2DataAgeDays: 40,
				MinT3DataAgeDays: 100,
			},
			expectError: true,
			errorCode:   codes.FailedPrecondition,
			errorMsg:    "sharedpool is creating or deleting, no allow to change backup gc setting",
		},
		{
			name: "should fail when shared pool is deleting",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:           schema.BackupGcStatusSPDeleting,
					MinT2DataAgeDays: 30,
					MinT3DataAgeDays: 90,
				},
			},
			req: &apiv1.BackupGcSetting{
				MinT2DataAgeDays: 40,
				MinT3DataAgeDays: 95,
			},
			expectError: true,
			errorCode:   codes.FailedPrecondition,
			errorMsg:    "sharedpool is creating or deleting, no allow to change backup gc setting",
		},
		{
			name: "should fail when shared pool is not running and MinT2DataAgeDays changes",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:           schema.BackupGcStatusSPDeleted,
					MinT2DataAgeDays: 30,
					MinT3DataAgeDays: 90,
				},
			},
			req: &apiv1.BackupGcSetting{
				MinT2DataAgeDays: 60, // changed from 30
				MinT3DataAgeDays: 90,
			},
			expectError: true,
			errorCode:   codes.FailedPrecondition,
			errorMsg:    "sharedpool is not running, no allow to change MinT2DataAgeDays or MinT3DataAgeDays",
		},
		{
			name: "should fail when shared pool is not running and MinT3DataAgeDays changes",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:           schema.BackupGcStatusSPDeleted,
					MinT2DataAgeDays: 30,
					MinT3DataAgeDays: 90,
				},
			},
			req: &apiv1.BackupGcSetting{
				MinT2DataAgeDays: 30,
				MinT3DataAgeDays: 120, // changed from 90
			},
			expectError: true,
			errorCode:   codes.FailedPrecondition,
			errorMsg:    "sharedpool is not running, no allow to change MinT2DataAgeDays or MinT3DataAgeDays",
		},
		{
			name: "should fail when shared pool is not running and both MinT2 and MinT3 change",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:           schema.BackupGcStatusSPDeleted,
					MinT2DataAgeDays: 30,
					MinT3DataAgeDays: 90,
				},
			},
			req: &apiv1.BackupGcSetting{
				MinT2DataAgeDays: 60,  // changed from 30
				MinT3DataAgeDays: 120, // changed from 90
			},
			expectError: true,
			errorCode:   codes.FailedPrecondition,
			errorMsg:    "sharedpool is not running, no allow to change MinT2DataAgeDays or MinT3DataAgeDays",
		},
		{
			name: "should pass when shared pool is running and MinT2DataAgeDays changes",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:           schema.BackupGcStatusSPRunning,
					MinT2DataAgeDays: 30,
					MinT3DataAgeDays: 90,
				},
			},
			req: &apiv1.BackupGcSetting{
				MinT2DataAgeDays: 60, // changed from 30
				MinT3DataAgeDays: 90,
			},
			expectError: false,
		},
		{
			name: "should reject archive delay changes when shared pool is deleted but gc setting is still running",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:           schema.BackupGcStatusSPRunning,
					MinT2DataAgeDays: 30,
					MinT3DataAgeDays: 90,
				},
			},
			req: &apiv1.BackupGcSetting{
				MinT2DataAgeDays: 60,
				MinT3DataAgeDays: 90,
			},
			sharedPoolDeleted: true,
			expectError:       true,
			errorCode:         codes.FailedPrecondition,
			errorMsg:          "sharedpool is not running, no allow to change MinT2DataAgeDays or MinT3DataAgeDays",
		},
		{
			name: "should pass when shared pool is running and MinT3DataAgeDays changes",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:           schema.BackupGcStatusSPRunning,
					MinT2DataAgeDays: 30,
					MinT3DataAgeDays: 90,
				},
			},
			req: &apiv1.BackupGcSetting{
				MinT2DataAgeDays: 30,
				MinT3DataAgeDays: 120, // changed from 90
			},
			expectError: false,
		},
		{
			name: "should pass when shared pool is running and both MinT2 and MinT3 change",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:           schema.BackupGcStatusSPRunning,
					MinT2DataAgeDays: 30,
					MinT3DataAgeDays: 90,
				},
			},
			req: &apiv1.BackupGcSetting{
				MinT2DataAgeDays: 60,  // changed from 30
				MinT3DataAgeDays: 120, // changed from 90
			},
			expectError: false,
		},
		{
			name: "should pass when shared pool is not running but MinT2 and MinT3 don't change",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:                schema.BackupGcStatusSPDeleted,
					MinT2DataAgeDays:      30,
					MinT3DataAgeDays:      90,
					MaxT1MetaLifetimeDays: lo.ToPtr(uint32(10)),
				},
			},
			req: &apiv1.BackupGcSetting{
				MinT2DataAgeDays:      30, // same as current
				MinT3DataAgeDays:      90, // same as current
				MaxT1MetaLifetimeDays: 30,
			},
			expectError: false,
		},
		{
			name: "should pass when shared pool is running and no changes to MinT2 and MinT3",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:           schema.BackupGcStatusSPRunning,
					MinT2DataAgeDays: 30,
					MinT3DataAgeDays: 90,
				},
			},
			req: &apiv1.BackupGcSetting{
				MinT2DataAgeDays: 30, // same as current
				MinT3DataAgeDays: 90, // same as current
			},
			expectError: false,
		},
		{
			name: "should fail when T2StrategyEnabled changes",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:            schema.BackupGcStatusSPRunning,
					T2StrategyEnabled: lo.ToPtr(true),
					T3StrategyEnabled: lo.ToPtr(true),
					MinT2DataAgeDays:  30,
					MinT3DataAgeDays:  90,
				},
			},
			req: &apiv1.BackupGcSetting{
				T2StrategyEnabled: false, // changed from true
				T3StrategyEnabled: true,
				MinT2DataAgeDays:  30,
				MinT3DataAgeDays:  90,
			},
			expectError: true,
			errorCode:   codes.InvalidArgument,
			errorMsg:    "not allow to change T2StrategyEnabled",
		},
		{
			name: "should fail when T3StrategyEnabled changes",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:            schema.BackupGcStatusSPRunning,
					T2StrategyEnabled: lo.ToPtr(true),
					T3StrategyEnabled: lo.ToPtr(true),
					MinT2DataAgeDays:  30,
					MinT3DataAgeDays:  90,
				},
			},
			req: &apiv1.BackupGcSetting{
				T2StrategyEnabled: true,
				T3StrategyEnabled: false, // changed from true
				MinT2DataAgeDays:  30,
				MinT3DataAgeDays:  90,
			},
			expectError: true,
			errorCode:   codes.InvalidArgument,
			errorMsg:    "not allow to change T3StrategyEnabled",
		},
		{
			name: "should fail when both T2StrategyEnabled and T3StrategyEnabled change",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:            schema.BackupGcStatusSPRunning,
					T2StrategyEnabled: lo.ToPtr(true),
					T3StrategyEnabled: lo.ToPtr(true),
					MinT2DataAgeDays:  30,
					MinT3DataAgeDays:  90,
				},
			},
			req: &apiv1.BackupGcSetting{
				T2StrategyEnabled: false, // changed from true
				T3StrategyEnabled: false, // changed from true
				MinT2DataAgeDays:  30,
				MinT3DataAgeDays:  90,
			},
			expectError: true,
			errorCode:   codes.InvalidArgument,
			errorMsg:    "not allow to change T2StrategyEnabled",
		},
		{
			name: "should pass when T2StrategyEnabled and T3StrategyEnabled don't change",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:            schema.BackupGcStatusSPRunning,
					T2StrategyEnabled: lo.ToPtr(true),
					T3StrategyEnabled: lo.ToPtr(true),
					MinT2DataAgeDays:  30,
					MinT3DataAgeDays:  90,
				},
			},
			req: &apiv1.BackupGcSetting{
				T2StrategyEnabled: true, // same as current
				T3StrategyEnabled: true, // same as current
				MinT2DataAgeDays:  60,   // changed from 30
				MinT3DataAgeDays:  120,  // changed from 90
			},
			expectError: false,
		},
		{
			name: "should fail when MinT2DataAgeDays is 0",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:            schema.BackupGcStatusSPRunning,
					T2StrategyEnabled: lo.ToPtr(true),
					T3StrategyEnabled: lo.ToPtr(true),
					MinT2DataAgeDays:  30,
					MinT3DataAgeDays:  90,
				},
			},
			req: &apiv1.BackupGcSetting{
				T2StrategyEnabled: true,
				T3StrategyEnabled: true,
				MinT2DataAgeDays:  0, // invalid value
				MinT3DataAgeDays:  90,
			},
			expectError: true,
			errorCode:   codes.InvalidArgument,
			errorMsg:    "min t2 data age days is 1",
		},
		{
			name: "should fail when MinT3DataAgeDays is 0",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:            schema.BackupGcStatusSPRunning,
					T2StrategyEnabled: lo.ToPtr(true),
					T3StrategyEnabled: lo.ToPtr(true),
					MinT2DataAgeDays:  30,
					MinT3DataAgeDays:  90,
				},
			},
			req: &apiv1.BackupGcSetting{
				T2StrategyEnabled: true,
				T3StrategyEnabled: true,
				MinT2DataAgeDays:  30,
				MinT3DataAgeDays:  0, // invalid value
			},
			expectError: true,
			errorCode:   codes.InvalidArgument,
			errorMsg:    "min t3 data age days is 1",
		},
		{
			name: "should fail when both MinT2DataAgeDays and MinT3DataAgeDays are 0",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:            schema.BackupGcStatusSPRunning,
					T2StrategyEnabled: lo.ToPtr(true),
					T3StrategyEnabled: lo.ToPtr(true),
					MinT2DataAgeDays:  30,
					MinT3DataAgeDays:  90,
				},
			},
			req: &apiv1.BackupGcSetting{
				T2StrategyEnabled: true,
				T3StrategyEnabled: true,
				MinT2DataAgeDays:  0, // invalid value
				MinT3DataAgeDays:  0, // invalid value
			},
			expectError: true,
			errorCode:   codes.InvalidArgument,
			errorMsg:    "min t2 data age days is 1",
		},
		{
			name: "should pass when MinT2DataAgeDays and MinT3DataAgeDays are valid (greater than 0)",
			curSetting: &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					Status:            schema.BackupGcStatusSPRunning,
					T2StrategyEnabled: lo.ToPtr(true),
					T3StrategyEnabled: lo.ToPtr(true),
					MinT2DataAgeDays:  30,
					MinT3DataAgeDays:  90,
				},
			},
			req: &apiv1.BackupGcSetting{
				T2StrategyEnabled: true,
				T3StrategyEnabled: true,
				MinT2DataAgeDays:  1, // valid minimum value
				MinT3DataAgeDays:  1, // valid minimum value
			},
			expectError: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := doUpdateBackupGcSettingValidation(tt.curSetting, tt.req, tt.sharedPoolDeleted)

			if tt.expectError {
				if err == nil {
					t.Errorf("expected error but got none")
					return
				}

				st, ok := status.FromError(err)
				if !ok {
					t.Errorf("expected gRPC status error but got: %v", err)
					return
				}

				if st.Code() != tt.errorCode {
					t.Errorf("expected error code %v but got %v", tt.errorCode, st.Code())
				}

				if st.Message() != tt.errorMsg {
					t.Errorf("expected error message %q but got %q", tt.errorMsg, st.Message())
				}
			} else if err != nil {
				t.Errorf("expected no error but got: %v", err)
			}
		})
	}
}

func TestUpdateBackupGcSetting_StaticRetentionConfig(t *testing.T) {
	tests := []struct {
		name           string
		bizConfig      *config.BizConfig
		expectedT1Meta uint32
		expectedT1Wal  uint32
		expectedT2Sst  uint32
		expectedT3Data uint32
	}{
		{
			name: "all static retention values configured should override",
			bizConfig: &config.BizConfig{
				DefaultTiBRConfig: config.TiBRConfig{
					MaxRetainT1AfterSPDeletedDays: 120,
					MaxRetainT2AfterSPDeletedDays: 240,
					MaxRetainT3AfterSPDeletedDays: 360,
				},
			},
			expectedT1Meta: 120,
			expectedT1Wal:  120,
			expectedT2Sst:  240,
			expectedT3Data: 360,
		},
		{
			name: "no static retention values should use request values",
			bizConfig: &config.BizConfig{
				DefaultTiBRConfig: config.TiBRConfig{},
			},
			expectedT1Meta: 101,
			expectedT1Wal:  102,
			expectedT2Sst:  104,
			expectedT3Data: 105,
		},
		{
			name: "partial static retention values should override partially",
			bizConfig: &config.BizConfig{
				DefaultTiBRConfig: config.TiBRConfig{
					MaxRetainT1AfterSPDeletedDays: 90,
					MaxRetainT2AfterSPDeletedDays: 0,
					MaxRetainT3AfterSPDeletedDays: 180,
				},
			},
			expectedT1Meta: 90,
			expectedT1Wal:  90,
			expectedT2Sst:  104,
			expectedT3Data: 180,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			backupGcSettingRepo := repomocks.NewBackupGcSettingRepo(t)
			dagExecutor := executormocks.NewRemoteExecutor(t)
			transactioner := dbmocks.NewTransactioner(t)

			currentSetting := &entity.BackupGcSetting{
				BackupGcSetting: schema.BackupGcSetting{
					SharedPoolID:            "sp-1",
					Status:                  schema.BackupGcStatusSPRunning,
					HasManualBackup:         lo.ToPtr(true),
					T2StrategyEnabled:       lo.ToPtr(true),
					MinT2DataAgeDays:        7,
					T3StrategyEnabled:       lo.ToPtr(true),
					MinT3DataAgeDays:        8,
					MaxT1MetaLifetimeDays:   lo.ToPtr(uint32(11)),
					MaxT1WalLifetimeDays:    lo.ToPtr(uint32(12)),
					MaxT1PackedLifetimeDays: lo.ToPtr(uint32(13)),
					MaxT2SstLifetimeDays:    lo.ToPtr(uint32(14)),
					MaxT3DataLifetimeDays:   lo.ToPtr(uint32(15)),
				},
			}

			var savedSetting *entity.BackupGcSetting
			backupGcSettingRepo.On("Load", mock.Anything, "sp-1").Return(currentSetting, nil).Once()
			backupGcSettingRepo.On("Save", mock.Anything, mock.AnythingOfType("*entity.BackupGcSetting")).Run(func(args mock.Arguments) {
				savedSetting = args.Get(1).(*entity.BackupGcSetting)
			}).Return(nil).Once()

			dagExecutor.On("QueryRunnings", mock.Anything, dag.PlanUpdateBackupGcSetting, mock.Anything).Return(executor.NewExecTaskIdStatus(), nil).Once()
			dagExecutor.On("Submit", mock.Anything, dag.PlanUpdateBackupGcSetting, actions.ActionArgs{
				workflow.SharedPoolIdKey: actions.ActionArgString("sp-1"),
			}, mock.AnythingOfType("string")).Return(nil).Once()

			transactioner.EXPECT().Transaction(mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, cb func(ctx context.Context) error) error {
				return cb(ctx)
			}).Once()

			s := &serverImpl{
				backupGcSettingRepo: backupGcSettingRepo,
				sharedPoolRepo:      availableSharedPoolRepoForCleanupMutationTest(),
				dagExecutor:         dagExecutor,
				transactioner:       transactioner,
				bizCfg:              tt.bizConfig,
			}

			req := &UpdateBackupGcSettingInput{
				UpdateBackupGcSettingRequest: &apiv1.UpdateBackupGcSettingRequest{
					BackupGcSetting: &apiv1.BackupGcSetting{
						SharedPoolId:            "sp-1",
						HasManualBackup:         false,
						T2StrategyEnabled:       true,
						MinT2DataAgeDays:        30,
						T3StrategyEnabled:       true,
						MinT3DataAgeDays:        60,
						MaxT1MetaLifetimeDays:   101,
						MaxT1WalLifetimeDays:    102,
						MaxT1PackedLifetimeDays: 103,
						MaxT2SstLifetimeDays:    104,
						MaxT3DataLifetimeDays:   105,
					},
				},
			}

			resp, err := s.UpdateBackupGcSetting(context.Background(), req)
			require.NoError(t, err)
			require.NotNil(t, resp)
			require.NotNil(t, savedSetting)

			require.NotNil(t, savedSetting.HasManualBackup)
			assert.False(t, *savedSetting.HasManualBackup)
			assert.Equal(t, uint32(30), savedSetting.MinT2DataAgeDays)
			assert.Equal(t, uint32(60), savedSetting.MinT3DataAgeDays)

			require.NotNil(t, savedSetting.MaxT1MetaLifetimeDays)
			require.NotNil(t, savedSetting.MaxT1WalLifetimeDays)
			require.NotNil(t, savedSetting.MaxT1PackedLifetimeDays)
			require.NotNil(t, savedSetting.MaxT2SstLifetimeDays)
			require.NotNil(t, savedSetting.MaxT3DataLifetimeDays)

			assert.Equal(t, tt.expectedT1Meta, *savedSetting.MaxT1MetaLifetimeDays)
			assert.Equal(t, tt.expectedT1Wal, *savedSetting.MaxT1WalLifetimeDays)
			assert.Equal(t, uint32(103), *savedSetting.MaxT1PackedLifetimeDays)
			assert.Equal(t, tt.expectedT2Sst, *savedSetting.MaxT2SstLifetimeDays)
			assert.Equal(t, tt.expectedT3Data, *savedSetting.MaxT3DataLifetimeDays)
			assert.Equal(t, apiv1.BackupGcSettingStatus_SP_RUNNING, resp.GetStatus())
		})
	}
}

func TestUpdateBackupGcSettingAllowsDeletedSharedPoolCleanup(t *testing.T) {
	ctx := context.Background()
	backupGcSettingRepo := repomocks.NewBackupGcSettingRepo(t)
	sharedPoolRepo := repomocks.NewSharedPoolRepo(t)
	dagExecutor := executormocks.NewRemoteExecutor(t)
	transactioner := dbmocks.NewTransactioner(t)

	currentSetting := &entity.BackupGcSetting{BackupGcSetting: schema.BackupGcSetting{
		SharedPoolID:          "sp-deleted",
		Status:                schema.BackupGcStatusSPDeleted,
		T2StrategyEnabled:     lo.ToPtr(true),
		MinT2DataAgeDays:      3,
		T3StrategyEnabled:     lo.ToPtr(true),
		MinT3DataAgeDays:      3,
		MaxT1MetaLifetimeDays: lo.ToPtr(uint32(10)),
		MaxT1WalLifetimeDays:  lo.ToPtr(uint32(10)),
		MaxT2SstLifetimeDays:  lo.ToPtr(uint32(10)),
		MaxT3DataLifetimeDays: lo.ToPtr(uint32(10)),
	}}
	deletedAt := time.Now()
	deletedPool := &entity.SharedPool{
		SharedPool: apiv1.SharedPool{Id: "sp-deleted"},
		DeletedAt:  &deletedAt,
	}

	sharedPoolRepo.EXPECT().Load(ctx, "sp-deleted", true).Return(deletedPool, nil)
	dagExecutor.EXPECT().QueryRunnings(ctx, dag.PlanUpdateBackupGcSetting, mock.Anything).
		Return(executor.NewExecTaskIdStatus(), nil)
	backupGcSettingRepo.EXPECT().Load(ctx, "sp-deleted").Return(currentSetting, nil)
	backupGcSettingRepo.EXPECT().Save(mock.Anything, mock.AnythingOfType("*entity.BackupGcSetting")).Return(nil)
	dagExecutor.EXPECT().Submit(mock.Anything, dag.PlanUpdateBackupGcSetting, actions.ActionArgs{
		workflow.SharedPoolIdKey: actions.ActionArgString("sp-deleted"),
	}, mock.AnythingOfType("string")).Return(nil)
	transactioner.EXPECT().Transaction(ctx, mock.Anything).RunAndReturn(
		func(txCtx context.Context, callback func(context.Context) error) error {
			return callback(txCtx)
		},
	)

	server := &serverImpl{
		backupGcSettingRepo: backupGcSettingRepo,
		sharedPoolRepo:      sharedPoolRepo,
		dagExecutor:         dagExecutor,
		transactioner:       transactioner,
		bizCfg:              &config.BizConfig{},
	}
	response, err := server.UpdateBackupGcSetting(ctx, &UpdateBackupGcSettingInput{
		UpdateBackupGcSettingRequest: &apiv1.UpdateBackupGcSettingRequest{
			BackupGcSetting: &apiv1.BackupGcSetting{
				SharedPoolId:          "sp-deleted",
				T2StrategyEnabled:     true,
				MinT2DataAgeDays:      3,
				T3StrategyEnabled:     true,
				MinT3DataAgeDays:      3,
				MaxT1MetaLifetimeDays: 5,
				MaxT1WalLifetimeDays:  5,
				MaxT2SstLifetimeDays:  5,
				MaxT3DataLifetimeDays: 5,
			},
		},
	})

	require.NoError(t, err)
	require.Equal(t, apiv1.BackupGcSettingStatus_SP_DELETED, response.GetStatus())
}

func TestUpdateBackupGcSettingRejectsArchiveDelayChangeDuringPoolDeletion(t *testing.T) {
	ctx := context.Background()
	backupGcSettingRepo := repomocks.NewBackupGcSettingRepo(t)
	sharedPoolRepo := repomocks.NewSharedPoolRepo(t)
	dagExecutor := executormocks.NewRemoteExecutor(t)
	deletedAt := time.Now()

	sharedPoolRepo.EXPECT().Load(ctx, "sp-deleted", true).Return(&entity.SharedPool{
		SharedPool: apiv1.SharedPool{Id: "sp-deleted"},
		DeletedAt:  &deletedAt,
	}, nil)
	dagExecutor.EXPECT().QueryRunnings(ctx, dag.PlanUpdateBackupGcSetting, mock.Anything).
		Return(executor.NewExecTaskIdStatus(), nil)
	backupGcSettingRepo.EXPECT().Load(ctx, "sp-deleted").Return(&entity.BackupGcSetting{
		BackupGcSetting: schema.BackupGcSetting{
			SharedPoolID:      "sp-deleted",
			Status:            schema.BackupGcStatusSPRunning,
			T2StrategyEnabled: lo.ToPtr(true),
			MinT2DataAgeDays:  3,
			T3StrategyEnabled: lo.ToPtr(true),
			MinT3DataAgeDays:  3,
		},
	}, nil)

	server := &serverImpl{
		backupGcSettingRepo: backupGcSettingRepo,
		sharedPoolRepo:      sharedPoolRepo,
		dagExecutor:         dagExecutor,
	}
	_, err := server.UpdateBackupGcSetting(ctx, &UpdateBackupGcSettingInput{
		UpdateBackupGcSettingRequest: &apiv1.UpdateBackupGcSettingRequest{
			BackupGcSetting: &apiv1.BackupGcSetting{
				SharedPoolId:      "sp-deleted",
				T2StrategyEnabled: true,
				MinT2DataAgeDays:  4,
				T3StrategyEnabled: true,
				MinT3DataAgeDays:  3,
			},
		},
	})

	require.Equal(t, codes.FailedPrecondition, status.Code(err))
}
