package huaweidrive

import (
	"context"
	"errors"
	"fmt"
	"mime"
	"net/http"
	"path"
	"slices"
	"testing"
	"time"

	"github.com/rclone/rclone/backend/huaweidrive/api"
	"github.com/rclone/rclone/fs"
	"github.com/rclone/rclone/fs/config/configmap"
	"github.com/rclone/rclone/fs/hash"
	"github.com/rclone/rclone/fstest/fstests"
	"github.com/rclone/rclone/lib/encoder"
)

// TestIntegration runs integration tests against the remote
func TestIntegration(t *testing.T) {
	fstests.Run(t, &fstests.Opt{
		RemoteName:               "TestHuaweiDrive:",
		NilObject:                (*Object)(nil),
		SkipBadWindowsCharacters: true,
	})
}

// TestNewFs tests the NewFs constructor
func TestNewFs(t *testing.T) {
	ctx := context.Background()

	// Test with empty config - should fail due to missing OAuth credentials
	m := configmap.Simple{}
	_, err := NewFs(ctx, "test", "", m)
	if err == nil {
		t.Fatal("expected error with empty config")
	}
}

// TestFsName tests the filesystem name
func TestFsName(t *testing.T) {
	f := &Fs{name: "test"}
	if f.Name() != "test" {
		t.Errorf("expected name 'test', got %q", f.Name())
	}
}

// TestFsRoot tests the filesystem root
func TestFsRoot(t *testing.T) {
	f := &Fs{root: "test/path"}
	if f.Root() != "test/path" {
		t.Errorf("expected root 'test/path', got %q", f.Root())
	}
}

// TestFsString tests the filesystem string representation
func TestFsString(t *testing.T) {
	f := &Fs{root: "test/path"}
	expected := "Huawei Drive root 'test/path'"
	if f.String() != expected {
		t.Errorf("expected string %q, got %q", expected, f.String())
	}
}

// TestFsPrecision tests the filesystem precision
func TestFsPrecision(t *testing.T) {
	f := &Fs{}
	precision := f.Precision()
	expectedPrecision := fs.ModTimeNotSupported
	if precision != expectedPrecision {
		t.Errorf("expected precision %v (ModTimeNotSupported), got %v", expectedPrecision, precision)
	}
}

// TestFsHashes tests the supported hash types
func TestFsHashes(t *testing.T) {
	f := &Fs{}
	hashes := f.Hashes()
	if !hashes.Contains(hash.SHA256) {
		t.Error("expected SHA256 hash support")
	}
}

// TestModTimeSupport tests if the filesystem supports modification time preservation
func TestModTimeSupport(t *testing.T) {
	f := &Fs{}
	// Huawei Drive does NOT preserve original modification times
	// Despite the API accepting editedTime/createdTime parameters,
	// the server always overwrites them with current server timestamp
	expectedPrecision := fs.ModTimeNotSupported
	if f.Precision() != expectedPrecision {
		t.Errorf("expected precision of %v to indicate no time preservation, got %v", expectedPrecision, f.Precision())
	}
}

// TestTimeFormats tests time format handling for RFC 3339
func TestTimeFormats(t *testing.T) {
	// Test that we can format time correctly for Huawei Drive API
	testTime := time.Date(2023, 10, 15, 14, 30, 45, 0, time.UTC)
	formatted := testTime.Format(time.RFC3339)
	expected := "2023-10-15T14:30:45Z"

	if formatted != expected {
		t.Errorf("expected RFC3339 format %q, got %q", expected, formatted)
	}

	// Test parsing back
	parsed, err := time.Parse(time.RFC3339, formatted)
	if err != nil {
		t.Errorf("failed to parse RFC3339 time: %v", err)
	}

	if !parsed.Equal(testTime) {
		t.Errorf("parsed time %v does not match original %v", parsed, testTime)
	}
}

