package br

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/mock/gomock"
	"google.golang.org/protobuf/proto"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/regionalserver"
	mockregionalserver "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/regionalserver/mock_regionalserver"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	brrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/br"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/br/mock_br"
	mockcluster "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/cluster/mock_cluster"
	mockregion "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/region/mock_region"
	mockresourcepool "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/resourcepool/mock_resourcepool"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/infra/repoimpl/schema"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/pagination"
)

func TestCalculateLongestRetentionDaysForSharedPool(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	ctx := t.Context()

	backupPolicies := []*entity.BackupPolicy{
		{
			BackupPolicy: &schema.BackupPolicy{
				Name: schema.BackupPolicyNameTIER1,
				Pitr: &schema.BackupPolicyPiTR{
					RetentionDays: 7,
				},
				DailyBackup: &schema.BackupPolicyDailyBackup{
					RetentionDays: 33,
				},
			},
		},
		{
			BackupPolicy: &schema.BackupPolicy{
				Name: schema.BackupPolicyNameTIER2,
				Pitr: &schema.BackupPolicyPiTR{
					RetentionDays: 15,
				},
				DailyBackup: &schema.BackupPolicyDailyBackup{
					RetentionDays: 60,
				},
			},
		},
		{
			BackupPolicy: &schema.BackupPolicy{
				Name: schema.BackupPolicyNameTIER3,
				Pitr: &schema.BackupPolicyPiTR{
					RetentionDays: 32,
				},
				DailyBackup: &schema.BackupPolicyDailyBackup{
					RetentionDays: 90,
				},
			},
		},
	}

	t.Run("multiple backup policies - find longest", func(t *testing.T) {
		mockBackupPolicyRepo := mock_br.NewMockBackupPolicyRepo(ctrl)

		mockBackupPolicyRepo.EXPECT().ListAll(ctx).Return(backupPolicies, nil)

		backupSettings := []*entity.BackupSetting{
			{
				BackupSetting: &schema.BackupSetting{
					BackupPolicy: schema.BackupPolicyNameTIER1,
				},
			},
			{
				BackupSetting: &schema.BackupSetting{
					BackupPolicy: schema.BackupPolicyNameTIER3,
				},
			},
		}

		longestPitr, longestDaily, err := calculateLongestRetentionDaysForSharedPool(
			ctx,
			backupSettings,
			mockBackupPolicyRepo,
		)
		require.NoError(t, err)
		assert.Equal(t, 32, longestPitr)  // TIER3 has longest PiTR
		assert.Equal(t, 90, longestDaily) // TIER3 has longest Daily
	})

	t.Run("multiple backup settings with same policy - should only count once", func(t *testing.T) {
		mockBackupPolicyRepo := mock_br.NewMockBackupPolicyRepo(ctrl)

		mockBackupPolicyRepo.EXPECT().ListAll(ctx).Return(backupPolicies, nil)

		backupSettings := []*entity.BackupSetting{
			{
				BackupSetting: &schema.BackupSetting{
					BackupPolicy: schema.BackupPolicyNameTIER1,
				},
			},
			{
				BackupSetting: &schema.BackupSetting{
					BackupPolicy: schema.BackupPolicyNameTIER1,
				},
			},
			{
				BackupSetting: &schema.BackupSetting{
					BackupPolicy: schema.BackupPolicyNameTIER1,
				},
			},
		}

		longestPitr, longestDaily, err := calculateLongestRetentionDaysForSharedPool(
			ctx,
			backupSettings,
			mockBackupPolicyRepo,
		)
		require.NoError(t, err)
		assert.Equal(t, 7, longestPitr)
		assert.Equal(t, 33, longestDaily)
	})

	t.Run("mixed backup settings - some with empty BackupPolicy", func(t *testing.T) {
		mockBackupPolicyRepo := mock_br.NewMockBackupPolicyRepo(ctrl)

		mockBackupPolicyRepo.EXPECT().ListAll(ctx).Return(backupPolicies, nil)

		backupSettings := []*entity.BackupSetting{
			{
				BackupSetting: &schema.BackupSetting{
					BackupPolicy: "", // Empty, should be skipped
				},
			},
			{
				BackupSetting: &schema.BackupSetting{
					BackupPolicy: schema.BackupPolicyNameTIER1,
				},
			},
			{
				BackupSetting: &schema.BackupSetting{
					BackupPolicy: "", // Empty, should be skipped
				},
			},
		}

		longestPitr, longestDaily, err := calculateLongestRetentionDaysForSharedPool(
			ctx,
			backupSettings,
			mockBackupPolicyRepo,
		)
		require.NoError(t, err)
		assert.Equal(t, 7, longestPitr)
		assert.Equal(t, 33, longestDaily)
	})

	t.Run("error - getAllBackupPolicies fails", func(t *testing.T) {
		mockBackupPolicyRepo := mock_br.NewMockBackupPolicyRepo(ctrl)
		expectedErr := bizerr.New(bizerr.ErrCodeDB, "list error")
		mockBackupPolicyRepo.EXPECT().ListAll(ctx).Return(nil, expectedErr)

		backupSettings := []*entity.BackupSetting{
			{
				BackupSetting: &schema.BackupSetting{
					BackupPolicy: schema.BackupPolicyNameTIER1,
				},
			},
		}

		longestPitr, longestDaily, err := calculateLongestRetentionDaysForSharedPool(
			ctx,
			backupSettings,
			mockBackupPolicyRepo,
		)
		require.Error(t, err)
		assert.Contains(t, err.Error(), "failed to get all backup policies")
		assert.Equal(t, 0, longestPitr)
		assert.Equal(t, 0, longestDaily)
	})

	t.Run("multiple policies with different retention days - find maximums", func(t *testing.T) {
		mockBackupPolicyRepo := mock_br.NewMockBackupPolicyRepo(ctrl)

		mockBackupPolicyRepo.EXPECT().ListAll(ctx).Return(backupPolicies, nil)

		backupSettings := []*entity.BackupSetting{
			{
				BackupSetting: &schema.BackupSetting{
					BackupPolicy: schema.BackupPolicyNameTIER1,
				},
			},
			{
				BackupSetting: &schema.BackupSetting{
					BackupPolicy: schema.BackupPolicyNameTIER2,
				},
			},
			{
				BackupSetting: &schema.BackupSetting{
					BackupPolicy: schema.BackupPolicyNameTIER3,
				},
			},
		}

		longestPitr, longestDaily, err := calculateLongestRetentionDaysForSharedPool(
			ctx,
			backupSettings,
			mockBackupPolicyRepo,
		)
		require.NoError(t, err)
		assert.Equal(t, 32, longestPitr)  // TIER2 has longest PiTR
		assert.Equal(t, 90, longestDaily) // TIER3 has longest Daily
	})

	t.Run("mixed standard and flexible settings use effective schedules", func(t *testing.T) {
		mockBackupPolicyRepo := mock_br.NewMockBackupPolicyRepo(ctrl)
		backupSettings := []*entity.BackupSetting{
			{BackupSetting: &schema.BackupSetting{
				Mode: schema.BackupModeStandardBundle,
				Schedule: schema.AutoSchedule{
					NonZeroClockRetentionDays: StandardRetentionDays,
					ZeroClockRetentionDays:    StandardDailyRetentionDays,
				},
			}},
			{BackupSetting: &schema.BackupSetting{
				Mode: schema.BackupModeFlexibleRetention,
				Schedule: schema.AutoSchedule{
					NonZeroClockRetentionDays: 20,
					ZeroClockRetentionDays:    20,
				},
			}},
		}

		longestPitr, longestDaily, err := calculateLongestRetentionDaysForSharedPool(
			ctx, backupSettings, mockBackupPolicyRepo,
		)

		require.NoError(t, err)
		assert.Equal(t, 20, longestPitr)
		assert.Equal(t, StandardDailyRetentionDays, longestDaily)
	})
}

