package inputvalidation

import (
	"fmt"
	"net/mail"
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"
)

// Validation limits for input fields
const (
	// Email limits
	MaxEmailLength = 254 // RFC 5321

	// Name limits (collection names, file names, user names)
	MinNameLength = 1
	MaxNameLength = 255

	// Display name limits
	MaxDisplayNameLength = 100

	// Description limits
	MaxDescriptionLength = 1000

	// UUID format (standard UUID v4)
	UUIDLength = 36

	// OTT (One-Time Token) limits
	OTTLength = 8 // 8-digit code

	// Password limits
	MinPasswordLength = 8
	MaxPasswordLength = 128
)

// uuidRegex matches standard UUID format (8-4-4-4-12)
var uuidRegex = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)

// ottRegex matches 8-digit OTT codes
var ottRegex = regexp.MustCompile(`^[0-9]{8}$`)

// ValidateEmail validates an email address
func ValidateEmail(email string) error {
	if email == "" {
		return fmt.Errorf("email is required")
	}

	// Check length
	if len(email) > MaxEmailLength {
		return fmt.Errorf("email exceeds maximum length of %d characters", MaxEmailLength)
	}

	// Use Go's mail package for RFC 5322 validation
	_, err := mail.ParseAddress(email)
	if err != nil {
		return fmt.Errorf("invalid email format")
	}

	// Additional checks for security
	if strings.ContainsAny(email, "\x00\n\r") {
		return fmt.Errorf("email contains invalid characters")
	}

	return nil
}

// ValidateUUID validates a UUID string
func ValidateUUID(id, fieldName string) error {
	if id == "" {
		return fmt.Errorf("%s is required", fieldName)
	}

	if len(id) != UUIDLength {
		return fmt.Errorf("%s must be a valid UUID", fieldName)
	}

	if !uuidRegex.MatchString(id) {
		return fmt.Errorf("%s must be a valid UUID format", fieldName)
	}

	return nil
}

// ValidateName validates a name field (collection name, filename, etc.)
func ValidateName(name, fieldName string) error {
	if name == "" {
		return fmt.Errorf("%s is required", fieldName)
	}

	// Check length
	if len(name) > MaxNameLength {
		return fmt.Errorf("%s exceeds maximum length of %d characters", fieldName, MaxNameLength)
	}

	// Check for valid UTF-8
	if !utf8.ValidString(name) {
		return fmt.Errorf("%s contains invalid characters", fieldName)
	}

	// Check for control characters (except tab and newline which might be valid in descriptions)
	for _, r := range name {
		if r < 32 && r != '\t' && r != '\n' && r != '\r' {
			return fmt.Errorf("%s contains invalid control characters", fieldName)
		}
		// Also check for null byte and other dangerous characters
		if r == 0 {
			return fmt.Errorf("%s contains null characters", fieldName)
		}
	}

	// Check that it's not all whitespace
	if strings.TrimSpace(name) == "" {
		return fmt.Errorf("%s cannot be empty or whitespace only", fieldName)
	}

	return nil
}

// ValidateDisplayName validates a display name (first name, last name, etc.)
func ValidateDisplayName(name, fieldName string) error {
	// Display names can be empty (optional fields)
	if name == "" {
		return nil
	}

	// Check length
	if len(name) > MaxDisplayNameLength {
		return fmt.Errorf("%s exceeds maximum length of %d characters", fieldName, MaxDisplayNameLength)
	}

	// Check for valid UTF-8
	if !utf8.ValidString(name) {
		return fmt.Errorf("%s contains invalid characters", fieldName)
	}

	// Check for control characters
	for _, r := range name {
		if r < 32 || !unicode.IsPrint(r) {
			if r != ' ' { // Allow spaces
				return fmt.Errorf("%s contains invalid characters", fieldName)
			}
		}
	}

	return nil
}

// ValidateDescription validates a description field
func ValidateDescription(desc string) error {
	// Descriptions can be empty (optional)
	if desc == "" {
		return nil
	}

	// Check length
	if len(desc) > MaxDescriptionLength {
		return fmt.Errorf("description exceeds maximum length of %d characters", MaxDescriptionLength)
	}

	// Check for valid UTF-8
	if !utf8.ValidString(desc) {
		return fmt.Errorf("description contains invalid characters")
	}

	// Check for null bytes
	if strings.ContainsRune(desc, 0) {
		return fmt.Errorf("description contains null characters")
	}

	return nil
}

// ValidateOTT validates a one-time token (8-digit code)
func ValidateOTT(ott string) error {
	if ott == "" {
		return fmt.Errorf("verification code is required")
	}

	// Trim whitespace (users might copy-paste with spaces)
	ott = strings.TrimSpace(ott)

	if !ottRegex.MatchString(ott) {
		return fmt.Errorf("verification code must be an 8-digit number")
	}

	return nil
}

// ValidatePassword validates a password
func ValidatePassword(password string) error {
	if password == "" {
		return fmt.Errorf("password is required")
	}

	if len(password) < MinPasswordLength {
		return fmt.Errorf("password must be at least %d characters", MinPasswordLength)
	}

	if len(password) > MaxPasswordLength {
		return fmt.Errorf("password exceeds maximum length of %d characters", MaxPasswordLength)
	}

	// Check for null bytes (could indicate injection attempt)
	if strings.ContainsRune(password, 0) {
		return fmt.Errorf("password contains invalid characters")
	}

	return nil
}

// ValidateCollectionID is a convenience function for collection ID validation
func ValidateCollectionID(id string) error {
	return ValidateUUID(id, "collection ID")
}

// ValidateFileID is a convenience function for file ID validation
func ValidateFileID(id string) error {
	return ValidateUUID(id, "file ID")
}

// ValidateTagID is a convenience function for tag ID validation
func ValidateTagID(id string) error {
	return ValidateUUID(id, "tag ID")
}

// ValidateCollectionName validates a collection name
func ValidateCollectionName(name string) error {
	return ValidateName(name, "collection name")
}

// ValidateFileName validates a file name
func ValidateFileName(name string) error {
	if err := ValidateName(name, "filename"); err != nil {
		return err
	}

	// Additional file-specific validations
	// Check for path traversal attempts
	if strings.Contains(name, "..") {
		return fmt.Errorf("filename cannot contain path traversal sequences")
	}

	// Check for path separators
	if strings.ContainsAny(name, "/\\") {
		return fmt.Errorf("filename cannot contain path separators")
	}

	return nil
}

// SanitizeString removes or replaces potentially dangerous characters
// This is a defense-in-depth measure - validation should be done first
func SanitizeString(s string) string {
	// Remove null bytes
	s = strings.ReplaceAll(s, "\x00", "")

	// Trim excessive whitespace
	s = strings.TrimSpace(s)

	return s
}