// TestEncoding tests filename encoding for Huawei Drive restrictions
func TestEncoding(t *testing.T) {
	// Create encoder with Huawei Drive restrictions
	enc := encoder.MultiEncoder( //nolint:unconvert
		encoder.Display |
			encoder.EncodeBackSlash |
			encoder.EncodeSlash |
			encoder.EncodeInvalidUtf8 |
			encoder.EncodeRightSpace |
			encoder.EncodeLeftSpace |
			encoder.EncodeLeftTilde |
			encoder.EncodeRightPeriod |
			encoder.EncodeLeftPeriod |
			encoder.EncodeColon |
			encoder.EncodePipe |
			encoder.EncodeDoubleQuote |
			encoder.EncodeLtGt |
			encoder.EncodeQuestion |
			encoder.EncodeAsterisk |
			encoder.EncodeCtl |
			encoder.EncodeDot)

	// Test cases for problematic characters that should be encoded
	testCases := []struct {
		name     string
		input    string
		expected string
	}{
		{"angle_brackets", "file<>name", "file＜＞name"},
		{"quotes", "file\"name", "file＂name"},
		{"pipe", "file|name", "file｜name"},
		{"colon", "file:name", "file：name"},
		{"asterisk", "file*name", "file＊name"},
		{"question", "file?name", "file？name"},
		{"backslash", "file\\name", "file＼name"},
		{"forward_slash", "file/name", "file／name"},
		{"leading_space", " filename", "␠filename"},
		{"trailing_space", "filename ", "filename␠"},
		{"leading_dot", ".filename", "．filename"},
		{"control_chars", "file\tname\ntest", "file␉name␊test"},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			encoded := enc.FromStandardName(tc.input)
			if encoded != tc.expected {
				t.Errorf("encoding %q: expected %q, got %q", tc.input, tc.expected, encoded)
			}

			// Test decoding back - only for reversible encodings
			// Some characters like forward slash and control chars are one-way encoded
			decoded := enc.ToStandardName(encoded)
			if tc.name != "forward_slash" && tc.name != "control_chars" {
				if decoded != tc.input {
					t.Errorf("decoding %q: expected %q, got %q", encoded, tc.input, decoded)
				}
			}
		})
	}
}

// TestFileNameEncoding tests that problematic characters are properly encoded
func TestFileNameEncoding(t *testing.T) {
	// Create a mock Fs with default encoding options
	opts := Options{}
	// Set the default encoding from our config
	opts.Enc = (encoder.Display |
		encoder.EncodeBackSlash |
		encoder.EncodeInvalidUtf8 |
		encoder.EncodeRightSpace |
		encoder.EncodeLeftSpace |
		encoder.EncodeLeftTilde |
		encoder.EncodeRightPeriod |
		encoder.EncodeLeftPeriod |
		encoder.EncodeColon |
		encoder.EncodePipe |
		encoder.EncodeDoubleQuote |
		encoder.EncodeLtGt |
		encoder.EncodeQuestion |
		encoder.EncodeAsterisk |
		encoder.EncodeCtl |
		encoder.EncodeDot)

	f := &Fs{opt: opts}

	// Test problematic characters that Huawei Drive rejects
	testCases := []struct {
		input string
		desc  string
	}{
		{`file<name>.txt`, "angle brackets"},
		{`file|name.txt`, "pipe character"},
		{`file:name.txt`, "colon"},
		{`file"name.txt`, "double quote"},
		{`file*name.txt`, "asterisk"},
		{`file?name.txt`, "question mark"},
		{`file\name.txt`, "backslash"},
		{` leading_space.txt`, "leading space"},
		{`trailing_space.txt `, "trailing space"},
		{`.leading_dot.txt`, "leading dot"},
		{`trailing_dot.txt.`, "trailing dot"},
		{`~leading_tilde.txt`, "leading tilde"},
		{"file\x00name.txt", "control character"},
	}

	for _, tc := range testCases {
		encoded := f.opt.Enc.FromStandardName(tc.input)
		// The encoded name should be different from input (meaning it was encoded)
		if encoded == tc.input {
			t.Errorf("Expected %s (%q) to be encoded, but got same string", tc.desc, tc.input)
		}

		// Test that we can decode it back (skip control characters as they are not reversible)
		decoded := f.opt.Enc.ToStandardName(encoded)
		if tc.desc != "control character" && decoded != tc.input {
			t.Errorf("Round-trip failed for %s: input=%q, encoded=%q, decoded=%q", tc.desc, tc.input, encoded, decoded)
		}

		t.Logf("✓ %s: %q → %q → %q", tc.desc, tc.input, encoded, decoded)
	}
}