func TestValidateUpdateBackupSettingRequest(t *testing.T) {
	standardSetting := func() *entity.BackupSetting {
		return &entity.BackupSetting{BackupSetting: &schema.BackupSetting{
			ClusterId: 1, Mode: schema.BackupModeStandardBundle,
			RetentionDays: StandardRetentionDays, DailyBackupTimeUTC: DefaultDailyBackupTimeUTC,
		}}
	}
	essentialV2Setting := func() *entity.BackupSetting {
		return &entity.BackupSetting{BackupSetting: &schema.BackupSetting{
			ClusterId: 1, Mode: schema.BackupModeStandardBundle,
			RetentionDays: StandardRetentionDays, DailyBackupTimeUTC: DefaultDailyBackupTimeUTC,
			ServicePlan: globalcommonv1.ServicePlan_Essential_V2.String(),
		}}
	}
	request := func(mode globalngv1.BackupSetting_BackupMode, retention *int32, dailyTime *string) *globalngv1.UpdateBackupSettingRequest {
		return &globalngv1.UpdateBackupSettingRequest{BackupSetting: &globalngv1.BackupSetting{
			ClusterId: "1", Mode: mode, RetentionDays: retention, DailyBackupTimeUtc: dailyTime,
		}}
	}

	tests := []struct {
		name      string
		req       *globalngv1.UpdateBackupSettingRequest
		dbSetting *entity.BackupSetting
		override  bool
		errText   string
	}{
		{
			name:      "missing backup setting",
			req:       &globalngv1.UpdateBackupSettingRequest{},
			dbSetting: standardSetting(),
			errText:   "backup_setting is required",
		},
		{
			name: "unsupported legacy policy",
			req: &globalngv1.UpdateBackupSettingRequest{
				BackupSetting: &globalngv1.BackupSetting{BackupPolicy: globalngv1.BackupSetting_BackupPolicy_TIER2},
			},
			dbSetting: standardSetting(),
			errText:   "only TIER1",
		},
		{
			name:      "standard retention must be seven",
			req:       request(globalngv1.BackupSetting_STANDARD_BUNDLE, proto.Int32(8), nil),
			dbSetting: standardSetting(),
			errText:   "must be 7",
		},
		{
			name:      "standard rejects daily time",
			req:       request(globalngv1.BackupSetting_STANDARD_BUNDLE, proto.Int32(7), proto.String("00:00")),
			dbSetting: standardSetting(),
			errText:   "only supported for flexible",
		},
		{
			name:      "flexible retention required",
			req:       request(globalngv1.BackupSetting_FLEXIBLE_RETENTION, nil, proto.String("08:30")),
			dbSetting: standardSetting(),
			errText:   "between 3 and 33",
		},
		{
			name:      "flexible retention too short",
			req:       request(globalngv1.BackupSetting_FLEXIBLE_RETENTION, proto.Int32(2), proto.String("08:30")),
			dbSetting: standardSetting(),
			errText:   "between 3 and 33",
		},
		{
			name:      "flexible retention too long",
			req:       request(globalngv1.BackupSetting_FLEXIBLE_RETENTION, proto.Int32(34), proto.String("08:30")),
			dbSetting: standardSetting(),
			errText:   "between 3 and 33",
		},
		{
			name:      "flexible daily time required",
			req:       request(globalngv1.BackupSetting_FLEXIBLE_RETENTION, proto.Int32(3), nil),
			dbSetting: standardSetting(),
			errText:   "daily_backup_time_utc is required",
		},
		{
			name:      "flexible daily time invalid",
			req:       request(globalngv1.BackupSetting_FLEXIBLE_RETENTION, proto.Int32(3), proto.String("24:00")),
			dbSetting: standardSetting(),
			errText:   "HH:mm",
		},
		{
			name:      "invalid mode",
			req:       request(globalngv1.BackupSetting_BackupMode(99), nil, nil),
			dbSetting: standardSetting(),
			errText:   "invalid backup mode",
		},
		{
			name:      "no update",
			req:       request(globalngv1.BackupSetting_STANDARD_BUNDLE, proto.Int32(7), nil),
			dbSetting: standardSetting(),
			errText:   "no update",
		},
		{
			name:      "valid flexible",
			req:       request(globalngv1.BackupSetting_FLEXIBLE_RETENTION, proto.Int32(3), proto.String("08:30")),
			dbSetting: standardSetting(),
		},
		{
			name:      "valid internal short retention",
			req:       request(globalngv1.BackupSetting_FLEXIBLE_RETENTION, proto.Int32(1), proto.String("08:30")),
			dbSetting: standardSetting(),
			override:  true,
		},
		{
			name: "valid switch to standard",
			req:  request(globalngv1.BackupSetting_STANDARD_BUNDLE, nil, nil),
			dbSetting: &entity.BackupSetting{
				BackupSetting: &schema.BackupSetting{Mode: schema.BackupModeFlexibleRetention, RetentionDays: 20, DailyBackupTimeUTC: "08:30"},
			},
		},
		{
			name:      "essential v2 defaults unspecified mode to flexible",
			req:       request(globalngv1.BackupSetting_BACKUP_MODE_UNSPECIFIED, nil, nil),
			dbSetting: essentialV2Setting(),
		},
		{
			name:      "essential v2 rejects standard bundle",
			req:       request(globalngv1.BackupSetting_STANDARD_BUNDLE, proto.Int32(StandardRetentionDays), nil),
			dbSetting: essentialV2Setting(),
			errText:   "only support flexible retention",
		},
		{
			name:      "essential v2 retention too short",
			req:       request(globalngv1.BackupSetting_FLEXIBLE_RETENTION, proto.Int32(0), proto.String("08:30")),
			dbSetting: essentialV2Setting(),
			errText:   "between 1 and 30",
		},
		{
			name:      "essential v2 minimum retention",
			req:       request(globalngv1.BackupSetting_FLEXIBLE_RETENTION, proto.Int32(1), proto.String("08:30")),
			dbSetting: essentialV2Setting(),
		},
		{
			name:      "essential v2 maximum retention",
			req:       request(globalngv1.BackupSetting_FLEXIBLE_RETENTION, proto.Int32(30), proto.String("08:30")),
			dbSetting: essentialV2Setting(),
		},
		{
			name:      "essential v2 retention too long",
			req:       request(globalngv1.BackupSetting_FLEXIBLE_RETENTION, proto.Int32(31), proto.String("08:30")),
			dbSetting: essentialV2Setting(),
			errText:   "between 1 and 30",
		},
		{
			name:      "essential v2 incident override keeps existing maximum",
			req:       request(globalngv1.BackupSetting_FLEXIBLE_RETENTION, proto.Int32(33), proto.String("08:30")),
			dbSetting: essentialV2Setting(),
			override:  true,
		},
		{
			name:      "essential v2 incident override rejects above existing maximum",
			req:       request(globalngv1.BackupSetting_FLEXIBLE_RETENTION, proto.Int32(34), proto.String("08:30")),
			dbSetting: essentialV2Setting(),
			override:  true,
			errText:   "between 1 and 33",
		},
	}

	service := &serviceImpl{}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			updateSource := backupSettingUpdateSourceStandard
			if tt.override {
				updateSource = backupSettingUpdateSourceIncidentOverride
			}
			err := service.validateUpdateBackupSettingRequest(tt.req, tt.dbSetting, updateSource)
			if tt.errText == "" {
				require.NoError(t, err)
				return
			}
			require.Error(t, err)
			require.ErrorContains(t, err, tt.errText)
		})
	}
}

