using System.Text.RegularExpressions; using Microsoft.EntityFrameworkCore; namespace DysonNetwork.Sphere.Account; /// /// Service for handling username generation and validation /// public class AccountUsernameService(AppDatabase db) { private readonly Random _random = new(); /// /// Generates a unique username based on the provided base name /// /// The preferred username /// A unique username public async Task GenerateUniqueUsernameAsync(string baseName) { // Sanitize the base name var sanitized = SanitizeUsername(baseName); // If the base name is empty after sanitization, use a default if (string.IsNullOrEmpty(sanitized)) { sanitized = "user"; } // Check if the sanitized name is available if (!await IsUsernameExistsAsync(sanitized)) { return sanitized; } // Try up to 10 times with random numbers for (int i = 0; i < 10; i++) { var suffix = _random.Next(1000, 9999); var candidate = $"{sanitized}{suffix}"; if (!await IsUsernameExistsAsync(candidate)) { return candidate; } } // If all attempts fail, use a timestamp var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); return $"{sanitized}{timestamp}"; } /// /// Sanitizes a username by removing invalid characters and converting to lowercase /// public string SanitizeUsername(string username) { if (string.IsNullOrEmpty(username)) return string.Empty; // Replace spaces and special characters with underscores var sanitized = Regex.Replace(username, @"[^a-zA-Z0-9_\-]", ""); // Convert to lowercase sanitized = sanitized.ToLowerInvariant(); // Ensure it starts with a letter if (sanitized.Length > 0 && !char.IsLetter(sanitized[0])) { sanitized = "u" + sanitized; } // Truncate if too long if (sanitized.Length > 30) { sanitized = sanitized[..30]; } return sanitized; } /// /// Checks if a username already exists /// public async Task IsUsernameExistsAsync(string username) { return await db.Accounts.AnyAsync(a => a.Name == username); } /// /// Generates a username from an email address /// /// The email address to generate a username from /// A unique username derived from the email public async Task GenerateUsernameFromEmailAsync(string email) { if (string.IsNullOrEmpty(email)) return await GenerateUniqueUsernameAsync("user"); // Extract the local part of the email (before the @) var localPart = email.Split('@')[0]; // Use the local part as the base for username generation return await GenerateUniqueUsernameAsync(localPart); } }