import 'package:em2rp/models/event_model.dart'; import 'package:em2rp/models/equipment_model.dart'; import 'package:em2rp/models/container_model.dart'; import 'package:em2rp/models/qr_code_process_result.dart'; import 'package:em2rp/utils/debug_log.dart'; /// Service pour traiter les codes QR scannés ou saisis manuellement /// pendant la préparation d'un événement class QRCodeProcessingService { /// Traiter un code (équipement ou container) Future processCode({ required String code, required EventModel event, required dynamic step, // Changed to dynamic to accept any PreparationStep enum required Map equipmentCache, required Map containerCache, required Map validationState, required Map currentQuantities, }) async { try { DebugLog.info('[QRCodeProcessingService] Processing code: $code'); // Identifier le type selon le préfixe final isContainer = code.startsWith('BOX_'); if (isContainer) { return await _processContainer( code: code, event: event, step: step, equipmentCache: equipmentCache, containerCache: containerCache, validationState: validationState, currentQuantities: currentQuantities, ); } else { return await _processEquipment( code: code, event: event, step: step, equipmentCache: equipmentCache, validationState: validationState, currentQuantities: currentQuantities, ); } } catch (e) { DebugLog.error('[QRCodeProcessingService] Error processing code', e); return QRCodeProcessResult.error('Erreur lors du traitement du code: $e'); } } /// Traiter un code d'équipement Future _processEquipment({ required String code, required EventModel event, required dynamic step, required Map equipmentCache, required Map validationState, required Map currentQuantities, }) async { // Chercher l'équipement dans les équipements assignés final eventEquipment = event.assignedEquipment .cast() .firstWhere( (eq) => eq?.equipmentId == code, orElse: () => null, ); if (eventEquipment == null) { DebugLog.info('[QRCodeProcessingService] Equipment $code not found in event'); return QRCodeProcessResult.notFoundInEvent(code); } final equipment = equipmentCache[code]; final equipmentName = equipment?.name ?? 'Équipement inconnu'; // Vérifier si l'équipement a des quantités if (equipment?.hasQuantity ?? false) { return _processQuantitativeEquipment( code: code, equipmentName: equipmentName, eventEquipment: eventEquipment, step: step, validationState: validationState, currentQuantities: currentQuantities, ); } else { return _processNonQuantitativeEquipment( code: code, equipmentName: equipmentName, validationState: validationState, ); } } /// Traiter un équipement quantitatif (incrémenter la quantité) QRCodeProcessResult _processQuantitativeEquipment({ required String code, required String equipmentName, required EventEquipment eventEquipment, required dynamic step, required Map validationState, required Map currentQuantities, }) { final currentQty = currentQuantities[code] ?? 0; final targetQty = _getTargetQuantity(eventEquipment, step); // Vérifier si on a déjà atteint la quantité cible if (currentQty >= targetQty) { return QRCodeProcessResult.error( 'Quantité cible déjà atteinte pour $equipmentName ($currentQty/$targetQty)', ); } // Incrémenter la quantité final newQty = currentQty + 1; final shouldCheck = newQty >= targetQty; return QRCodeProcessResult.success( message: '$equipmentName : $newQty/$targetQty${shouldCheck ? " ✓" : ""}', affectedEquipmentIds: [code], updatedQuantities: {code: newQty}, updatedValidationState: shouldCheck ? {code: true} : null, ); } /// Traiter un équipement non quantitatif (cocher) QRCodeProcessResult _processNonQuantitativeEquipment({ required String code, required String equipmentName, required Map validationState, }) { // Vérifier si déjà coché if (validationState[code] == true) { return QRCodeProcessResult.error('$equipmentName est déjà coché'); } return QRCodeProcessResult.success( message: '$equipmentName a été coché ✓', affectedEquipmentIds: [code], updatedValidationState: {code: true}, ); } /// Traiter un code de container (cocher tous les enfants) Future _processContainer({ required String code, required EventModel event, required dynamic step, required Map equipmentCache, required Map containerCache, required Map validationState, required Map currentQuantities, }) async { // Vérifier que le container est assigné à l'événement if (!event.assignedContainers.contains(code)) { DebugLog.info('[QRCodeProcessingService] Container $code not found in event'); return QRCodeProcessResult.notFoundInEvent(code); } final container = containerCache[code]; if (container == null) { return QRCodeProcessResult.error('Container introuvable dans le cache'); } // Traiter tous les équipements enfants final updatedValidation = {}; final updatedQuantities = {}; int processedCount = 0; for (final childId in container.equipmentIds) { final childEventEq = event.assignedEquipment .cast() .firstWhere( (eq) => eq?.equipmentId == childId, orElse: () => null, ); if (childEventEq == null) continue; final childEquipment = equipmentCache[childId]; // Si quantitatif, mettre la quantité actuelle = quantité cible if (childEquipment?.hasQuantity ?? false) { final targetQty = _getTargetQuantity(childEventEq, step); updatedQuantities[childId] = targetQty; } // Cocher l'enfant updatedValidation[childId] = true; processedCount++; } if (processedCount == 0) { return QRCodeProcessResult.error( 'Aucun équipement trouvé dans le container ${container.name}', ); } return QRCodeProcessResult.success( message: 'Container ${container.name} : $processedCount équipement(s) validé(s) ✓', affectedEquipmentIds: updatedValidation.keys.toList(), updatedValidationState: updatedValidation, updatedQuantities: updatedQuantities.isNotEmpty ? updatedQuantities : null, ); } /// Obtenir la quantité requise selon l'étape /// Logique: chaque étape utilise la quantité actuelle de l'étape N-1 int _getTargetQuantity(EventEquipment eventEquipment, dynamic step) { // Convertir l'enum en string pour comparer final stepString = step.toString().split('.').last; switch (stepString) { case 'preparation': // Étape 1 : Quantité définie à la création de l'événement return eventEquipment.quantity; case 'loadingOutbound': // Étape 2 : Quantité validée à l'étape 1 (préparation) return eventEquipment.quantityAtPreparation ?? eventEquipment.quantity; case 'unloadingReturn': // Étape 3 : Quantité validée à l'étape 2 (chargement) return eventEquipment.quantityAtLoading ?? eventEquipment.quantityAtPreparation ?? eventEquipment.quantity; case 'return_': // Étape 4 : Quantité validée à l'étape 3 (déchargement) return eventEquipment.quantityAtUnloading ?? eventEquipment.quantityAtLoading ?? eventEquipment.quantityAtPreparation ?? eventEquipment.quantity; default: return eventEquipment.quantity; } } }