import '../../models/ignis_group.dart'; final RegExp _groupIdAllowedPattern = RegExp(r'^[a-z0-9][a-z0-9_-]*$'); final RegExp _groupIdSanitizePattern = RegExp(r'[^a-z0-9_-]+'); final RegExp _dashCollapsePattern = RegExp(r'[-_]{2,}'); const Map _transliterationMap = { 'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd', 'е': 'e', 'ё': 'e', 'ж': 'zh', 'з': 'z', 'и': 'i', 'й': 'y', 'к': 'k', 'л': 'l', 'м': 'm', 'н': 'n', 'о': 'o', 'п': 'p', 'р': 'r', 'с': 's', 'т': 't', 'у': 'u', 'ф': 'f', 'х': 'h', 'ц': 'ts', 'ч': 'ch', 'ш': 'sh', 'щ': 'sch', 'ъ': '', 'ы': 'y', 'ь': '', 'э': 'e', 'ю': 'yu', 'я': 'ya', }; String slugifyGroupId(String input) { final lower = input.trim().toLowerCase(); final buffer = StringBuffer(); for (final rune in lower.runes) { final char = String.fromCharCode(rune); buffer.write(_transliterationMap[char] ?? char); } var value = buffer .toString() .replaceAll(RegExp(r'\s+'), '-') .replaceAll(_groupIdSanitizePattern, '-') .replaceAll(_dashCollapsePattern, '-') .replaceAll(RegExp(r'^[-_]+|[-_]+$'), ''); if (value.length > 32) { value = value.substring(0, 32).replaceAll(RegExp(r'[-_]+$'), ''); } return value; } String? validateGroupId(String value, Iterable existingGroups) { final normalized = value.trim().toLowerCase(); if (normalized.isEmpty) { return 'Укажите ID группы'; } if (!_groupIdAllowedPattern.hasMatch(normalized)) { return 'Только латиница, цифры, "-" и "_"'; } final alreadyExists = existingGroups.any((group) => group.id == normalized); if (alreadyExists) { return 'Группа с таким ID уже существует'; } return null; } String? validateGroupName(String value) { if (value.trim().isEmpty) { return 'Укажите название группы'; } return null; } List findGroupMembershipConflicts( Set selectedDeviceIds, Iterable existingGroups, ) { if (selectedDeviceIds.isEmpty) { return const []; } return existingGroups .where( (group) => group.macs.any((deviceId) => selectedDeviceIds.contains(deviceId)), ) .toList(); }