package filestore

import (
	"mime"
	"path/filepath"
	"strings"
)

// SupportedMimeTypes defines the allowed MIME types for uploads
var SupportedMimeTypes = map[string]bool{
	"image/jpeg":         true,
	"image/png":          true,
	"image/gif":          true,
	"image/webp":         true,
	"image/svg+xml":      true,
	"application/pdf":    true,
	"application/msword": true,
	"application/vnd.openxmlformats-officedocument.wordprocessingml.document": true,
	"application/vnd.ms-excel": true,
	"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": true,
	"text/plain":                   true,
	"text/csv":                     true,
	"application/json":             true,
	"application/zip":              true,
	"application/x-rar-compressed": true,
	"application/x-7z-compressed":  true,
}

// ProfileImageMimeTypes defines allowed MIME types for profile images
var ProfileImageMimeTypes = map[string]bool{
	"image/jpeg": true,
	"image/png":  true,
	"image/gif":  true,
	"image/webp": true,
}

// FileValidator validates file uploads
type FileValidator struct {
	MaxSize           int64
	AllowedMimeTypes  map[string]bool
	AllowedExtensions map[string]bool
}

// NewFileValidator creates a new file validator
func NewFileValidator(maxSize int64, mimeTypes map[string]bool) *FileValidator {
	return &FileValidator{
		MaxSize:           maxSize,
		AllowedMimeTypes:  mimeTypes,
		AllowedExtensions: make(map[string]bool),
	}
}

// NewProfileImageValidator creates a validator for profile images
func NewProfileImageValidator() *FileValidator {
	return &FileValidator{
		MaxSize:           5 * 1024 * 1024, // 5 MB
		AllowedMimeTypes:  ProfileImageMimeTypes,
		AllowedExtensions: map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".webp": true},
	}
}

// ValidateFileSize validates that the file size is within limits
func (v *FileValidator) ValidateFileSize(size int64) error {
	if size <= 0 {
		return NewError(ErrCodeInvalidInput, "file size must be greater than 0")
	}
	if size > v.MaxSize {
		return NewError(ErrCodeFileTooLarge, "file size exceeds maximum limit")
	}
	return nil
}

// ValidateMimeType validates that the MIME type is supported
func (v *FileValidator) ValidateMimeType(contentType string) error {
	if contentType == "" {
		return NewError(ErrCodeInvalidInput, "content type is required")
	}

	if !v.AllowedMimeTypes[contentType] {
		return NewError(ErrCodeUnsupported, "unsupported file type: "+contentType)
	}
	return nil
}

// ValidateFilename validates that the filename is valid and has an allowed extension
func (v *FileValidator) ValidateFilename(filename string) error {
	if filename == "" {
		return NewError(ErrCodeInvalidInput, "filename is required")
	}

	// Check for path traversal
	if strings.Contains(filename, "..") || strings.Contains(filename, "/") || strings.Contains(filename, "\\") {
		return NewError(ErrCodeInvalidInput, "invalid filename: path traversal detected")
	}

	// Check extension if configured
	if len(v.AllowedExtensions) > 0 {
		ext := strings.ToLower(filepath.Ext(filename))
		if !v.AllowedExtensions[ext] {
			return NewError(ErrCodeUnsupported, "unsupported file extension: "+ext)
		}
	}

	return nil
}

// ValidateFile validates all file properties
func (v *FileValidator) ValidateFile(filename string, contentType string, size int64) error {
	if err := v.ValidateFilename(filename); err != nil {
		return err
	}
	if err := v.ValidateFileSize(size); err != nil {
		return err
	}
	if err := v.ValidateMimeType(contentType); err != nil {
		return err
	}
	return nil
}

// DetectMimeType detects MIME type from filename
func DetectMimeType(filename string) string {
	ext := filepath.Ext(filename)
	if detected := mime.TypeByExtension(ext); detected != "" {
		return detected
	}
	return "application/octet-stream"
}

// SanitizeFilename removes potentially dangerous characters from filename
func SanitizeFilename(filename string) string {
	// Remove path separators
	filename = strings.ReplaceAll(filename, "/", "_")
	filename = strings.ReplaceAll(filename, "\\", "_")

	// Remove null bytes
	filename = strings.ReplaceAll(filename, "\x00", "")

	// Remove other potentially dangerous characters
	dangerous := []string{"..", "~", "`", "!", "@", "#", "$", "%", "^", "&", "*", "?", ":", ";", "<", ">", "|"}
	for _, char := range dangerous {
		filename = strings.ReplaceAll(filename, char, "_")
	}

	// Trim whitespace
	filename = strings.TrimSpace(filename)

	// Ensure filename is not empty
	if filename == "" {
		filename = "file"
	}

	return filename
}
