package file

import (
	"strings"
	"testing"

	dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
)

// TestValidateFileUpload_FolderValidCases tests valid folder uploads
func TestValidateFileUpload_FolderValidCases(t *testing.T) {
	validator := NewFileValidator()

	tests := []struct {
		name          string
		fileSize      int64
		thumbnailSize int64
		contentType   string
	}{
		{
			name:          "valid document upload to folder",
			fileSize:      10 * 1024 * 1024, // 10MB
			thumbnailSize: 0,
			contentType:   "application/pdf",
		},
		{
			name:          "valid large file to folder",
			fileSize:      500 * 1024 * 1024, // 500MB (max)
			thumbnailSize: 0,
			contentType:   "application/zip",
		},
		{
			name:          "valid executable to folder",
			fileSize:      50 * 1024 * 1024, // 50MB
			thumbnailSize: 0,
			contentType:   "application/x-executable",
		},
		{
			name:          "valid image with thumbnail to folder",
			fileSize:      20 * 1024 * 1024, // 20MB
			thumbnailSize: 5 * 1024 * 1024,  // 5MB
			contentType:   "image/png",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.ValidateFileUpload(
				dom_collection.CollectionTypeFolder,
				tt.fileSize,
				tt.thumbnailSize,
				tt.contentType,
			)
			if err != nil {
				t.Errorf("Expected valid folder upload, got error: %v", err)
			}
		})
	}
}

// TestValidateFileUpload_AlbumValidCases tests valid album uploads
func TestValidateFileUpload_AlbumValidCases(t *testing.T) {
	validator := NewFileValidator()

	tests := []struct {
		name          string
		fileSize      int64
		thumbnailSize int64
		contentType   string
	}{
		{
			name:          "valid JPEG image to album",
			fileSize:      10 * 1024 * 1024, // 10MB
			thumbnailSize: 1 * 1024 * 1024,  // 1MB
			contentType:   "image/jpeg",
		},
		{
			name:          "valid PNG image to album",
			fileSize:      20 * 1024 * 1024, // 20MB
			thumbnailSize: 2 * 1024 * 1024,  // 2MB
			contentType:   "image/png",
		},
		{
			name:          "valid MP4 video to album",
			fileSize:      100 * 1024 * 1024, // 100MB (max)
			thumbnailSize: 5 * 1024 * 1024,   // 5MB
			contentType:   "video/mp4",
		},
		{
			name:          "valid HEIC image to album",
			fileSize:      15 * 1024 * 1024, // 15MB
			thumbnailSize: 0,
			contentType:   "image/heic",
		},
		{
			name:          "valid WebP image to album",
			fileSize:      8 * 1024 * 1024, // 8MB
			thumbnailSize: 500 * 1024,      // 500KB
			contentType:   "image/webp",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.ValidateFileUpload(
				dom_collection.CollectionTypeAlbum,
				tt.fileSize,
				tt.thumbnailSize,
				tt.contentType,
			)
			if err != nil {
				t.Errorf("Expected valid album upload, got error: %v", err)
			}
		})
	}
}

// TestValidateFileUpload_FolderSizeLimits tests folder size limit enforcement
func TestValidateFileUpload_FolderSizeLimits(t *testing.T) {
	validator := NewFileValidator()

	tests := []struct {
		name          string
		fileSize      int64
		thumbnailSize int64
		expectError   bool
		errorContains string
	}{
		{
			name:          "file exceeds folder limit",
			fileSize:      501 * 1024 * 1024, // 501MB (over limit)
			thumbnailSize: 0,
			expectError:   true,
			errorContains: "exceeds maximum allowed size for folder",
		},
		{
			name:          "file at folder limit",
			fileSize:      500 * 1024 * 1024, // 500MB (exact limit)
			thumbnailSize: 0,
			expectError:   false,
		},
		{
			name:          "thumbnail exceeds limit",
			fileSize:      10 * 1024 * 1024,  // 10MB
			thumbnailSize: 11 * 1024 * 1024,  // 11MB (over limit)
			expectError:   true,
			errorContains: "thumbnail size",
		},
		{
			name:          "zero file size",
			fileSize:      0,
			thumbnailSize: 0,
			expectError:   true,
			errorContains: "must be greater than 0",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.ValidateFileUpload(
				dom_collection.CollectionTypeFolder,
				tt.fileSize,
				tt.thumbnailSize,
				"application/pdf",
			)
			if tt.expectError {
				if err == nil {
					t.Error("Expected error but got none")
				} else if !strings.Contains(err.Error(), tt.errorContains) {
					t.Errorf("Expected error containing '%s', got: %v", tt.errorContains, err)
				}
			} else {
				if err != nil {
					t.Errorf("Expected no error, got: %v", err)
				}
			}
		})
	}
}

