package br

import (
	"context"
	"errors"
	"testing"

	"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/config"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"github.com/tidbcloud/cluster-service-ng/pkg/clustergateway"
	dpmocks "github.com/tidbcloud/cluster-service-ng/pkg/dataplanemgr/mocks"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/mysql/schema"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/repo/mocks"
)

func TestResolveTiDBClusterKeyspaceForBackup(t *testing.T) {
	t.Run("topology driven uses cluster id keyspace", func(t *testing.T) {
		sharedPool := &entity.SharedPool{SharedPool: apiv1.SharedPool{CapacityMode: apiv1.CapacityMode_TOPOLOGY_DRIVEN}}
		assert.Equal(t, "cluster-123", resolveTiDBClusterKeyspaceForBackup(sharedPool, "cluster-123"))
	})

	t.Run("non topology uses cluster id keyspace", func(t *testing.T) {
		sharedPool := &entity.SharedPool{SharedPool: apiv1.SharedPool{CapacityMode: apiv1.CapacityMode_RCU_DRIVEN}}
		assert.Equal(t, "cluster-123", resolveTiDBClusterKeyspaceForBackup(sharedPool, "cluster-123"))
	})

	t.Run("nil shared pool uses cluster id keyspace", func(t *testing.T) {
		assert.Equal(t, "cluster-123", resolveTiDBClusterKeyspaceForBackup(nil, "cluster-123"))
	})
}

