package matching import ( "fuj-management/go/internal/domain/czech" "regexp" "strings" ) var ( nicknameRe = regexp.MustCompile(`\(([^)]+)\)`) nicknameStripRe = regexp.MustCompile(`\s*\([^)]*\)\s*`) ) // BuildNameVariants returns searchable lowercase ASCII variants of a member name. // // Example: "František Vrbík (Štrúdl)" → ["frantisek vrbik", "strudl", "vrbik", "frantisek"] // // variants[0] is always the full normalized base name (no nickname). MatchMembers relies on // this invariant for the exact short-circuit pass. Variants shorter than 3 characters are // dropped. // // Ports scripts/match_payments.py _build_name_variants. func BuildNameVariants(name string) []string { var nickname string if m := nicknameRe.FindStringSubmatch(name); m != nil { nickname = m[1] } base := strings.TrimSpace(nicknameStripRe.ReplaceAllString(name, " ")) normalizedBase := czech.Normalize(base) normalizedNick := czech.Normalize(nickname) variants := []string{normalizedBase} if normalizedNick != "" { variants = append(variants, normalizedNick) } parts := strings.Fields(normalizedBase) if len(parts) >= 2 { variants = append(variants, parts[len(parts)-1]) // last name variants = append(variants, parts[0]) // first name } filtered := variants[:0] for _, v := range variants { if len(v) >= 3 { filtered = append(filtered, v) } } return filtered } // wordIn returns true if needle appears as a whole word in haystack. // Both needle and haystack must already be ASCII-folded (via czech.Normalize). func wordIn(needle, haystack string) bool { pattern := `\b` + regexp.QuoteMeta(needle) + `\b` matched, _ := regexp.MatchString(pattern, haystack) return matched }