package security

import (
	"net"
	"regexp"
	"strings"
)

// Validator groups lightweight validation and sanitization helpers.
type Validator struct {
	passwordMinLength      int
	passwordRequireSpecial bool
	passwordRequireNumber  bool
}

// NewValidator creates a validator with the configured password rules.
func NewValidator(minLength int, requireSpecial, requireNumber bool) *Validator {
	return &Validator{
		passwordMinLength:      minLength,
		passwordRequireSpecial: requireSpecial,
		passwordRequireNumber:  requireNumber,
	}
}

// ValidateEmail validates email format.
func (v *Validator) ValidateEmail(email string) bool {
	if email == "" {
		return false
	}

	pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
	matched, _ := regexp.MatchString(pattern, email)
	return matched
}

// ValidatePhone validates mainland China mobile numbers.
func (v *Validator) ValidatePhone(phone string) bool {
	if phone == "" {
		return false
	}

	pattern := `^1[3-9]\d{9}$`
	matched, _ := regexp.MatchString(pattern, phone)
	return matched
}

// ValidateUsername validates usernames.
func (v *Validator) ValidateUsername(username string) bool {
	if username == "" {
		return false
	}

	pattern := `^[a-zA-Z][a-zA-Z0-9_]{3,19}$`
	matched, _ := regexp.MatchString(pattern, username)
	return matched
}

// ValidatePassword validates passwords using the shared runtime policy.
func (v *Validator) ValidatePassword(password string) bool {
	policy := PasswordPolicy{
		MinLength:      v.passwordMinLength,
		RequireSpecial: v.passwordRequireSpecial,
		RequireNumber:  v.passwordRequireNumber,
	}

	return policy.Validate(password) == nil
}

// SanitizeSQL removes obviously dangerous SQL injection patterns using regex.
// This is a defense-in-depth measure; parameterized queries should always be used.
func (v *Validator) SanitizeSQL(input string) string {
	// Escape SQL special characters by doubling them (SQL standard approach)
	// Order matters: escape backslash first to avoid double-escaping
	replacer := strings.NewReplacer(
		`\`, `\\`,
		`'`, `''`,
		`"`, `""`,
	)

	// Remove common SQL injection patterns that could bypass quoting
	dangerousPatterns := []string{
		`;[\s]*--`,           // SQL comment
		`/\*.*?\*/`,          // Block comment (non-greedy)
		`\bxp_\w+`,           // Extended stored procedures
		`\bexec[\s\(]`,       // EXEC statements
		`\bsp_\w+`,           // System stored procedures
		`\bwaitfor[\s]+delay`, // Time-based blind SQL injection
		`\bunion[\s]+select`, // UNION injection
		`\bdrop[\s]+table`,   // DROP TABLE
		`\binsert[\s]+into`,   // INSERT
		`\bupdate[\s]+\w+[\s]+set`, // UPDATE
		`\bdelete[\s]+from`,   // DELETE
	}

	result := replacer.Replace(input)

	// Apply pattern removal
	for _, pattern := range dangerousPatterns {
		re := regexp.MustCompile(`(?i)` + pattern) // Case-insensitive
		result = re.ReplaceAllString(result, "")
	}

	return result
}

// SanitizeXSS removes obviously dangerous XSS patterns using regex.
// This is a defense-in-depth measure; output encoding should always be used.
func (v *Validator) SanitizeXSS(input string) string {
	// Remove dangerous tags and attributes using pattern matching
	dangerousPatterns := []struct {
		pattern     string
		replaceAll  bool
	}{
		{`(?i)<script[^>]*>.*?</script>`, true},    // Script tags
		{`(?i)</script>`, false},                   // Closing script
		{`(?i)<iframe[^>]*>.*?</iframe>`, true},   // Iframe injection
		{`(?i)<object[^>]*>.*?</object>`, true},   // Object injection
		{`(?i)<embed[^>]*>.*?</embed>`, true},     // Embed injection
		{`(?i)<applet[^>]*>.*?</applet>`, true},   // Applet injection
		{`(?i)javascript\s*:`, false},              // JavaScript protocol
		{`(?i)vbscript\s*:`, false},               // VBScript protocol
		{`(?i)data\s*:`, false},                   // Data URL protocol
		{`(?i)on\w+\s*=`, false},                  // Event handlers
		{`(?i)<style[^>]*>.*?</style>`, true},    // Style injection
	}

	result := input

	for _, p := range dangerousPatterns {
		re := regexp.MustCompile(p.pattern)
		if p.replaceAll {
			result = re.ReplaceAllString(result, "")
		} else {
			result = re.ReplaceAllString(result, "")
		}
	}

	// Encode < and > to prevent tag construction
	result = strings.ReplaceAll(result, "<", "&lt;")
	result = strings.ReplaceAll(result, ">", "&gt;")

	// Restore entities if they were part of legitimate content
	result = strings.ReplaceAll(result, "&lt;", "<")
	result = strings.ReplaceAll(result, "&gt;", ">")

	return result
}

// ValidateURL validates a basic HTTP/HTTPS URL.
func (v *Validator) ValidateURL(url string) bool {
	if url == "" {
		return false
	}

	pattern := `^https?://[a-zA-Z0-9\-._~:/?#[\]@!$&'()*+,;=]+$`
	matched, _ := regexp.MatchString(pattern, url)
	return matched
}

// ValidateIP validates IPv4 or IPv6 addresses using net.ParseIP.
// Supports all valid formats including compressed IPv6 (::1, fe80::1, etc.)
func (v *Validator) ValidateIP(ip string) bool {
	if ip == "" {
		return false
	}
	return net.ParseIP(ip) != nil
}

// ValidateIPv4 validates IPv4 addresses only.
func (v *Validator) ValidateIPv4(ip string) bool {
	if ip == "" {
		return false
	}
	parsed := net.ParseIP(ip)
	return parsed != nil && parsed.To4() != nil
}

// ValidateIPv6 validates IPv6 addresses only.
func (v *Validator) ValidateIPv6(ip string) bool {
	if ip == "" {
		return false
	}
	parsed := net.ParseIP(ip)
	return parsed != nil && parsed.To4() == nil
}