func TestGetBucketPermissionContext(t *testing.T) {
	tests := []struct {
		name           string
		restoreID      string
		setupMocks     func(*mocks.LightweightRestoreRepo, *mocks.SharedPoolRepo, *mocks.SharedPoolObjectStoreRepo)
		expectedResult *BucketPermissionContext
		expectedError  string
	}{
		{
			name:      "successful case",
			restoreID: "test-restore-id",
			setupMocks: func(lwRepo *mocks.LightweightRestoreRepo, spRepo *mocks.SharedPoolRepo, osRepo *mocks.SharedPoolObjectStoreRepo) {
				// Mock LightweightRestoreRepo.Load
				restore := &entity.LightweightRestore{
					LightweightRestore: schema.LightweightRestore{
						SourceSharedPoolID: "source-pool-id",
						TargetSharedPoolID: "target-pool-id",
					},
				}
				lwRepo.EXPECT().Load(mock.Anything, "test-restore-id", false).Return(restore, nil)

				// Mock SharedPoolRepo.Load for target shared pool
				targetSharedPool := &entity.SharedPool{
					SharedPool: apiv1.SharedPool{
						Id:            "target-pool-id",
						DataplaneName: "target-dataplane",
					},
				}
				spRepo.EXPECT().Load(mock.Anything, "target-pool-id", false).Return(targetSharedPool, nil)

				// Mock SharedPoolRepo.Load for source shared pool
				sourceSharedPool := &entity.SharedPool{
					SharedPool: apiv1.SharedPool{
						Id:            "source-pool-id",
						DataplaneName: "source-dataplane",
					},
				}
				spRepo.EXPECT().Load(mock.Anything, "source-pool-id", true).Return(sourceSharedPool, nil)

				// Mock SharedPoolObjectStoreRepo.Load
				sourceObjectStore := &entity.SharedPoolObjectStore{
					SharedPoolObjectStore: schema.SharedPoolObjectStore{
						StoreBucket: "source-bucket",
					},
				}
				osRepo.EXPECT().Load(mock.Anything, "source-pool-id", schema.SharedPoolObjectStoreTypeData, true).Return(sourceObjectStore, nil)
			},
			expectedResult: &BucketPermissionContext{
				DataplaneID:          "source-dataplane",
				BucketID:             "source-bucket",
				PermittedDataplaneID: "target-dataplane",
			},
			expectedError: "",
		},
		{
			name:      "database error when loading lightweight restore",
			restoreID: "test-restore-id",
			setupMocks: func(lwRepo *mocks.LightweightRestoreRepo, spRepo *mocks.SharedPoolRepo, osRepo *mocks.SharedPoolObjectStoreRepo) {
				lwRepo.EXPECT().Load(mock.Anything, "test-restore-id", false).Return(nil, errors.New("database connection failed"))
			},
			expectedResult: nil,
			expectedError:  "failed to load lw restore: database connection failed",
		},
		{
			name:      "database error when loading target shared pool",
			restoreID: "test-restore-id",
			setupMocks: func(lwRepo *mocks.LightweightRestoreRepo, spRepo *mocks.SharedPoolRepo, osRepo *mocks.SharedPoolObjectStoreRepo) {
				restore := &entity.LightweightRestore{
					LightweightRestore: schema.LightweightRestore{
						SourceSharedPoolID: "source-pool-id",
						TargetSharedPoolID: "target-pool-id",
					},
				}
				lwRepo.EXPECT().Load(mock.Anything, "test-restore-id", false).Return(restore, nil)

				spRepo.EXPECT().Load(mock.Anything, "target-pool-id", false).Return(nil, errors.New("database connection failed"))
			},
			expectedResult: nil,
			expectedError:  "failed to load targetSharedPool: database connection failed",
		},
		{
			name:      "database error when loading source shared pool",
			restoreID: "test-restore-id",
			setupMocks: func(lwRepo *mocks.LightweightRestoreRepo, spRepo *mocks.SharedPoolRepo, osRepo *mocks.SharedPoolObjectStoreRepo) {
				restore := &entity.LightweightRestore{
					LightweightRestore: schema.LightweightRestore{
						SourceSharedPoolID: "source-pool-id",
						TargetSharedPoolID: "target-pool-id",
					},
				}
				lwRepo.EXPECT().Load(mock.Anything, "test-restore-id", false).Return(restore, nil)

				targetSharedPool := &entity.SharedPool{
					SharedPool: apiv1.SharedPool{
						Id:            "target-pool-id",
						DataplaneName: "target-dataplane",
					},
				}
				spRepo.EXPECT().Load(mock.Anything, "target-pool-id", false).Return(targetSharedPool, nil)

				spRepo.EXPECT().Load(mock.Anything, "source-pool-id", true).Return(nil, errors.New("database connection failed"))
			},
			expectedResult: nil,
			expectedError:  "failed to load sourceSharedPool: database connection failed",
		},
		{
			name:      "database error when loading source object store",
			restoreID: "test-restore-id",
			setupMocks: func(lwRepo *mocks.LightweightRestoreRepo, spRepo *mocks.SharedPoolRepo, osRepo *mocks.SharedPoolObjectStoreRepo) {
				restore := &entity.LightweightRestore{
					LightweightRestore: schema.LightweightRestore{
						SourceSharedPoolID: "source-pool-id",
						TargetSharedPoolID: "target-pool-id",
					},
				}
				lwRepo.EXPECT().Load(mock.Anything, "test-restore-id", false).Return(restore, nil)

				targetSharedPool := &entity.SharedPool{
					SharedPool: apiv1.SharedPool{
						Id:            "target-pool-id",
						DataplaneName: "target-dataplane",
					},
				}
				spRepo.EXPECT().Load(mock.Anything, "target-pool-id", false).Return(targetSharedPool, nil)

				sourceSharedPool := &entity.SharedPool{
					SharedPool: apiv1.SharedPool{
						Id:            "source-pool-id",
						DataplaneName: "source-dataplane",
					},
				}
				spRepo.EXPECT().Load(mock.Anything, "source-pool-id", true).Return(sourceSharedPool, nil)

				osRepo.EXPECT().Load(mock.Anything, "source-pool-id", schema.SharedPoolObjectStoreTypeData, true).Return(nil, errors.New("database connection failed"))
			},
			expectedResult: nil,
			expectedError:  "failed to load sourceSharedPoolObjectStore: database connection failed",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Create mocks
			lwRepo := mocks.NewLightweightRestoreRepo(t)
			spRepo := mocks.NewSharedPoolRepo(t)
			osRepo := mocks.NewSharedPoolObjectStoreRepo(t)

			// Setup mocks
			tt.setupMocks(lwRepo, spRepo, osRepo)

			// Call the function
			ctx := context.Background()
			result, err := getBucketPermissionContext(ctx, tt.restoreID, lwRepo, spRepo, osRepo)

			// Assert results
			if tt.expectedError != "" {
				require.Error(t, err)
				assert.Equal(t, tt.expectedError, err.Error())
				assert.Nil(t, result)
			} else {
				require.NoError(t, err)
				assert.NotNil(t, result)
				assert.Equal(t, tt.expectedResult.DataplaneID, result.DataplaneID)
				assert.Equal(t, tt.expectedResult.BucketID, result.BucketID)
				assert.Equal(t, tt.expectedResult.PermittedDataplaneID, result.PermittedDataplaneID)
			}
		})
	}
}