// TestQueryFilter tests the QueryFilter functionality
func TestQueryFilter(t *testing.T) {
	t.Run("empty_filter", func(t *testing.T) {
		filter := NewQueryFilter()
		if filter.String() != "" {
			t.Errorf("expected empty string, got %q", filter.String())
		}
	})

	t.Run("single_condition", func(t *testing.T) {
		filter := NewQueryFilter()
		filter.AddParentFolder("test_parent_id")
		expected := "'test_parent_id' in parentFolder"
		if filter.String() != expected {
			t.Errorf("expected %q, got %q", expected, filter.String())
		}
	})

	t.Run("multiple_conditions", func(t *testing.T) {
		filter := NewQueryFilter()
		filter.AddParentFolder("parent_id")
		filter.AddMimeType("image/jpeg")
		filter.AddFavorite(true)
		expected := "'parent_id' in parentFolder and mimeType = 'image/jpeg' and favorite = true"
		if filter.String() != expected {
			t.Errorf("expected %q, got %q", expected, filter.String())
		}
	})

	t.Run("filename_with_quotes", func(t *testing.T) {
		filter := NewQueryFilter()
		filter.AddFileName("file'with'quotes.txt")
		expected := "fileName = 'file\\'with\\'quotes.txt'"
		if filter.String() != expected {
			t.Errorf("expected %q, got %q", expected, filter.String())
		}
	})

	t.Run("mime_type_not_equal", func(t *testing.T) {
		filter := NewQueryFilter()
		filter.AddMimeTypeNot("application/vnd.huawei-apps.folder")
		expected := "mimeType != 'application/vnd.huawei-apps.folder'"
		if filter.String() != expected {
			t.Errorf("expected %q, got %q", expected, filter.String())
		}
	})

	t.Run("filename_contains", func(t *testing.T) {
		filter := NewQueryFilter()
		filter.AddFileNameContains("test")
		expected := "fileName contains 'test'"
		if filter.String() != expected {
			t.Errorf("expected %q, got %q", expected, filter.String())
		}
	})

	t.Run("recycled_status", func(t *testing.T) {
		filter := NewQueryFilter()
		filter.AddRecycled(true)
		filter.AddDirectlyRecycled(false)
		expected := "recycled = true and directlyRecycled = false"
		if filter.String() != expected {
			t.Errorf("expected %q, got %q", expected, filter.String())
		}
	})

	t.Run("edited_time_range", func(t *testing.T) {
		filter := NewQueryFilter()
		testTime := time.Date(2023, 10, 15, 14, 30, 45, 0, time.UTC)
		filter.AddEditedTimeRange(">=", testTime)
		expected := "editedTime >= '2023-10-15T14:30:45Z'"
		if filter.String() != expected {
			t.Errorf("expected %q, got %q", expected, filter.String())
		}
	})
}

// TestAPITypes tests the API type structures and methods
func TestAPITypes(t *testing.T) {
	t.Run("file_is_dir", func(t *testing.T) {
		// Test folder
		folder := api.File{MimeType: api.FolderMimeType}
		if !folder.IsDir() {
			t.Error("expected folder to be recognized as directory")
		}

		// Test regular file
		file := api.File{MimeType: "text/plain"}
		if file.IsDir() {
			t.Error("expected regular file to not be recognized as directory")
		}
	})

	t.Run("constants", func(t *testing.T) {
		if api.FolderMimeType != "application/vnd.huawei-apps.folder" {
			t.Errorf("unexpected folder mime type: %q", api.FolderMimeType)
		}

		if api.CategoryDriveFile != "drive#file" {
			t.Errorf("unexpected drive file category: %q", api.CategoryDriveFile)
		}

		if api.CategoryDriveFileList != "drive#fileList" {
			t.Errorf("unexpected drive file list category: %q", api.CategoryDriveFileList)
		}

		if api.UploadTypeMultipart != "multipart" {
			t.Errorf("unexpected multipart upload type: %q", api.UploadTypeMultipart)
		}
	})
}

// TestErrorHandling tests error handling scenarios
func TestErrorHandling(t *testing.T) {
	ctx := context.Background()

	testCases := []struct {
		name       string
		statusCode int
		errorBody  string
		expectErr  bool
		errType    string
	}{
		{"missing_param_400", 400, "21004001", true, "missing required parameters"},
		{"invalid_param_400", 400, "PARAM_INVALID", true, "invalid parameters"},
		{"parent_not_found_400", 400, "PARENTFOLDER_NOT_FOUND", true, "ErrorDirNotFound"},
		{"insufficient_scope_403", 403, "INSUFFICIENT_SCOPE", true, "insufficient OAuth scope"},
		{"insufficient_permission_403", 403, "INSUFFICIENT_PERMISSION", true, "insufficient permissions"},
		{"agreement_not_signed_403", 403, "AGREEMENT_NOT_SIGNED", true, "user agreement not signed"},
		{"data_migrating_403", 403, "DATA_MIGRATING", false, ""},           // Should allow retry
		{"outer_service_error_403", 403, "OUTER_SERVICE_ERROR", false, ""}, // Should allow retry
		{"cursor_expired_410", 410, "CURSOR_EXPIRED", true, "cursor expired"},
		{"temp_data_cleared_410", 410, "TEMP_DATA_CLEARED", true, "temporary data cleared"},
		{"server_temp_error_500", 500, "SERVER_TEMP_ERROR", false, ""},                 // Should allow retry
		{"outer_service_unavailable_500", 500, "OUTER_SERVICE_UNAVAILABLE", false, ""}, // Should allow retry
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			// Mock HTTP response
			resp := &http.Response{
				StatusCode: tc.statusCode,
			}

			// Mock error with specific error code
			err := errors.New(tc.errorBody)

			shouldRetryResult, resultErr := shouldRetry(ctx, resp, err)

			if tc.expectErr {
				// Should not retry and should return error
				if shouldRetryResult {
					t.Error("expected shouldRetry to return false for non-retryable error")
				}
				if resultErr == nil {
					t.Error("expected error to be returned")
				}
			} else if !shouldRetryResult {
				// Should retry (temporary error)
				t.Error("expected shouldRetry to return true for retryable error")
			}
		})
	}
}