func TestNormalizeEssentialV2UnspecifiedMode(t *testing.T) {
	req := &globalngv1.UpdateBackupSettingRequest{BackupSetting: &globalngv1.BackupSetting{
		ClusterId: "1",
	}}
	dbSetting := &entity.BackupSetting{BackupSetting: &schema.BackupSetting{
		ServicePlan: globalcommonv1.ServicePlan_Essential_V2.String(),
		Mode:        schema.BackupModeStandardBundle,
	}}

	normalized := normalizeUpdateBackupSettingRequest(req, dbSetting)

	require.NotSame(t, req, normalized)
	require.Equal(t, globalngv1.BackupSetting_BACKUP_MODE_UNSPECIFIED, req.BackupSetting.Mode)
	require.Equal(t, globalngv1.BackupSetting_FLEXIBLE_RETENTION, normalized.BackupSetting.Mode)
	require.Equal(t, int32(StandardRetentionDays), normalized.BackupSetting.GetRetentionDays())
	require.Equal(t, DefaultDailyBackupTimeUTC, normalized.BackupSetting.GetDailyBackupTimeUtc())
}

func TestNormalizeEssentialV2UnspecifiedModePreservesFlexibleValues(t *testing.T) {
	req := &globalngv1.UpdateBackupSettingRequest{BackupSetting: &globalngv1.BackupSetting{ClusterId: "1"}}
	dbSetting := &entity.BackupSetting{BackupSetting: &schema.BackupSetting{
		ServicePlan:        globalcommonv1.ServicePlan_Essential_V2.String(),
		Mode:               schema.BackupModeFlexibleRetention,
		RetentionDays:      30,
		DailyBackupTimeUTC: "08:00",
	}}

	normalized := normalizeUpdateBackupSettingRequest(req, dbSetting)

	require.Equal(t, globalngv1.BackupSetting_FLEXIBLE_RETENTION, normalized.BackupSetting.Mode)
	require.Equal(t, int32(30), normalized.BackupSetting.GetRetentionDays())
	require.Equal(t, "08:00", normalized.BackupSetting.GetDailyBackupTimeUtc())
}

