package file

import (
	"fmt"
	"strings"

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

// File size limits (in bytes)
const (
	// MaxFileSizeGeneral is the maximum file size for general folders (500MB)
	MaxFileSizeGeneral = 500 * 1024 * 1024

	// MaxFileSizeAlbum is the maximum file size for album uploads (100MB)
	// Albums are for photos/videos, so we use a more restrictive limit
	MaxFileSizeAlbum = 100 * 1024 * 1024

	// MaxThumbnailSize is the maximum thumbnail size (10MB)
	MaxThumbnailSize = 10 * 1024 * 1024
)

// Allowed content types for albums (photos and videos only)
var AlbumAllowedContentTypes = []string{
	// Image formats
	"image/jpeg",
	"image/jpg",
	"image/png",
	"image/gif",
	"image/webp",
	"image/heic",
	"image/heif",
	"image/bmp",
	"image/tiff",
	"image/svg+xml",

	// Video formats
	"video/mp4",
	"video/mpeg",
	"video/quicktime", // .mov files
	"video/x-msvideo", // .avi files
	"video/x-matroska", // .mkv files
	"video/webm",
	"video/3gpp",
	"video/x-flv",
}

// FileValidator provides file upload validation based on collection type
type FileValidator struct{}

// NewFileValidator creates a new file validator
func NewFileValidator() *FileValidator {
	return &FileValidator{}
}

// ValidateFileUpload validates a file upload based on collection type and file properties
// CWE-434: Unrestricted Upload of File with Dangerous Type
// OWASP A01:2021: Broken Access Control - File upload restrictions
func (v *FileValidator) ValidateFileUpload(
	collectionType string,
	fileSize int64,
	thumbnailSize int64,
	contentType string,
) error {
	// Validate file size based on collection type
	if err := v.validateFileSize(collectionType, fileSize); err != nil {
		return err
	}

	// Validate thumbnail size if provided
	if thumbnailSize > 0 {
		if err := v.validateThumbnailSize(thumbnailSize); err != nil {
			return err
		}
	}

	// Validate content type for albums (photos/videos only)
	if collectionType == dom_collection.CollectionTypeAlbum {
		if err := v.validateContentType(contentType); err != nil {
			return err
		}
	}

	// For folders (non-albums), no content-type restrictions
	// Users can upload any file type to regular folders

	return nil
}

// validateFileSize checks if the file size is within limits
func (v *FileValidator) validateFileSize(collectionType string, fileSize int64) error {
	if fileSize <= 0 {
		return fmt.Errorf("file size must be greater than 0")
	}

	var maxSize int64
	var collectionTypeName string

	if collectionType == dom_collection.CollectionTypeAlbum {
		maxSize = MaxFileSizeAlbum
		collectionTypeName = "album"
	} else {
		maxSize = MaxFileSizeGeneral
		collectionTypeName = "folder"
	}

	if fileSize > maxSize {
		return fmt.Errorf(
			"file size (%s) exceeds maximum allowed size for %s (%s)",
			formatBytes(fileSize),
			collectionTypeName,
			formatBytes(maxSize),
		)
	}

	return nil
}

// validateThumbnailSize checks if the thumbnail size is within limits
func (v *FileValidator) validateThumbnailSize(thumbnailSize int64) error {
	if thumbnailSize <= 0 {
		return nil // Thumbnail is optional
	}

	if thumbnailSize > MaxThumbnailSize {
		return fmt.Errorf(
			"thumbnail size (%s) exceeds maximum allowed size (%s)",
			formatBytes(thumbnailSize),
			formatBytes(MaxThumbnailSize),
		)
	}

	return nil
}

// validateContentType checks if the content type is allowed for albums
func (v *FileValidator) validateContentType(contentType string) error {
	if contentType == "" {
		return fmt.Errorf("content type is required for album uploads")
	}

	// Normalize content type (lowercase and trim)
	normalizedType := strings.ToLower(strings.TrimSpace(contentType))

	// Check if content type is in allowed list
	for _, allowedType := range AlbumAllowedContentTypes {
		if normalizedType == allowedType {
			return nil
		}
	}

	return fmt.Errorf(
		"content type '%s' is not allowed in albums. Only photos and videos are permitted",
		contentType,
	)
}

// GetAllowedContentTypes returns the list of allowed content types for albums
func (v *FileValidator) GetAllowedContentTypes() []string {
	return AlbumAllowedContentTypes
}

// GetMaxFileSize returns the maximum file size for a collection type
func (v *FileValidator) GetMaxFileSize(collectionType string) int64 {
	if collectionType == dom_collection.CollectionTypeAlbum {
		return MaxFileSizeAlbum
	}
	return MaxFileSizeGeneral
}

// GetMaxThumbnailSize returns the maximum thumbnail size
func (v *FileValidator) GetMaxThumbnailSize() int64 {
	return MaxThumbnailSize
}

// formatBytes formats byte size into human-readable format
func formatBytes(bytes int64) string {
	const unit = 1024
	if bytes < unit {
		return fmt.Sprintf("%d B", bytes)
	}
	div, exp := int64(unit), 0
	for n := bytes / unit; n >= unit; n /= unit {
		div *= unit
		exp++
	}
	return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}