// TestMimeTypeDetection tests MIME type detection for various file types
func TestMimeTypeDetection(t *testing.T) {
	testCases := []struct {
		filename      string
		expectedMimes []string // Allow multiple valid MIME types for cross-platform compatibility
	}{
		{"document.pdf", []string{"application/pdf"}},
		{"image.jpg", []string{"image/jpeg"}},
		{"image.jpeg", []string{"image/jpeg"}},
		{"image.png", []string{"image/png"}},
		{"image.gif", []string{"image/gif"}},
		{"video.mp4", []string{"video/mp4"}},
		{"video.avi", []string{"video/x-msvideo", "video/vnd.avi", "video/avi"}}, // Different systems may return different values
		{"audio.mp3", []string{"audio/mpeg"}},
		{"text.txt", []string{"text/plain; charset=utf-8"}},
		{"data.json", []string{"application/json"}},
		{"archive.zip", []string{"application/zip", "application/x-zip-compressed"}}, // Different systems may return different values
		{"noextension", []string{"application/octet-stream"}},
		{"", []string{"application/octet-stream"}},
	}

	for _, tc := range testCases {
		t.Run(tc.filename, func(t *testing.T) {
			detected := mime.TypeByExtension(path.Ext(tc.filename))
			if detected == "" {
				detected = "application/octet-stream"
			}

			// Check if detected MIME type is in the list of expected types
			if !slices.Contains(tc.expectedMimes, detected) {
				t.Errorf("expected one of MIME types %v for %q, got %q", tc.expectedMimes, tc.filename, detected)
			}
		})
	}
}

// TestRootFolderID tests root folder ID handling
func TestRootFolderID(t *testing.T) {
	f := &Fs{}
	// Default root folder ID should be empty until detected
	if f.rootFolderID != "" {
		t.Errorf("expected empty root folder ID, got %q", f.rootFolderID)
	}

	// After setting, it should be accessible
	f.rootFolderID = "test-root-id"
	if f.rootFolderID != "test-root-id" {
		t.Errorf("expected root folder ID %q, got %q", "test-root-id", f.rootFolderID)
	}
}

// TestOptionsDefaults tests default option values
func TestOptionsDefaults(t *testing.T) {
	opts := Options{}

	// Check default values match what we expect
	if opts.ChunkSize != 0 {
		t.Errorf("expected default ChunkSize to be 0, got %v", opts.ChunkSize)
	}

	if opts.ListChunk != 0 {
		t.Errorf("expected default ListChunk to be 0, got %v", opts.ListChunk)
	}

	if opts.UploadCutoff != 0 {
		t.Errorf("expected default UploadCutoff to be 0, got %v", opts.UploadCutoff)
	}
}

// TestParsePath tests path parsing functionality
func TestParsePath(t *testing.T) {
	testCases := []struct {
		input    string
		expected string
	}{
		{"", ""},
		{"/", ""},
		{"path", "path"},
		{"/path", "path"},
		{"path/", "path"},
		{"/path/", "path"},
		{"path/to/file", "path/to/file"},
		{"/path/to/file", "path/to/file"},
		{"path/to/file/", "path/to/file"},
		{"/path/to/file/", "path/to/file"},
		{"//multiple//slashes//", "multiple//slashes"},
	}

	for _, tc := range testCases {
		t.Run(fmt.Sprintf("input_%q", tc.input), func(t *testing.T) {
			result := parsePath(tc.input)
			if result != tc.expected {
				t.Errorf("parsePath(%q): expected %q, got %q", tc.input, tc.expected, result)
			}
		})
	}
}

// TestConstants tests various constants and configuration values
func TestConstants(t *testing.T) {
	// Test OAuth configuration
	if rcloneClientID != "115505059" {
		t.Errorf("expected client ID %q, got %q", "115505059", rcloneClientID)
	}

	// Test URLs
	if rootURL != "https://driveapis.cloud.huawei.com.cn/drive/v1" {
		t.Errorf("expected root URL %q, got %q", "https://driveapis.cloud.huawei.com.cn/drive/v1", rootURL)
	}

	if uploadURL != "https://driveapis.cloud.huawei.com.cn/upload/drive/v1" {
		t.Errorf("expected upload URL %q, got %q", "https://driveapis.cloud.huawei.com.cn/upload/drive/v1", uploadURL)
	}

	// Test size limits
	expectedChunkSize := 8 * fs.Mebi // 8MB
	if defaultChunkSize != expectedChunkSize {
		t.Errorf("expected default chunk size %d, got %d", expectedChunkSize, defaultChunkSize)
	}
}