func TestGetBucketPermissionContext_EmptyDataplaneNames(t *testing.T) {
	// Test case where dataplane names are empty
	lwRepo := mocks.NewLightweightRestoreRepo(t)
	spRepo := mocks.NewSharedPoolRepo(t)
	osRepo := mocks.NewSharedPoolObjectStoreRepo(t)

	restore := &entity.LightweightRestore{
		LightweightRestore: schema.LightweightRestore{
			SourceSharedPoolID: "source-pool-id",
			TargetSharedPoolID: "target-pool-id",
		},
	}
	lwRepo.EXPECT().Load(mock.Anything, "test-restore-id", false).Return(restore, nil)

	targetSharedPool := &entity.SharedPool{
		SharedPool: apiv1.SharedPool{
			Id:            "target-pool-id",
			DataplaneName: "", // Empty dataplane name
		},
	}
	spRepo.EXPECT().Load(mock.Anything, "target-pool-id", false).Return(targetSharedPool, nil)

	sourceSharedPool := &entity.SharedPool{
		SharedPool: apiv1.SharedPool{
			Id:            "source-pool-id",
			DataplaneName: "", // Empty dataplane name
		},
	}
	spRepo.EXPECT().Load(mock.Anything, "source-pool-id", true).Return(sourceSharedPool, nil)

	sourceObjectStore := &entity.SharedPoolObjectStore{
		SharedPoolObjectStore: schema.SharedPoolObjectStore{
			StoreBucket: "source-bucket",
		},
	}
	osRepo.EXPECT().Load(mock.Anything, "source-pool-id", schema.SharedPoolObjectStoreTypeData, true).Return(sourceObjectStore, nil)

	ctx := context.Background()
	result, err := getBucketPermissionContext(ctx, "test-restore-id", lwRepo, spRepo, osRepo)

	require.NoError(t, err)
	assert.NotNil(t, result)
	assert.Empty(t, result.DataplaneID)
	assert.Equal(t, "source-bucket", result.BucketID)
	assert.Empty(t, result.PermittedDataplaneID)
}

func TestGetBucketPermissionContext_EmptyBucketID(t *testing.T) {
	// Test case where bucket ID is empty
	lwRepo := mocks.NewLightweightRestoreRepo(t)
	spRepo := mocks.NewSharedPoolRepo(t)
	osRepo := mocks.NewSharedPoolObjectStoreRepo(t)

	restore := &entity.LightweightRestore{
		LightweightRestore: schema.LightweightRestore{
			SourceSharedPoolID: "source-pool-id",
			TargetSharedPoolID: "target-pool-id",
		},
	}
	lwRepo.EXPECT().Load(mock.Anything, "test-restore-id", false).Return(restore, nil)

	targetSharedPool := &entity.SharedPool{
		SharedPool: apiv1.SharedPool{
			Id:            "target-pool-id",
			DataplaneName: "target-dataplane",
		},
	}
	spRepo.EXPECT().Load(mock.Anything, "target-pool-id", false).Return(targetSharedPool, nil)

	sourceSharedPool := &entity.SharedPool{
		SharedPool: apiv1.SharedPool{
			Id:            "source-pool-id",
			DataplaneName: "source-dataplane",
		},
	}
	spRepo.EXPECT().Load(mock.Anything, "source-pool-id", true).Return(sourceSharedPool, nil)

	sourceObjectStore := &entity.SharedPoolObjectStore{
		SharedPoolObjectStore: schema.SharedPoolObjectStore{
			StoreBucket: "", // Empty bucket ID
		},
	}
	osRepo.EXPECT().Load(mock.Anything, "source-pool-id", schema.SharedPoolObjectStoreTypeData, true).Return(sourceObjectStore, nil)

	ctx := context.Background()
	result, err := getBucketPermissionContext(ctx, "test-restore-id", lwRepo, spRepo, osRepo)

	require.NoError(t, err)
	assert.NotNil(t, result)
	assert.Equal(t, "source-dataplane", result.DataplaneID)
	assert.Empty(t, result.BucketID)
	assert.Equal(t, "target-dataplane", result.PermittedDataplaneID)
}