func TestSystemBackfillSuppressesDisplayEvent(t *testing.T) {
	require.True(t, shouldWriteBackupSettingDisplayEvent(backupSettingUpdateSourceStandard))
	require.True(t, shouldWriteBackupSettingDisplayEvent(backupSettingUpdateSourceIncidentOverride))
	require.False(t, shouldWriteBackupSettingDisplayEvent(backupSettingUpdateSourceSystemBackfill))
}

func TestSystemBackfillDoesNotUpdateLastScheduleTimestamp(t *testing.T) {
	require.True(t, shouldUpdateLastScheduleUpdatedAt(backupSettingUpdateSourceStandard))
	require.True(t, shouldUpdateLastScheduleUpdatedAt(backupSettingUpdateSourceIncidentOverride))
	require.False(t, shouldUpdateLastScheduleUpdatedAt(backupSettingUpdateSourceSystemBackfill))
}

func TestBackfillEssentialV2BackupSettingSkipsFreshFlexibleSetting(t *testing.T) {
	ctrl := gomock.NewController(t)
	settingRepo := mock_br.NewMockBackupSettingRepo(ctrl)
	settingRepo.EXPECT().Get(gomock.Any(), uint64(101)).Return(&entity.BackupSetting{BackupSetting: &schema.BackupSetting{
		ClusterId:   101,
		ServicePlan: globalcommonv1.ServicePlan_Essential_V2.String(),
		Mode:        schema.BackupModeFlexibleRetention,
	}}, nil)
	service := &serviceImpl{backupSettingRepo: settingRepo}

	updated, berr := service.BackfillEssentialV2BackupSetting(t.Context(), 101)

	require.NoError(t, berr)
	require.False(t, updated)
}