// TestFeatures tests the filesystem features configuration
func TestFeatures(t *testing.T) {
	ctx := context.Background()
	f := &Fs{}

	// Create features manually since we can't call NewFs without proper config
	features := (&fs.Features{
		CaseInsensitive:         false,
		DuplicateFiles:          false,
		ReadMimeType:            true,
		WriteMimeType:           true,
		CanHaveEmptyDirectories: true,
		BucketBased:             false,
		ReadMetadata:            true,
		WriteMetadata:           true,
		UserMetadata:            true,
		ReadDirMetadata:         false,
		WriteDirMetadata:        false,
		PartialUploads:          false,
		NoMultiThreading:        false,
		SlowModTime:             false,
		SlowHash:                false,
	}).Fill(ctx, f)

	f.features = features

	// Test that features are configured correctly for Huawei Drive
	if f.features.CaseInsensitive {
		t.Error("Huawei Drive should be case sensitive")
	}

	if f.features.DuplicateFiles {
		t.Error("Huawei Drive should not allow duplicate files")
	}

	if !f.features.ReadMimeType {
		t.Error("Huawei Drive should support reading MIME types")
	}

	if !f.features.WriteMimeType {
		t.Error("Huawei Drive should support setting MIME types")
	}

	if !f.features.CanHaveEmptyDirectories {
		t.Error("Huawei Drive should support empty directories")
	}

	if f.features.BucketBased {
		t.Error("Huawei Drive is not bucket-based")
	}

	if !f.features.ReadMetadata {
		t.Error("Huawei Drive should support reading metadata")
	}

	if !f.features.WriteMetadata {
		t.Error("Huawei Drive should support writing metadata")
	}
}

// TestAPIResponseParsing tests parsing of API responses
func TestAPIResponseParsing(t *testing.T) {
	// Test About response parsing
	t.Run("about_response", func(t *testing.T) {
		about := api.About{
			StorageQuota: struct {
				UsedSpace    string `json:"usedSpace"`
				UserCapacity string `json:"userCapacity"`
			}{
				UsedSpace:    "1073741824",  // 1GB
				UserCapacity: "10737418240", // 10GB
			},
			MaxThumbnailSize:  2097152,       // 2MB
			MaxFileUploadSize: "53687091200", // 50GB
			Domain:            "driveapis.cloud.huawei.com.cn",
			Category:          "drive#about",
			User: api.User{
				PermissionID: "test_permission_id",
				DisplayName:  "Test User",
				Me:           true,
				Category:     "drive#user",
			},
		}

		// Test parsing
		if about.StorageQuota.UsedSpace != "1073741824" {
			t.Errorf("expected used space %q, got %q", "1073741824", about.StorageQuota.UsedSpace)
		}
		if about.User.Me != true {
			t.Error("expected user.me to be true")
		}
		if about.Category != api.CategoryDriveAbout {
			t.Errorf("expected category %q, got %q", api.CategoryDriveAbout, about.Category)
		}
		if about.MaxThumbnailSize != 2097152 {
			t.Errorf("expected max thumbnail size 2097152, got %d", about.MaxThumbnailSize)
		}
		if about.MaxFileUploadSize != "53687091200" {
			t.Errorf("expected max file upload size %q, got %q", "53687091200", about.MaxFileUploadSize)
		}
		if about.Domain != "driveapis.cloud.huawei.com.cn" {
			t.Errorf("expected domain %q, got %q", "driveapis.cloud.huawei.com.cn", about.Domain)
		}
	})

	// Test FileList response parsing
	t.Run("filelist_response", func(t *testing.T) {
		fileList := api.FileList{
			Files: []api.File{
				{
					ID:           "test_file_id",
					FileName:     "test_file.txt",
					MimeType:     "text/plain",
					Category:     api.CategoryDriveFile,
					Size:         1024,
					SHA256:       "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
					CreatedTime:  time.Now(),
					EditedTime:   time.Now(),
					ParentFolder: []string{"parent_folder_id"},
					Owners: []api.User{
						{
							PermissionID: "owner_permission_id",
							DisplayName:  "Owner User",
							Me:           true,
							Category:     "drive#user",
						},
					},
					OwnedByMe:  true,
					EditedByMe: true,
					Recycled:   false,
					Favorite:   false,
					Containers: []string{"drive"},
				},
			},
			NextPageToken: "next_page_token",
			Category:      api.CategoryDriveFileList,
		}

		// Test file list properties
		if fileList.NextPageToken != "next_page_token" {
			t.Errorf("expected next page token %q, got %q", "next_page_token", fileList.NextPageToken)
		}
		if fileList.Category != api.CategoryDriveFileList {
			t.Errorf("expected category %q, got %q", api.CategoryDriveFileList, fileList.Category)
		}

		// Test file properties
		if len(fileList.Files) != 1 {
			t.Errorf("expected 1 file, got %d", len(fileList.Files))
		}

		file := fileList.Files[0]
		if file.IsDir() {
			t.Error("text file should not be recognized as directory")
		}
		if file.Size != 1024 {
			t.Errorf("expected file size 1024, got %d", file.Size)
		}
		if !file.OwnedByMe {
			t.Error("expected file to be owned by me")
		}
	})
}