func TestParseToRFC3339(t *testing.T) {
	tests := []struct {
		name          string
		timestr       string
		expected      string
		expectedError string
	}{
		{
			name:          "successful case",
			timestr:       "2025-01-01 12:23:12.123",
			expected:      "2025-01-01T12:23:12Z",
			expectedError: "",
		},
		{
			name:          "successful case - already in RFC3339 format",
			timestr:       "2025-01-01T12:23:12.123Z",
			expected:      "2025-01-01T12:23:12.123Z",
			expectedError: "",
		},
		{
			name:          "invalid time string",
			timestr:       "2025-01-01DD12:00:00.000TT",
			expected:      "",
			expectedError: "failed to parse time",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			actual, err := ParseToRFC3339(tt.timestr)
			if tt.expectedError != "" {
				require.Error(t, err)
				assert.Contains(t, err.Error(), tt.expectedError)
			} else {
				require.NoError(t, err)
				assert.Equal(t, tt.expected, actual)
			}
		})
	}
}

func TestApplyAzureRestorePackedBackupSource(t *testing.T) {
	var bucketSvc *dpmocks.Client

	tests := []struct {
		name               string
		targetSharedPool   *entity.SharedPool
		sourceSharedPoolID string
		setupMocks         func(*mocks.SharedPoolRepo, *mocks.SharedPoolObjectStoreRepo)
		expectedEndpoint   string
		expectedErr        string
	}{
		{
			name:               "non azure target skips endpoint setup",
			targetSharedPool:   &entity.SharedPool{},
			sourceSharedPoolID: "source-pool",
			setupMocks:         func(spRepo *mocks.SharedPoolRepo, osRepo *mocks.SharedPoolObjectStoreRepo) {},
		},
		{
			name: "azure target uses source bucket even without source provider type",
			targetSharedPool: &entity.SharedPool{
				SharedPool: apiv1.SharedPool{
					Provider: &apiv1.Provider{
						Type: apiv1.ProviderType_AZURE,
					},
				},
			},
			sourceSharedPoolID: "source-pool",
			setupMocks: func(spRepo *mocks.SharedPoolRepo, osRepo *mocks.SharedPoolObjectStoreRepo) {
				spRepo.EXPECT().Load(mock.Anything, "source-pool", true).Return(&entity.SharedPool{
					SharedPool: apiv1.SharedPool{
						Id:            "source-pool",
						DataplaneName: "source-dataplane",
					},
				}, nil)
				osRepo.EXPECT().Load(mock.Anything, "source-pool", schema.SharedPoolObjectStoreTypeData, true).Return(&entity.SharedPoolObjectStore{
					SharedPoolObjectStore: schema.SharedPoolObjectStore{
						StoreBucket: "source-bucket-id",
					},
				}, nil)
				bucketSvc.EXPECT().GetBucket(mock.Anything, &dplanemgrv1.GetBucketRequest{
					DataplaneId: "source-dataplane",
					BucketId:    "source-bucket-id",
				}).Return(&dplanemgrv1.Bucket{
					Name:   "account-a/container-a",
					Region: "eastus",
				}, nil)
			},
			expectedEndpoint: "https://account-a.blob.core.windows.net",
		},
		{
			name: "azure target rejects invalid source bucket format",
			targetSharedPool: &entity.SharedPool{
				SharedPool: apiv1.SharedPool{
					Provider: &apiv1.Provider{
						Type: apiv1.ProviderType_AZURE,
					},
				},
			},
			sourceSharedPoolID: "source-pool",
			setupMocks: func(spRepo *mocks.SharedPoolRepo, osRepo *mocks.SharedPoolObjectStoreRepo) {
				spRepo.EXPECT().Load(mock.Anything, "source-pool", true).Return(&entity.SharedPool{
					SharedPool: apiv1.SharedPool{
						Id:            "source-pool",
						DataplaneName: "source-dataplane",
					},
				}, nil)
				osRepo.EXPECT().Load(mock.Anything, "source-pool", schema.SharedPoolObjectStoreTypeData, true).Return(&entity.SharedPoolObjectStore{
					SharedPoolObjectStore: schema.SharedPoolObjectStore{
						StoreBucket: "source-bucket-id",
					},
				}, nil)
				bucketSvc.EXPECT().GetBucket(mock.Anything, &dplanemgrv1.GetBucketRequest{
					DataplaneId: "source-dataplane",
					BucketId:    "source-bucket-id",
				}).Return(&dplanemgrv1.Bucket{
					Name:   "invalid-bucket",
					Region: "eastus",
				}, nil)
			},
			expectedErr: "failed to parse azure bucket format invalid-bucket of source shared pool source-pool",
		},
		{
			name: "azure target returns object store load error",
			targetSharedPool: &entity.SharedPool{
				SharedPool: apiv1.SharedPool{
					Provider: &apiv1.Provider{
						Type: apiv1.ProviderType_AZURE,
					},
				},
			},
			sourceSharedPoolID: "source-pool",
			setupMocks: func(spRepo *mocks.SharedPoolRepo, osRepo *mocks.SharedPoolObjectStoreRepo) {
				spRepo.EXPECT().Load(mock.Anything, "source-pool", true).Return(&entity.SharedPool{
					SharedPool: apiv1.SharedPool{
						Id:            "source-pool",
						DataplaneName: "source-dataplane",
					},
				}, nil)
				osRepo.EXPECT().Load(mock.Anything, "source-pool", schema.SharedPoolObjectStoreTypeData, true).Return(nil, errors.New("database connection failed"))
			},
			expectedErr: "failed to get source bucket info of shared pool source-pool",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			spRepo := mocks.NewSharedPoolRepo(t)
			osRepo := mocks.NewSharedPoolObjectStoreRepo(t)
			bucketSvc = dpmocks.NewClient(t)
			tt.setupMocks(spRepo, osRepo)

			req := &clustergateway.CreateRestorePackedBackupTaskRequest{}

			err := applyAzureRestorePackedBackupSource(context.Background(), tt.targetSharedPool, tt.sourceSharedPoolID, spRepo, osRepo, bucketSvc, req)

			if tt.expectedErr != "" {
				require.Error(t, err)
				assert.Contains(t, err.Error(), tt.expectedErr)
				return
			}

			require.NoError(t, err)
			assert.Equal(t, tt.expectedEndpoint, req.ExoticBackupEndpoint)
		})
	}
}

