package service

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

const (
	MinUsernameLength = 6
	MaxUsernameLength = 24
	MinPasswordLength = 8
	MaxPasswordLength = 24
)

var (
	usernamePattern  = regexp.MustCompile(`^[A-Za-z0-9]{6,24}$`)
	emailCodePattern = regexp.MustCompile(`^\d{6}$`)
)

func ValidateEmail(email string) (string, error) {
	normalized := strings.TrimSpace(strings.ToLower(email))
	if normalized == "" {
		return "", errors.New("邮箱不能为空")
	}
	if len(normalized) > 254 {
		return "", errors.New("邮箱长度不能超过 254 个字符")
	}
	addr, err := mail.ParseAddress(normalized)
	if err != nil || strings.TrimSpace(strings.ToLower(addr.Address)) != normalized {
		return "", errors.New("邮箱格式不正确")
	}
	local, domain, ok := strings.Cut(normalized, "@")
	if !ok || local == "" || domain == "" || strings.Contains(domain, "..") || !strings.Contains(domain, ".") {
		return "", errors.New("邮箱格式不正确")
	}
	return normalized, nil
}

func ValidateUsername(username string) (string, error) {
	normalized := strings.TrimSpace(username)
	if normalized == "" {
		return "", errors.New("用户名不能为空")
	}
	length := utf8.RuneCountInString(normalized)
	if length < MinUsernameLength || length > MaxUsernameLength {
		return "", errors.New("用户名长度需为 6 到 24 个字符")
	}
	if !usernamePattern.MatchString(normalized) {
		return "", errors.New("用户名只能使用字母和数字")
	}
	return normalized, nil
}

func ValidatePassword(password string) error {
	length := utf8.RuneCountInString(password)
	if length < MinPasswordLength || length > MaxPasswordLength {
		return errors.New("密码长度需为 8 到 24 个字符")
	}

	var hasLetter bool
	var hasUpper bool
	var hasLower bool
	var hasDigit bool
	var hasSymbol bool
	for _, r := range password {
		if unicode.IsSpace(r) {
			return errors.New("密码不能包含空白字符")
		}
		if !isAllowedPasswordRune(r) {
			return errors.New("密码包含不允许的字符")
		}
		if unicode.IsLetter(r) {
			hasLetter = true
			if unicode.IsUpper(r) {
				hasUpper = true
			}
			if unicode.IsLower(r) {
				hasLower = true
			}
		}
		if unicode.IsDigit(r) {
			hasDigit = true
		}
		if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
			hasSymbol = true
		}
	}
	if !hasLetter || !hasUpper || !hasLower || !hasDigit || !hasSymbol {
		return errors.New("密码必须同时包含大写字母、小写字母、数字和符号")
	}
	return nil
}

func isAllowedPasswordRune(r rune) bool {
	if unicode.IsLetter(r) || unicode.IsDigit(r) {
		return true
	}
	switch r {
	case '(', ')', '~', '!', '@', '#', '$', '%', '^', '&', '*', '-', '_', '+', '=', '|',
		'{', '}', '[', ']', ':', ';', '\'', '<', '>', ',', '.', '?', '/':
		return true
	default:
		return false
	}
}

func ValidateEmailCode(code string) (string, error) {
	normalized := strings.TrimSpace(code)
	if !emailCodePattern.MatchString(normalized) {
		return "", errors.New("邮箱验证码必须是 6 位纯数字")
	}
	return normalized, nil
}

func ValidateLoginIdentifier(identifier string) (string, error) {
	normalized := strings.TrimSpace(identifier)
	if normalized == "" {
		return "", errors.New("账号不能为空")
	}
	if strings.Contains(normalized, "@") {
		return ValidateEmail(normalized)
	}
	return ValidateUsername(normalized)
}

func ValidateAllowedEmailDomains(domains []string) []string {
	out := make([]string, 0, len(domains))
	seen := map[string]struct{}{}
	for _, raw := range domains {
		normalized := strings.TrimSpace(strings.ToLower(strings.TrimPrefix(raw, "@")))
		if normalized == "" || strings.Contains(normalized, " ") {
			continue
		}
		if _, ok := seen[normalized]; ok {
			continue
		}
		seen[normalized] = struct{}{}
		out = append(out, normalized)
	}
	return out
}

func EmailDomainAllowed(email string, domains []string) bool {
	if len(domains) == 0 {
		return true
	}
	_, domain, ok := strings.Cut(strings.ToLower(strings.TrimSpace(email)), "@")
	if !ok {
		return false
	}
	for _, allowed := range ValidateAllowedEmailDomains(domains) {
		if domain == allowed {
			return true
		}
	}
	return false
}