// TestObjectMetadata tests object metadata operations
func TestObjectMetadata(t *testing.T) {
	// Create a mock object with metadata
	obj := &Object{
		fs:          &Fs{},
		remote:      "test/file.txt",
		hasMetaData: true,
		size:        1024,
		modTime:     time.Now(),
		id:          "test_object_id",
		sha256:      "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
		mimeType:    "text/plain",
	}

	// Test basic object properties
	if obj.Remote() != "test/file.txt" {
		t.Errorf("expected remote %q, got %q", "test/file.txt", obj.Remote())
	}

	if obj.Size() != 1024 {
		t.Errorf("expected size 1024, got %d", obj.Size())
	}

	// Test hash support
	ctx := context.Background()
	hash, err := obj.Hash(ctx, hash.SHA256)
	if err != nil {
		t.Errorf("unexpected error getting SHA256: %v", err)
	}
	if hash != obj.sha256 {
		t.Errorf("expected hash %q, got %q", obj.sha256, hash)
	}

	// Test unsupported hash (only SHA256 is supported by Huawei Drive)
	// We'll skip this test since it requires specific hash type constants

	// Test storable
	if !obj.Storable() {
		t.Error("object should be storable")
	}
}

// TestUploadTypes tests different upload type scenarios
func TestUploadTypes(t *testing.T) {
	testCases := []struct {
		name       string
		fileSize   int64
		cutoff     fs.SizeSuffix
		expectType string
	}{
		{"small_file_multipart", 1024, 20 * fs.Mebi, "multipart"},
		{"large_file_resume", int64(100 * fs.Mebi), 20 * fs.Mebi, "resume"},
		{"exact_cutoff_resume", int64(20 * fs.Mebi), 20 * fs.Mebi, "resume"},
		{"zero_size_metadata", 0, 20 * fs.Mebi, "metadata"},
		{"negative_size_resume", -1, 20 * fs.Mebi, "resume"}, // Unknown size
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			var actualType string
			if tc.fileSize == 0 {
				actualType = "metadata"
			} else if tc.fileSize > 0 && tc.fileSize < int64(tc.cutoff) {
				actualType = "multipart"
			} else {
				actualType = "resume"
			}

			if actualType != tc.expectType {
				t.Errorf("expected upload type %q for size %d with cutoff %d, got %q",
					tc.expectType, tc.fileSize, tc.cutoff, actualType)
			}
		})
	}
}

// TestRetryErrorCodes tests the retry error codes list
func TestRetryErrorCodes(t *testing.T) {
	expectedCodes := []int{429, 500, 502, 503, 504, 509}

	if len(retryErrorCodes) != len(expectedCodes) {
		t.Errorf("expected %d retry error codes, got %d", len(expectedCodes), len(retryErrorCodes))
	}

	for i, expected := range expectedCodes {
		if i >= len(retryErrorCodes) || retryErrorCodes[i] != expected {
			t.Errorf("expected retry error code %d at position %d, got %d", expected, i, retryErrorCodes[i])
		}
	}
}

// TestEncodingConfiguration tests the encoding configuration matches requirements
func TestEncodingConfiguration(t *testing.T) {
	// This should match the default encoding set in the config
	expectedEncoding := (encoder.Display |
		encoder.EncodeBackSlash |
		encoder.EncodeSlash |
		encoder.EncodeInvalidUtf8 |
		encoder.EncodeRightSpace |
		encoder.EncodeLeftSpace |
		encoder.EncodeLeftTilde |
		encoder.EncodeRightPeriod |
		encoder.EncodeLeftPeriod |
		encoder.EncodeColon |
		encoder.EncodePipe |
		encoder.EncodeDoubleQuote |
		encoder.EncodeLtGt |
		encoder.EncodeQuestion |
		encoder.EncodeAsterisk |
		encoder.EncodeCtl |
		encoder.EncodeDot)

	// Test that all required encodings are present
	requiredEncodings := []encoder.MultiEncoder{
		encoder.EncodeBackSlash,   // \
		encoder.EncodeSlash,       // /
		encoder.EncodeColon,       // :
		encoder.EncodePipe,        // |
		encoder.EncodeDoubleQuote, // "
		encoder.EncodeLtGt,        // < >
		encoder.EncodeQuestion,    // ?
		encoder.EncodeAsterisk,    // *
		encoder.EncodeCtl,         // Control characters
	}

	for _, required := range requiredEncodings {
		if (expectedEncoding & required) == 0 {
			t.Errorf("encoding configuration missing required encoding: %v", required)
		}
	}

	// Test that the encoding handles Huawei Drive restricted characters
	enc := expectedEncoding
	restrictedChars := []struct {
		char string
		desc string
	}{
		{"<", "less than"},
		{">", "greater than"},
		{"|", "pipe"},
		{":", "colon"},
		{"\"", "double quote"},
		{"*", "asterisk"},
		{"?", "question mark"},
		{"/", "forward slash"},
		{"\\", "backslash"},
	}

	for _, rc := range restrictedChars {
		testName := "test" + rc.char + "file.txt"
		encoded := enc.FromStandardName(testName)
		if encoded == testName {
			t.Errorf("character %s (%s) was not encoded", rc.char, rc.desc)
		}
	}
}