func TestResolveSourceBucketForTargetSharedPool(t *testing.T) {
	tests := []struct {
		name                 string
		targetSharedPool     *entity.SharedPool
		bucket               string
		expectedUseS3Bucket  string
		expectedExoticEP     string
		expectedErrorContain string
	}{
		{
			name:                "non azure target keeps source bucket",
			targetSharedPool:    &entity.SharedPool{},
			bucket:              "bucket-a",
			expectedUseS3Bucket: "bucket-a",
		},
		{
			name: "azure target splits account and container",
			targetSharedPool: &entity.SharedPool{
				SharedPool: apiv1.SharedPool{
					Provider: &apiv1.Provider{
						Type: apiv1.ProviderType_AZURE,
					},
				},
			},
			bucket:              "account-a/container-a",
			expectedUseS3Bucket: "container-a",
			expectedExoticEP:    "https://account-a.blob.core.windows.net",
		},
		{
			name: "azure target rejects invalid bucket format",
			targetSharedPool: &entity.SharedPool{
				SharedPool: apiv1.SharedPool{
					Provider: &apiv1.Provider{
						Type: apiv1.ProviderType_AZURE,
					},
				},
			},
			bucket:               "invalid-bucket",
			expectedErrorContain: "invalid azure bucket format",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			useS3Bucket, exoticEndpoint, err := resolveSourceBucketForTargetSharedPool(tt.targetSharedPool, tt.bucket)

			if tt.expectedErrorContain != "" {
				require.Error(t, err)
				assert.Contains(t, err.Error(), tt.expectedErrorContain)
				return
			}

			require.NoError(t, err)
			assert.Equal(t, tt.expectedUseS3Bucket, useS3Bucket)
			assert.Equal(t, tt.expectedExoticEP, exoticEndpoint)
		})
	}
}

func TestParseToMiB(t *testing.T) {
	tests := []struct {
		input       string
		expectedMiB uint64
		expectErr   bool
	}{
		{"1Gi", 1024, false},
		{"512Mi", 512, false},
		{"2G", 1907, false}, // 2 gigabytes (SI) = 2,000,000,000 bytes → ~1907MiB
		{"2048Mi", 2048, false},
		{"1Ti", 1048576, false}, // 1 TiB = 1024 * 1024 MiB
		{"", 0, true},
		{"abc", 0, true},
		{"100", 0, false}, // bytes by default → 100 / (1024*1024) = 0
	}

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			got, err := ParseToMiB(tt.input)
			if tt.expectErr {
				require.Error(t, err)
			} else {
				require.NoError(t, err)
				assert.Equal(t, tt.expectedMiB, got)
			}
		})
	}
}

