import 'package:flutter/foundation.dart'; import 'package:em2rp/models/event_model.dart'; import 'package:em2rp/models/equipment_model.dart'; import 'package:em2rp/models/container_model.dart'; import 'package:em2rp/services/api_service.dart'; import 'package:em2rp/services/data_service.dart'; /// Type de conflit enum ConflictType { equipmentUnavailable, // Équipement non quantifiable utilisé insufficientQuantity, // Quantité insuffisante pour consommable/câble containerFullyUsed, // Boîte complète utilisée containerPartiallyUsed, // Certains équipements de la boîte utilisés } /// Informations sur un conflit de disponibilité class AvailabilityConflict { final String equipmentId; final String equipmentName; final EventModel conflictingEvent; final int overlapDays; final ConflictType type; // Pour les quantités (consommables/câbles) final int? totalQuantity; final int? availableQuantity; final int? requestedQuantity; final int? reservedQuantity; // Pour les boîtes final String? containerId; final String? containerName; final List? conflictingChildrenIds; AvailabilityConflict({ required this.equipmentId, required this.equipmentName, required this.conflictingEvent, required this.overlapDays, this.type = ConflictType.equipmentUnavailable, this.totalQuantity, this.availableQuantity, this.requestedQuantity, this.reservedQuantity, this.containerId, this.containerName, this.conflictingChildrenIds, }); /// Message descriptif du conflit String get conflictMessage { switch (type) { case ConflictType.equipmentUnavailable: return 'Équipement déjà utilisé'; case ConflictType.insufficientQuantity: return 'Stock insuffisant : $availableQuantity/$totalQuantity disponible (demandé: $requestedQuantity)'; case ConflictType.containerFullyUsed: return 'Boîte complète déjà utilisée'; case ConflictType.containerPartiallyUsed: final count = conflictingChildrenIds?.length ?? 0; return '$count équipement(s) de la boîte déjà utilisé(s)'; } } } /// Service pour vérifier la disponibilité du matériel class EventAvailabilityService { final DataService _dataService = DataService(apiService); /// Vérifie si un équipement est disponible pour une plage de dates via Cloud Function Future> checkEquipmentAvailability({ required String equipmentId, required String equipmentName, required DateTime startDate, required DateTime endDate, String? excludeEventId, }) async { final conflicts = []; try { if (kDebugMode) debugPrint('[EventAvailabilityService] Checking availability for equipment $equipmentId ($equipmentName)'); final result = await _dataService.checkEquipmentAvailability( equipmentId: equipmentId, startDate: startDate, endDate: endDate, excludeEventId: excludeEventId, ); final available = result['available'] as bool? ?? true; if (!available) { final conflictsData = result['conflicts'] as List? ?? []; for (final conflictData in conflictsData) { final conflict = conflictData as Map; final eventId = conflict['eventId'] as String; final eventData = conflict['eventData'] as Map?; if (eventData != null && eventData.isNotEmpty) { try { final event = EventModel.fromMap(eventData, eventId); conflicts.add(AvailabilityConflict( equipmentId: equipmentId, equipmentName: equipmentName, conflictingEvent: event, overlapDays: conflict['overlapDays'] as int? ?? 0, )); } catch (e) { if (kDebugMode) debugPrint('[EventAvailabilityService] Error creating EventModel: $e'); } } } } } catch (e) { if (kDebugMode) debugPrint('[EventAvailabilityService] Error checking availability: $e'); } return conflicts; } /// Vérifie la disponibilité pour une liste d'équipements Future>> checkMultipleEquipmentAvailability({ required List equipmentIds, required Map equipmentNames, required DateTime startDate, required DateTime endDate, String? excludeEventId, }) async { final allConflicts = >{}; for (var equipmentId in equipmentIds) { final conflicts = await checkEquipmentAvailability( equipmentId: equipmentId, equipmentName: equipmentNames[equipmentId] ?? equipmentId, startDate: startDate, endDate: endDate, excludeEventId: excludeEventId, ); if (conflicts.isNotEmpty) { allConflicts[equipmentId] = conflicts; } } return allConflicts; } /// Vérifie la disponibilité d'une boîte et de son contenu via le backend Future> checkContainerAvailability({ required ContainerModel container, required List containerEquipment, required DateTime startDate, required DateTime endDate, String? excludeEventId, }) async { final conflicts = []; try { final result = await _dataService.checkContainerAvailability( containerId: container.id, startDate: startDate, endDate: endDate, excludeEventId: excludeEventId, ); final isAvailable = result['isAvailable'] as bool? ?? true; if (!isAvailable) { final conflictType = result['conflictType'] as String?; if (conflictType == 'complete') { final containerConflicts = result['containerConflicts'] as List? ?? []; for (var conflictData in containerConflicts) { final conflict = conflictData as Map; final eventId = conflict['eventId'] as String; final eventDoc = await _dataService.getEvents(); final eventData = (eventDoc['events'] as List).firstWhere((e) => e['id'] == eventId, orElse: () => null); if (eventData != null) { final event = EventModel.fromMap(eventData as Map, eventId); conflicts.add(AvailabilityConflict( equipmentId: container.id, equipmentName: container.name, conflictingEvent: event, overlapDays: conflict['overlapDays'] as int? ?? 0, type: ConflictType.containerFullyUsed, containerId: container.id, containerName: container.name, )); } } } } } catch (e) { if (kDebugMode) debugPrint('[EventAvailabilityService] Error checking container availability: $e'); } return conflicts; } }