// TestScopeConfiguration tests OAuth scope configuration
func TestScopeConfiguration(t *testing.T) {
	expectedScopes := []string{
		"openid",
		"profile",
		"https://www.huawei.com/auth/drive",
		"https://www.huawei.com/auth/drive.file",
	}

	if len(oauthConfig.Scopes) != len(expectedScopes) {
		t.Errorf("expected %d OAuth scopes, got %d", len(expectedScopes), len(oauthConfig.Scopes))
	}

	for i, expected := range expectedScopes {
		if i >= len(oauthConfig.Scopes) || oauthConfig.Scopes[i] != expected {
			t.Errorf("expected OAuth scope %q at position %d, got %q", expected, i, oauthConfig.Scopes[i])
		}
	}

	// Test OAuth URLs
	expectedAuthURL := "https://oauth-login.cloud.huawei.com/oauth2/v3/authorize"
	if oauthConfig.AuthURL != expectedAuthURL {
		t.Errorf("expected auth URL %q, got %q", expectedAuthURL, oauthConfig.AuthURL)
	}

	expectedTokenURL := "https://oauth-login.cloud.huawei.com/oauth2/v3/token"
	if oauthConfig.TokenURL != expectedTokenURL {
		t.Errorf("expected token URL %q, got %q", expectedTokenURL, oauthConfig.TokenURL)
	}
}

// TestObjectString tests object string representation
func TestObjectString(t *testing.T) {
	// Test normal object
	obj := &Object{remote: "path/to/file.txt"}
	expected := "path/to/file.txt"
	if obj.String() != expected {
		t.Errorf("expected object string %q, got %q", expected, obj.String())
	}

	// Test nil object
	var nilObj *Object
	if nilObj.String() != "<nil>" {
		t.Errorf("expected nil object string %q, got %q", "<nil>", nilObj.String())
	}
}

// TestSetModTime tests setting modification time
func TestSetModTime(t *testing.T) {
	// Huawei Drive does not support setting modification times
	// SetModTime should always return fs.ErrorCantSetModTime
	obj := &Object{id: "test-id"}
	err := obj.SetModTime(context.Background(), time.Now())
	if err != fs.ErrorCantSetModTime {
		t.Errorf("SetModTime should return fs.ErrorCantSetModTime, got: %v", err)
	}
} // TestCleanUp tests the CleanUp functionality
func TestCleanUp(t *testing.T) {
	// Test that the interface is correctly implemented at compile time
	// This test will fail to compile if CleanUpper interface is not properly implemented
	var _ fs.CleanUpper = (*Fs)(nil)

	t.Log("CleanUpper interface properly implemented")
}

// TestInterfaceCompliance tests all implemented interfaces at compile time
func TestInterfaceCompliance(t *testing.T) {
	var _ fs.Fs = (*Fs)(nil)
	var _ fs.Copier = (*Fs)(nil)
	var _ fs.Mover = (*Fs)(nil)
	var _ fs.DirMover = (*Fs)(nil)
	var _ fs.ListRer = (*Fs)(nil)
	var _ fs.Abouter = (*Fs)(nil)
	var _ fs.Purger = (*Fs)(nil)
	var _ fs.CleanUpper = (*Fs)(nil)
	var _ fs.UserInfoer = (*Fs)(nil)
	var _ fs.Disconnecter = (*Fs)(nil)
	var _ fs.DirCacheFlusher = (*Fs)(nil)
	var _ fs.MimeTyper = (*Object)(nil)
}

