const { validateUsername, USERNAME_MIN_LENGTH, USERNAME_MAX_LENGTH, } = require('./usernameValidation') const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') const isUsernameTaken = async (UserModel, userName, excludeUserId) => { const existing = await UserModel.findOne({ user_name: { $regex: new RegExp(`^${escapeRegex(userName)}$`, 'i') } }) if (!existing) return false if (excludeUserId && existing._id.toString() === excludeUserId) return false return true } const padBase = (base) => { let value = base.replace(/[^a-zA-Z0-9._-]/g, '') if (!/^[a-zA-Z0-9]/.test(value)) value = `u${value}` while (value.length < USERNAME_MIN_LENGTH - 2) { value = `${value}0` } return value.slice(0, Math.max(USERNAME_MIN_LENGTH - 2, 1)) } const buildCandidates = (base, attempt) => { const random2 = () => String(Math.floor(Math.random() * 90 + 10)) const random3 = () => String(Math.floor(Math.random() * 900 + 100)) const stem = padBase(base) const builders = [ () => `${stem}${random2()}`, () => `${stem}_${random2()}`, () => `${stem}.${random2()}`, () => `${stem}-${random2()}`, () => `${stem}${random3()}`, () => `${stem.slice(0, Math.max(USERNAME_MIN_LENGTH - 3, 1))}${random3()}`, ] return builders[attempt % builders.length]() } const trimToMax = (value) => value.length > USERNAME_MAX_LENGTH ? value.slice(0, USERNAME_MAX_LENGTH) : value const generateUsernameSuggestions = async (UserModel, baseUserName, excludeUserId) => { const base = trimToMax(baseUserName.trim().toLowerCase()) const suggestions = [] let attempt = 0 while (suggestions.length < 3 && attempt < 40) { const candidate = trimToMax(buildCandidates(base, attempt)) attempt += 1 if (validateUsername(candidate)) continue if (suggestions.includes(candidate)) continue if (await isUsernameTaken(UserModel, candidate, excludeUserId)) continue suggestions.push(candidate) } return suggestions } module.exports = { generateUsernameSuggestions, }