import 'package:em2rp/services/api_service.dart'; import 'package:flutter/material.dart'; /// Utilitaires partages pour la suppression d'equipement avec forcage. class EquipmentDeleteUtils { static const String _legacyConflictToken = 'future_event_assignment'; static const List _conflictMessageTokens = [ 'cannot delete equipment because it is assigned to upcoming events', 'cannot delete equipment because it is assigned to future events', 'assigned to upcoming events', 'assigned to future events', ]; static const String deleteDialogTitle = 'Confirmer la suppression'; static const String deleteDialogCancelLabel = 'Annuler'; static const String deleteDialogConfirmLabel = 'Supprimer'; static const String deleteSuccessMessage = 'Équipement supprimé avec succès'; /// Retourne [name] si renseigne, sinon [id]. static String resolveEquipmentLabel({required String id, String? name}) { final trimmedName = name?.trim(); if (trimmedName == null || trimmedName.isEmpty) { return id; } return trimmedName; } /// Construit le message de confirmation de suppression d'un equipement. static String buildSingleDeleteConfirmationMessage(String equipmentLabel) { return 'Voulez-vous vraiment supprimer "$equipmentLabel" ?\n\n' 'Cette action est irréversible.'; } /// Construit le message de confirmation de suppression multiple. static String buildBulkDeleteConfirmationMessage(int selectedCount) { return 'Voulez-vous vraiment supprimer $selectedCount équipement(s) ?\n\n' 'Cette action est irréversible.'; } /// Construit le message de succes de suppression multiple. static String buildBulkDeleteSuccessMessage(int deletedCount) { return '$deletedCount équipement(s) supprimé(s) avec succès'; } /// Construit un message d'erreur de suppression homogene. static String buildDeleteErrorMessage(Object error) { return 'Erreur lors de la suppression : $error'; } /// Indique si l'erreur correspond a un conflit de suppression 409. static bool isFutureAssignmentDeleteConflict(Object error) { if (error is ApiException && !error.isConflict) { return false; } final normalizedMessage = _normalizeErrorMessage(error); if (normalizedMessage.contains(_legacyConflictToken)) { return true; } return _conflictMessageTokens.any(normalizedMessage.contains); } /// Affiche la confirmation de suppression forcee. static Future showForceDeleteDialog( BuildContext context, { required String equipmentLabel, }) async { final shouldForceDelete = await showDialog( context: context, builder: (dialogContext) => AlertDialog( title: const Text('Équipement utilisé dans un événement à venir'), content: Text( '"$equipmentLabel" est assigné à au moins un événement à venir.\n\n' 'Voulez-vous forcer la suppression ?', ), actions: [ TextButton( onPressed: () => Navigator.pop(dialogContext, false), child: const Text('Annuler'), ), TextButton( onPressed: () => Navigator.pop(dialogContext, true), style: TextButton.styleFrom(foregroundColor: Colors.red), child: const Text('Forcer la suppression'), ), ], ), ); return shouldForceDelete == true; } /// Execute une suppression, puis propose un forcage en cas de conflit 409. static Future deleteWithFutureAssignmentCheck({ required BuildContext context, required String equipmentLabel, required Future Function({bool forceDelete}) deleteEquipment, }) async { try { await deleteEquipment(forceDelete: false); return true; } catch (error) { if (!isFutureAssignmentDeleteConflict(error)) { rethrow; } if (!context.mounted) { return false; } final shouldForceDelete = await showForceDeleteDialog( context, equipmentLabel: equipmentLabel, ); if (!shouldForceDelete) { return false; } await deleteEquipment(forceDelete: true); return true; } } static String _normalizeErrorMessage(Object error) { if (error is ApiException) { return error.message.toLowerCase(); } return error.toString().toLowerCase(); } }