// TestMimeTyper tests the MimeType method on Object
func TestMimeTyper(t *testing.T) {
	ctx := context.Background()
	obj := &Object{
		mimeType:    "application/pdf",
		hasMetaData: true,
	}

	if obj.MimeType(ctx) != "application/pdf" {
		t.Errorf("expected MimeType %q, got %q", "application/pdf", obj.MimeType(ctx))
	}

	folderObj := &Object{
		mimeType:    api.FolderMimeType,
		hasMetaData: true,
	}
	if folderObj.MimeType(ctx) != api.FolderMimeType {
		t.Errorf("expected folder MimeType %q, got %q", api.FolderMimeType, folderObj.MimeType(ctx))
	}
}

// TestRootFolderIDConfig tests the root_folder_id configuration option
func TestRootFolderIDConfig(t *testing.T) {
	opts := Options{
		RootFolderID: "test-root-folder-id",
	}

	if opts.RootFolderID != "test-root-folder-id" {
		t.Errorf("expected RootFolderID %q, got %q", "test-root-folder-id", opts.RootFolderID)
	}

	emptyOpts := Options{}
	if emptyOpts.RootFolderID != "" {
		t.Errorf("expected empty default RootFolderID, got %q", emptyOpts.RootFolderID)
	}
}

// TestGlobalDomains tests the global domain detection
func TestGlobalDomains(t *testing.T) {
	if !api.GlobalDomains["driveapis.cloud.huawei.com.cn"] {
		t.Error("driveapis.cloud.huawei.com.cn should be recognized as global domain")
	}
	if !api.GlobalDomains["drive.cloud.hicloud.com"] {
		t.Error("drive.cloud.hicloud.com should be recognized as global domain")
	}
	if api.GlobalDomains["unknown.domain.com"] {
		t.Error("unknown.domain.com should not be recognized as global domain")
	}
}

// TestDomainToRootURL tests domain-to-URL mapping
func TestDomainToRootURL(t *testing.T) {
	testCases := []struct {
		domain   string
		expected string
	}{
		{"china", "https://drive-drcn.cloud.dbankcloud.cn"},
		{"europe", "https://drive-dre.cloud.dbankcloud.cn"},
		{"russia", "https://drive-drru.cloud.dbankcloud.cn"},
	}

	for _, tc := range testCases {
		url, ok := api.DomainToRootURL[tc.domain]
		if !ok {
			t.Errorf("domain %q not found in DomainToRootURL", tc.domain)
			continue
		}
		if url != tc.expected {
			t.Errorf("domain %q: expected URL %q, got %q", tc.domain, tc.expected, url)
		}
	}
}

// TestDetectRootIDAlgorithm tests the root ID detection algorithm logic
func TestDetectRootIDAlgorithm(t *testing.T) {
	t.Run("root_not_in_file_ids", func(t *testing.T) {
		files := []api.File{
			{ID: "file1", ParentFolder: []string{"ROOT_ID"}},
			{ID: "file2", ParentFolder: []string{"ROOT_ID"}},
			{ID: "dir1", ParentFolder: []string{"ROOT_ID"}},
			{ID: "file3", ParentFolder: []string{"dir1"}},
		}

		fileIDs := make(map[string]bool)
		parentCounts := make(map[string]int)
		for _, f := range files {
			fileIDs[f.ID] = true
			for _, p := range f.ParentFolder {
				parentCounts[p]++
			}
		}

		var bestID string
		var bestCount int
		for id, count := range parentCounts {
			if !fileIDs[id] && count > bestCount {
				bestID = id
				bestCount = count
			}
		}

		if bestID != "ROOT_ID" {
			t.Errorf("expected ROOT_ID, got %q", bestID)
		}
		if bestCount != 3 {
			t.Errorf("expected count 3, got %d", bestCount)
		}
	})

	t.Run("all_parents_are_file_ids", func(t *testing.T) {
		files := []api.File{
			{ID: "dir_a", ParentFolder: []string{"dir_b"}},
			{ID: "dir_b", ParentFolder: []string{"dir_a"}},
		}

		fileIDs := make(map[string]bool)
		parentCounts := make(map[string]int)
		for _, f := range files {
			fileIDs[f.ID] = true
			for _, p := range f.ParentFolder {
				parentCounts[p]++
			}
		}

		var bestID string
		var bestCount int
		for id, count := range parentCounts {
			if !fileIDs[id] && count > bestCount {
				bestID = id
				bestCount = count
			}
		}

		if bestID != "" {
			t.Errorf("expected empty bestID from primary pass, got %q", bestID)
		}

		// Fallback
		for id, count := range parentCounts {
			if count > bestCount {
				bestID = id
				bestCount = count
			}
		}

		if bestID == "" {
			t.Error("expected fallback to find a parent ID")
		}
	})

	t.Run("empty_file_list", func(t *testing.T) {
		files := []api.File{}
		if len(files) != 0 {
			t.Error("expected empty file list")
		}
	})
}