func TestParseCPU(t *testing.T) {
	tests := []struct {
		name        string
		input       string
		expected    int64
		expectError bool
	}{
		{"500m", "500m", 500, false},
		{"1 core", "1", 1000, false},
		{"0.25 core", "0.25", 250, false},
		{"empty", "", 0, true},
		{"invalid string", "abc", 0, true},
		{"2 cores", "2", 2000, false},
		{"750m", "750m", 750, false},
		{"1.5 core", "1.5", 1500, false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			actual, err := ParseCPU(tt.input)
			if tt.expectError {
				require.Error(t, err)
			} else {
				require.NoError(t, err)
				assert.Equal(t, tt.expected, actual)
			}
		})
	}
}

func TestComputeAvailableCPU(t *testing.T) {
	tests := []struct {
		input    uint64
		expected uint64
	}{
		{0, 1},   // <= 1 returns 1
		{1, 1},   // <= 1 returns 1
		{2, 1},   // even > 1 returns coreNum - 1
		{3, 3},   // odd > 1 returns coreNum
		{4, 3},   // even > 1 returns coreNum - 1
		{5, 5},   // odd > 1 returns coreNum
		{10, 9},  // even > 1 returns coreNum - 1
		{11, 11}, // odd > 1 returns coreNum
	}

	for _, tt := range tests {
		t.Run(
			"input="+string(rune(tt.input)),
			func(t *testing.T) {
				got := ComputeAvailableCPU(tt.input)
				assert.Equal(t, tt.expected, got)
			},
		)
	}
}

func TestGetResourceStorageClass(t *testing.T) {
	tests := []struct {
		name           string
		tibrConfig     *config.TiBRConfig
		providerType   apiv1.ProviderType
		expectedResult string
	}{
		{
			name: "bizConfig with StorageClass set for AWS",
			tibrConfig: &config.TiBRConfig{
				StorageClass: "gp4",
			},
			providerType:   apiv1.ProviderType_AWS,
			expectedResult: "gp4",
		},
		{
			name: "bizConfig with StorageClass set for ALICLOUD",
			tibrConfig: &config.TiBRConfig{
				StorageClass: "gp4",
			},
			providerType:   apiv1.ProviderType_ALICLOUD,
			expectedResult: "gp4",
		},
		{
			name:           "bizConfig with empty StorageClass for AWS",
			tibrConfig:     &config.TiBRConfig{},
			providerType:   apiv1.ProviderType_AWS,
			expectedResult: DefaultStorageClass,
		},
		{
			name:           "bizConfig with empty StorageClass for ALICLOUD",
			tibrConfig:     &config.TiBRConfig{},
			providerType:   apiv1.ProviderType_ALICLOUD,
			expectedResult: DefaultStorageClass,
		},
		{
			name:           "nil bizConfig for AWS",
			tibrConfig:     nil,
			providerType:   apiv1.ProviderType_AWS,
			expectedResult: DefaultStorageClass,
		},
		{
			name:           "nil bizConfig for ALICLOUD",
			tibrConfig:     nil,
			providerType:   apiv1.ProviderType_ALICLOUD,
			expectedResult: DefaultStorageClass,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := GetStorageClass(tt.tibrConfig, tt.providerType)
			assert.Equal(t, tt.expectedResult, result)
		})
	}
}

func Test_genOptionsStr(t *testing.T) {
	tests := []struct {
		name     string
		options  map[string]string
		expected string
	}{
		{
			name:     "empty options",
			options:  map[string]string{},
			expected: `[]`,
		},
		{
			name:     "single option",
			options:  map[string]string{"keyspace-name": "test-123"},
			expected: `["--keyspace-name=test-123"]`,
		},
		{
			name: "multiple options",
			options: map[string]string{
				"keyspace-name":  "test-123",
				"with-sys-table": "true",
			},
			expected: `["--keyspace-name=test-123", "--with-sys-table=true"]`,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			resultStr := genOptionsStr(tt.options)
			assert.Equal(t, tt.expected, resultStr)
		})
	}
}