// TestValidateFileUpload_AlbumSizeLimits tests album size limit enforcement
func TestValidateFileUpload_AlbumSizeLimits(t *testing.T) {
	validator := NewFileValidator()

	tests := []struct {
		name          string
		fileSize      int64
		expectError   bool
		errorContains string
	}{
		{
			name:          "file exceeds album limit",
			fileSize:      101 * 1024 * 1024, // 101MB (over limit)
			expectError:   true,
			errorContains: "exceeds maximum allowed size for album",
		},
		{
			name:          "file at album limit",
			fileSize:      100 * 1024 * 1024, // 100MB (exact limit)
			expectError:   false,
		},
		{
			name:        "file below album limit",
			fileSize:    50 * 1024 * 1024, // 50MB
			expectError: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.ValidateFileUpload(
				dom_collection.CollectionTypeAlbum,
				tt.fileSize,
				0,
				"image/jpeg",
			)
			if tt.expectError {
				if err == nil {
					t.Error("Expected error but got none")
				} else if !strings.Contains(err.Error(), tt.errorContains) {
					t.Errorf("Expected error containing '%s', got: %v", tt.errorContains, err)
				}
			} else {
				if err != nil {
					t.Errorf("Expected no error, got: %v", err)
				}
			}
		})
	}
}

// TestValidateFileUpload_AlbumContentTypeRestrictions tests album content type validation
func TestValidateFileUpload_AlbumContentTypeRestrictions(t *testing.T) {
	validator := NewFileValidator()

	tests := []struct {
		name          string
		contentType   string
		expectError   bool
		errorContains string
	}{
		{
			name:        "valid JPEG",
			contentType: "image/jpeg",
			expectError: false,
		},
		{
			name:        "valid PNG",
			contentType: "image/png",
			expectError: false,
		},
		{
			name:        "valid MP4",
			contentType: "video/mp4",
			expectError: false,
		},
		{
			name:          "invalid PDF",
			contentType:   "application/pdf",
			expectError:   true,
			errorContains: "not allowed in albums",
		},
		{
			name:          "invalid ZIP",
			contentType:   "application/zip",
			expectError:   true,
			errorContains: "not allowed in albums",
		},
		{
			name:          "invalid executable",
			contentType:   "application/x-executable",
			expectError:   true,
			errorContains: "not allowed in albums",
		},
		{
			name:          "empty content type",
			contentType:   "",
			expectError:   true,
			errorContains: "content type is required",
		},
		{
			name:        "case insensitive IMAGE/JPEG",
			contentType: "IMAGE/JPEG",
			expectError: false,
		},
		{
			name:        "content type with extra spaces",
			contentType: "  image/png  ",
			expectError: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.ValidateFileUpload(
				dom_collection.CollectionTypeAlbum,
				10*1024*1024, // 10MB
				0,
				tt.contentType,
			)
			if tt.expectError {
				if err == nil {
					t.Error("Expected error but got none")
				} else if !strings.Contains(err.Error(), tt.errorContains) {
					t.Errorf("Expected error containing '%s', got: %v", tt.errorContains, err)
				}
			} else {
				if err != nil {
					t.Errorf("Expected no error, got: %v", err)
				}
			}
		})
	}
}