func TestNewEssentialV2BackfillRequestUsesThirtyDayRetention(t *testing.T) {
	req := newEssentialV2BackfillRequest(101)

	require.Equal(t, "101", req.BackupSetting.GetClusterId())
	require.Equal(t, globalngv1.BackupSetting_FLEXIBLE_RETENTION, req.BackupSetting.GetMode())
	require.Equal(t, 30, EssentialV2BackfillRetentionDays)
	require.Equal(t, int32(EssentialV2BackfillRetentionDays), req.BackupSetting.GetRetentionDays())
	require.Equal(t, DefaultDailyBackupTimeUTC, req.BackupSetting.GetDailyBackupTimeUtc())
}

func TestUpdateAutoBackupExpiredTimeUsesPersistedTypeAndFallsBackForUnknown(t *testing.T) {
	ctrl := gomock.NewController(t)
	backupRepo := mock_br.NewMockBackupRepo(ctrl)
	service := &serviceImpl{backupRepo: backupRepo}
	now := time.Now().UTC()
	daily := &entity.Backup{Backup: &schema.Backup{
		ID: 1, BackupTime: time.Date(2026, 7, 1, 9, 31, 15, 0, time.UTC),
		ExpirationTime: now.Add(time.Hour), AutoBackupType: schema.AutoBackupTypeDaily,
	}}
	hourly := &entity.Backup{Backup: &schema.Backup{
		ID: 2, BackupTime: time.Date(2026, 7, 1, 8, 31, 15, 0, time.UTC),
		ExpirationTime: now.Add(time.Hour), AutoBackupType: schema.AutoBackupTypeHourly,
	}}
	unknownExpirationTime := now.Add(2 * time.Hour)
	unknown := &entity.Backup{Backup: &schema.Backup{
		ID: 3, BackupTime: time.Date(2026, 7, 1, 10, 31, 15, 0, time.UTC),
		ExpirationTime: unknownExpirationTime, AutoBackupType: schema.AutoBackupTypeUnknown,
	}}
	expired := &entity.Backup{Backup: &schema.Backup{
		ID: 4, BackupTime: time.Date(2026, 7, 1, 10, 30, 0, 0, time.UTC),
		ExpirationTime: now.Add(-time.Hour), AutoBackupType: schema.AutoBackupTypeDaily,
	}}
	setting := &entity.BackupSetting{BackupSetting: &schema.BackupSetting{
		ClusterId: 1,
		Schedule: schema.AutoSchedule{
			Type:                      "per-hour",
			At:                        30,
			DailyBackupMinuteOfDay:    8*60 + 30,
			ZeroClockRetentionDays:    20,
			NonZeroClockRetentionDays: 3,
		},
	}}
	backupRepo.EXPECT().UnscopedQuery(gomock.Any(), gomock.Any(), nil).DoAndReturn(
		func(_ context.Context, filter *brrepo.QueryBackupsFilter, _ *pagination.Page) ([]*entity.Backup, bizerr.BizError) {
			require.Equal(t, []uint64{uint64(1)}, filter.ClusterIds)
			require.Equal(t, []schema.BackupTriggerType{schema.BackupTriggerTypeAuto}, filter.TriggerTypes)
			require.False(t, *filter.Deleted)
			require.NotNil(t, filter.ExpirationTimeGt)
			return []*entity.Backup{daily, hourly, unknown, expired}, nil
		},
	)
	backupRepo.EXPECT().Save(gomock.Any(), daily).Return(nil)
	backupRepo.EXPECT().Save(gomock.Any(), hourly).Return(nil)
	backupRepo.EXPECT().Save(gomock.Any(), unknown).Return(nil)

	err := service.updateAutoBackupExpiredTime(context.Background(), setting)

	require.NoError(t, err)
	require.Equal(t, daily.BackupTime.Add(20*24*time.Hour), daily.ExpirationTime)
	require.Equal(t, hourly.BackupTime.Add(3*24*time.Hour), hourly.ExpirationTime)
	require.Equal(t, unknown.BackupTime.Add(3*24*time.Hour), unknown.ExpirationTime)
	require.Equal(t, schema.AutoBackupTypeUnknown, unknown.AutoBackupType)
}

