/*
Copyright The Velero Contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package util

import (
	"reflect"
	"testing"

	"github.com/pkg/errors"

	velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
)

func TestStoreBackupConfig(t *testing.T) {
	config := &velerov1api.UploaderConfigForBackup{
		ParallelFilesUpload: 3,
	}

	expectedData := map[string]string{
		ParallelFilesUpload: "3",
	}

	result := StoreBackupConfig(config)

	if !reflect.DeepEqual(result, expectedData) {
		t.Errorf("Expected: %v, but got: %v", expectedData, result)
	}
}

func TestStoreRestoreConfig(t *testing.T) {
	var (
		boolTrue  = true
		boolFalse = false
	)
	testCases := []struct {
		name         string
		config       *velerov1api.UploaderConfigForRestore
		expectedData map[string]string
	}{
		{
			name: "WriteSparseFiles is true",
			config: &velerov1api.UploaderConfigForRestore{
				WriteSparseFiles: &boolTrue,
			},
			expectedData: map[string]string{
				WriteSparseFiles: "true",
			},
		},
		{
			name: "WriteSparseFiles is false",
			config: &velerov1api.UploaderConfigForRestore{
				WriteSparseFiles: &boolFalse,
			},
			expectedData: map[string]string{
				WriteSparseFiles: "false",
			},
		},
		{
			name: "WriteSparseFiles is nil",
			config: &velerov1api.UploaderConfigForRestore{
				WriteSparseFiles: nil,
			},
			expectedData: map[string]string{
				WriteSparseFiles: "false", // Assuming default value is false for nil case
			},
		},
		{
			name: "Parallel is set",
			config: &velerov1api.UploaderConfigForRestore{
				ParallelFilesDownload: 5,
			},
			expectedData: map[string]string{
				RestoreConcurrency: "5",
				WriteSparseFiles:   "false",
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			result := StoreRestoreConfig(tc.config)

			if !reflect.DeepEqual(result, tc.expectedData) {
				t.Errorf("Expected: %v, but got: %v", tc.expectedData, result)
			}
		})
	}
}

func TestGetParallelFilesUpload(t *testing.T) {
	tests := []struct {
		name           string
		uploaderCfg    map[string]string
		expectedResult int
		expectedError  error
	}{
		{
			name:           "Valid ParallelFilesUpload",
			uploaderCfg:    map[string]string{ParallelFilesUpload: "5"},
			expectedResult: 5,
			expectedError:  nil,
		},
		{
			name:           "Missing ParallelFilesUpload",
			uploaderCfg:    map[string]string{},
			expectedResult: 0,
			expectedError:  nil,
		},
		{
			name:           "Invalid ParallelFilesUpload (not a number)",
			uploaderCfg:    map[string]string{ParallelFilesUpload: "invalid"},
			expectedResult: 0,
			expectedError:  errors.Wrap(errors.New("strconv.Atoi: parsing \"invalid\": invalid syntax"), "failed to parse ParallelFilesUpload config"),
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			result, err := GetParallelFilesUpload(test.uploaderCfg)

			if result != test.expectedResult {
				t.Errorf("Expected result %d, but got %d", test.expectedResult, result)
			}

			if (err == nil && test.expectedError != nil) || (err != nil && test.expectedError == nil) || (err != nil && test.expectedError != nil && err.Error() != test.expectedError.Error()) {
				t.Errorf("Expected error '%v', but got '%v'", test.expectedError, err)
			}
		})
	}
}

func TestGetWriteSparseFiles(t *testing.T) {
	tests := []struct {
		name           string
		uploaderCfg    map[string]string
		expectedResult bool
		expectedError  error
	}{
		{
			name:           "Valid WriteSparseFiles (true)",
			uploaderCfg:    map[string]string{WriteSparseFiles: "true"},
			expectedResult: true,
			expectedError:  nil,
		},
		{
			name:           "Valid WriteSparseFiles (false)",
			uploaderCfg:    map[string]string{WriteSparseFiles: "false"},
			expectedResult: false,
			expectedError:  nil,
		},
		{
			name:           "Invalid WriteSparseFiles (not a boolean)",
			uploaderCfg:    map[string]string{WriteSparseFiles: "invalid"},
			expectedResult: false,
			expectedError:  errors.Wrap(errors.New("strconv.ParseBool: parsing \"invalid\": invalid syntax"), "failed to parse WriteSparseFiles config"),
		},
		{
			name:           "Missing WriteSparseFiles",
			uploaderCfg:    map[string]string{},
			expectedResult: false,
			expectedError:  nil,
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			result, err := GetWriteSparseFiles(test.uploaderCfg)

			if result != test.expectedResult {
				t.Errorf("Expected result %t, but got %t", test.expectedResult, result)
			}

			if (err == nil && test.expectedError != nil) || (err != nil && test.expectedError == nil) || (err != nil && test.expectedError != nil && err.Error() != test.expectedError.Error()) {
				t.Errorf("Expected error '%v', but got '%v'", test.expectedError, err)
			}
		})
	}
}

func TestGetRestoreConcurrency(t *testing.T) {
	testCases := []struct {
		Name             string
		UploaderCfg      map[string]string
		ExpectedResult   int
		ExpectedError    bool
		ExpectedErrorMsg string
	}{
		{
			Name:           "Valid Configuration",
			UploaderCfg:    map[string]string{RestoreConcurrency: "10"},
			ExpectedResult: 10,
			ExpectedError:  false,
		},
		{
			Name:           "Missing Configuration",
			UploaderCfg:    map[string]string{},
			ExpectedResult: 0,
			ExpectedError:  false,
		},
		{
			Name:             "Invalid Configuration",
			UploaderCfg:      map[string]string{RestoreConcurrency: "not_an_integer"},
			ExpectedResult:   0,
			ExpectedError:    true,
			ExpectedErrorMsg: "failed to parse RestoreConcurrency config: strconv.Atoi: parsing \"not_an_integer\": invalid syntax",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.Name, func(t *testing.T) {
			result, err := GetRestoreConcurrency(tc.UploaderCfg)

			if tc.ExpectedError {
				if err.Error() != tc.ExpectedErrorMsg {
					t.Errorf("Expected error message %s, but got %s", tc.ExpectedErrorMsg, err.Error())
				}
			} else {
				if err != nil {
					t.Errorf("Expected no error, but got %v", err)
				}
			}

			if result != tc.ExpectedResult {
				t.Errorf("Expected result %d, but got %d", tc.ExpectedResult, result)
			}
		})
	}
}