// TestValidateFileUpload_FolderNoContentTypeRestrictions tests that folders allow any content type
func TestValidateFileUpload_FolderNoContentTypeRestrictions(t *testing.T) {
	validator := NewFileValidator()

	contentTypes := []string{
		"application/pdf",
		"application/zip",
		"application/x-executable",
		"text/plain",
		"application/json",
		"application/octet-stream",
		"image/jpeg", // Photos are also allowed in folders
		"video/mp4",  // Videos are also allowed in folders
		"",           // Even empty content type is OK for folders
	}

	for _, contentType := range contentTypes {
		t.Run("folder allows "+contentType, func(t *testing.T) {
			err := validator.ValidateFileUpload(
				dom_collection.CollectionTypeFolder,
				10*1024*1024, // 10MB
				0,
				contentType,
			)
			if err != nil {
				t.Errorf("Expected folder to allow content type '%s', got error: %v", contentType, err)
			}
		})
	}
}

// TestGetMaxFileSize tests the GetMaxFileSize helper method
func TestGetMaxFileSize(t *testing.T) {
	validator := NewFileValidator()

	folderMax := validator.GetMaxFileSize(dom_collection.CollectionTypeFolder)
	if folderMax != MaxFileSizeGeneral {
		t.Errorf("Expected folder max size %d, got %d", MaxFileSizeGeneral, folderMax)
	}

	albumMax := validator.GetMaxFileSize(dom_collection.CollectionTypeAlbum)
	if albumMax != MaxFileSizeAlbum {
		t.Errorf("Expected album max size %d, got %d", MaxFileSizeAlbum, albumMax)
	}
}

// TestGetMaxThumbnailSize tests the GetMaxThumbnailSize helper method
func TestGetMaxThumbnailSize(t *testing.T) {
	validator := NewFileValidator()

	maxThumb := validator.GetMaxThumbnailSize()
	if maxThumb != MaxThumbnailSize {
		t.Errorf("Expected max thumbnail size %d, got %d", MaxThumbnailSize, maxThumb)
	}
}

// TestGetAllowedContentTypes tests the GetAllowedContentTypes helper method
func TestGetAllowedContentTypes(t *testing.T) {
	validator := NewFileValidator()

	allowedTypes := validator.GetAllowedContentTypes()
	if len(allowedTypes) == 0 {
		t.Error("Expected non-empty list of allowed content types")
	}

	// Check that common photo/video types are included
	expectedTypes := []string{"image/jpeg", "image/png", "video/mp4"}
	for _, expected := range expectedTypes {
		found := false
		for _, allowed := range allowedTypes {
			if allowed == expected {
				found = true
				break
			}
		}
		if !found {
			t.Errorf("Expected allowed type '%s' not found in list", expected)
		}
	}
}

// TestFormatBytes tests the formatBytes helper function
func TestFormatBytes(t *testing.T) {
	tests := []struct {
		bytes    int64
		expected string
	}{
		{bytes: 0, expected: "0 B"},
		{bytes: 1023, expected: "1023 B"},
		{bytes: 1024, expected: "1.0 KB"},
		{bytes: 1024 * 1024, expected: "1.0 MB"},
		{bytes: 500 * 1024 * 1024, expected: "500.0 MB"},
		{bytes: 1024 * 1024 * 1024, expected: "1.0 GB"},
	}

	for _, tt := range tests {
		result := formatBytes(tt.bytes)
		if result != tt.expected {
			t.Errorf("formatBytes(%d) = %s, expected %s", tt.bytes, result, tt.expected)
		}
	}
}

// TestValidateFileUpload_AllAllowedAlbumContentTypes tests all allowed album content types
func TestValidateFileUpload_AllAllowedAlbumContentTypes(t *testing.T) {
	validator := NewFileValidator()

	for _, contentType := range AlbumAllowedContentTypes {
		t.Run("album allows "+contentType, func(t *testing.T) {
			err := validator.ValidateFileUpload(
				dom_collection.CollectionTypeAlbum,
				10*1024*1024, // 10MB
				0,
				contentType,
			)
			if err != nil {
				t.Errorf("Expected album to allow content type '%s', got error: %v", contentType, err)
			}
		})
	}
}