func TestUpdateAutoBackupMeteringBuildsRegionalRequest(t *testing.T) {
	tests := []struct {
		name        string
		servicePlan globalcommonv1.ServicePlan
		setting     *schema.BackupSetting
		expectedSKU string
	}{
		{
			name:        "premium standard bundle",
			servicePlan: globalcommonv1.ServicePlan_Premium,
			setting: &schema.BackupSetting{
				ClusterId: 1, SharedPoolId: 2, BackupPolicy: schema.BackupPolicyNameTIER1,
				Mode: schema.BackupModeStandardBundle, RetentionDays: 7, DailyBackupTimeUTC: "00:00",
			},
			expectedSKU: "T1",
		},
		{
			name:        "premium reserved flexible retention",
			servicePlan: globalcommonv1.ServicePlan_Premium_Reserved,
			setting: &schema.BackupSetting{
				ClusterId: 1, SharedPoolId: 2, BackupPolicy: schema.BackupPolicyNameTIER1,
				Mode: schema.BackupModeFlexibleRetention, RetentionDays: 20, DailyBackupTimeUTC: "08:30",
			},
			expectedSKU: "T1",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctrl := gomock.NewController(t)
			clusterRepo := mockcluster.NewMockRepo(ctrl)
			resourcePoolRepo := mockresourcepool.NewMockRepo(ctrl)
			regionRepo := mockregion.NewMockRepo(ctrl)
			regionalCli := mockregionalserver.NewMockClient(ctrl)
			clusterRepo.EXPECT().Get(gomock.Any(), uint64(1), false).Return(&entity.Cluster{Cluster: &globalngv1.Cluster{
				ClusterId: 1, ServicePlan: tt.servicePlan,
			}}, nil)
			resourcePool := &entity.ResourcePool{ResourcePool: &globalngv1.ResourcePool{Id: 2}, RegionId: 3}
			region := &entity.Region{Region: &globalcommonv1.Region{Id: globalcommonv1.Region_AWS_US_WEST_2}}
			resourcePoolRepo.EXPECT().Load(gomock.Any(), uint64(2), false).Return(resourcePool, nil)
			regionRepo.EXPECT().Get(gomock.Any(), uint64(3)).Return(region, nil)
			expectedReq := &regionalserver.AutoBackupMeteringRequest{
				ClusterID: "1", SkuName: "backup-storage-v2", SkuValue: tt.expectedSKU, Mode: "STANDARD_BUNDLE",
			}
			if tt.setting.Mode == schema.BackupModeFlexibleRetention {
				expectedReq.Mode = "FLEXIBLE_RETENTION"
				expectedReq.RetentionDays = proto.Uint32(uint32(tt.setting.RetentionDays))
				expectedReq.DailyBackupTimeUTC = proto.String(tt.setting.DailyBackupTimeUTC)
			}
			regionalCli.EXPECT().UpdateAutoBackupMetering(gomock.Any(), region, expectedReq).Return(nil)

			err := UpdateAutoBackupMetering(
				context.Background(),
				&entity.BackupSetting{BackupSetting: tt.setting},
				clusterRepo,
				resourcePoolRepo,
				regionRepo,
				regionalCli,
			)

			require.NoError(t, err)
		})
	}
}

func TestUpdateAutoBackupMeteringSkipsNonPremiumCluster(t *testing.T) {
	ctrl := gomock.NewController(t)
	clusterRepo := mockcluster.NewMockRepo(ctrl)
	resourcePoolRepo := mockresourcepool.NewMockRepo(ctrl)
	regionRepo := mockregion.NewMockRepo(ctrl)
	regionalCli := mockregionalserver.NewMockClient(ctrl)
	clusterRepo.EXPECT().Get(gomock.Any(), uint64(1), false).Return(&entity.Cluster{Cluster: &globalngv1.Cluster{
		ClusterId: 1, ServicePlan: globalcommonv1.ServicePlan_Essential,
	}}, nil)

	err := UpdateAutoBackupMetering(context.Background(), &entity.BackupSetting{BackupSetting: &schema.BackupSetting{
		ClusterId: 1, SharedPoolId: 2, BackupPolicy: schema.BackupPolicyNameTIER1,
	}}, clusterRepo, resourcePoolRepo, regionRepo, regionalCli)

	require.NoError(t, err)
}
