import 'package:cloud_firestore/cloud_firestore.dart'; enum EventStatus { confirmed, canceled, waitingForApproval, } String eventStatusToString(EventStatus status) { switch (status) { case EventStatus.confirmed: return 'CONFIRMED'; case EventStatus.canceled: return 'CANCELED'; case EventStatus.waitingForApproval: default: return 'WAITING_FOR_APPROVAL'; } } EventStatus eventStatusFromString(String? status) { switch (status) { case 'CONFIRMED': return EventStatus.confirmed; case 'CANCELED': return EventStatus.canceled; case 'WAITING_FOR_APPROVAL': default: return EventStatus.waitingForApproval; } } enum PreparationStatus { notStarted, inProgress, completed, completedWithMissing } String preparationStatusToString(PreparationStatus status) { switch (status) { case PreparationStatus.notStarted: return 'NOT_STARTED'; case PreparationStatus.inProgress: return 'IN_PROGRESS'; case PreparationStatus.completed: return 'COMPLETED'; case PreparationStatus.completedWithMissing: return 'COMPLETED_WITH_MISSING'; } } PreparationStatus preparationStatusFromString(String? status) { switch (status) { case 'NOT_STARTED': return PreparationStatus.notStarted; case 'IN_PROGRESS': return PreparationStatus.inProgress; case 'COMPLETED': return PreparationStatus.completed; case 'COMPLETED_WITH_MISSING': return PreparationStatus.completedWithMissing; default: return PreparationStatus.notStarted; } } enum ReturnStatus { notStarted, inProgress, completed, completedWithMissing } String returnStatusToString(ReturnStatus status) { switch (status) { case ReturnStatus.notStarted: return 'NOT_STARTED'; case ReturnStatus.inProgress: return 'IN_PROGRESS'; case ReturnStatus.completed: return 'COMPLETED'; case ReturnStatus.completedWithMissing: return 'COMPLETED_WITH_MISSING'; } } ReturnStatus returnStatusFromString(String? status) { switch (status) { case 'NOT_STARTED': return ReturnStatus.notStarted; case 'IN_PROGRESS': return ReturnStatus.inProgress; case 'COMPLETED': return ReturnStatus.completed; case 'COMPLETED_WITH_MISSING': return ReturnStatus.completedWithMissing; default: return ReturnStatus.notStarted; } } class EventEquipment { final String equipmentId; // ID de l'équipement final int quantity; // Quantité (pour consommables) final bool isPrepared; // Validé en préparation final bool isReturned; // Validé au retour final int? returnedQuantity; // Quantité retournée (pour consommables) EventEquipment({ required this.equipmentId, this.quantity = 1, this.isPrepared = false, this.isReturned = false, this.returnedQuantity, }); factory EventEquipment.fromMap(Map map) { return EventEquipment( equipmentId: map['equipmentId'] ?? '', quantity: map['quantity'] ?? 1, isPrepared: map['isPrepared'] ?? false, isReturned: map['isReturned'] ?? false, returnedQuantity: map['returnedQuantity'], ); } Map toMap() { return { 'equipmentId': equipmentId, 'quantity': quantity, 'isPrepared': isPrepared, 'isReturned': isReturned, 'returnedQuantity': returnedQuantity, }; } EventEquipment copyWith({ String? equipmentId, int? quantity, bool? isPrepared, bool? isReturned, int? returnedQuantity, }) { return EventEquipment( equipmentId: equipmentId ?? this.equipmentId, quantity: quantity ?? this.quantity, isPrepared: isPrepared ?? this.isPrepared, isReturned: isReturned ?? this.isReturned, returnedQuantity: returnedQuantity ?? this.returnedQuantity, ); } } class EventModel { final String id; final String name; final String description; final DateTime startDateTime; final DateTime endDateTime; final double basePrice; final int installationTime; final int disassemblyTime; final String eventTypeId; final DocumentReference? eventTypeRef; final String customerId; final String address; final double latitude; final double longitude; final List workforce; final List> documents; final List> options; final EventStatus status; // Nouveaux champs pour la gestion du matériel final List assignedEquipment; final PreparationStatus? preparationStatus; final ReturnStatus? returnStatus; EventModel({ required this.id, required this.name, required this.description, required this.startDateTime, required this.endDateTime, required this.basePrice, required this.installationTime, required this.disassemblyTime, required this.eventTypeId, this.eventTypeRef, required this.customerId, required this.address, required this.latitude, required this.longitude, required this.workforce, required this.documents, this.options = const [], this.status = EventStatus.waitingForApproval, this.assignedEquipment = const [], this.preparationStatus, this.returnStatus, }); factory EventModel.fromMap(Map map, String id) { try { // Gestion sécurisée des références workforce final List workforceRefs = map['workforce'] ?? []; final List safeWorkforce = []; for (var ref in workforceRefs) { if (ref is DocumentReference) { safeWorkforce.add(ref); } else { print('Warning: Invalid workforce reference in event $id: $ref'); } } // Gestion sécurisée des timestamps final Timestamp? startTimestamp = map['StartDateTime'] as Timestamp?; final Timestamp? endTimestamp = map['EndDateTime'] as Timestamp?; final DateTime startDate = startTimestamp?.toDate() ?? DateTime.now(); final DateTime endDate = endTimestamp?.toDate() ?? startDate.add(const Duration(hours: 1)); // Gestion sécurisée des documents final docsRaw = map['documents'] ?? []; final List> docs = []; if (docsRaw is List) { for (var e in docsRaw) { try { if (e is Map) { docs.add(Map.from(e)); } else if (e is String) { final fileName = Uri.decodeComponent( e.split('/').last.split('?').first, ); docs.add({'name': fileName, 'url': e}); } } catch (docError) { print('Warning: Failed to parse document in event $id: $docError'); } } } // Gestion sécurisée des options final optionsRaw = map['options'] ?? []; final List> options = []; if (optionsRaw is List) { for (var e in optionsRaw) { try { if (e is Map) { options.add(Map.from(e)); } } catch (optionError) { print('Warning: Failed to parse option in event $id: $optionError'); } } } // Gestion sécurisée de l'EventType String eventTypeId = ''; DocumentReference? eventTypeRef; if (map['EventType'] is DocumentReference) { eventTypeRef = map['EventType'] as DocumentReference; eventTypeId = eventTypeRef.id; } else if (map['EventType'] is String) { eventTypeId = map['EventType'] as String; } // Gestion sécurisée du customer String customerId = ''; if (map['customer'] is DocumentReference) { customerId = (map['customer'] as DocumentReference).id; } else if (map['customer'] is String) { customerId = map['customer'] as String; } // Gestion des équipements assignés final assignedEquipmentRaw = map['assignedEquipment'] ?? []; final List assignedEquipment = []; if (assignedEquipmentRaw is List) { for (var e in assignedEquipmentRaw) { try { if (e is Map) { assignedEquipment.add(EventEquipment.fromMap(Map.from(e))); } } catch (equipmentError) { print('Warning: Failed to parse equipment in event $id: $equipmentError'); } } } return EventModel( id: id, name: (map['Name'] ?? '').toString().trim(), description: (map['Description'] ?? '').toString(), startDateTime: startDate, endDateTime: endDate, basePrice: _parseDouble(map['BasePrice'] ?? map['Price'] ?? 0.0), installationTime: _parseInt(map['InstallationTime'] ?? 0), disassemblyTime: _parseInt(map['DisassemblyTime'] ?? 0), eventTypeId: eventTypeId, eventTypeRef: eventTypeRef, customerId: customerId, address: (map['Address'] ?? '').toString(), latitude: _parseDouble(map['Latitude'] ?? 0.0), longitude: _parseDouble(map['Longitude'] ?? 0.0), workforce: safeWorkforce, documents: docs, options: options, status: eventStatusFromString(map['status'] as String?), assignedEquipment: assignedEquipment, preparationStatus: preparationStatusFromString(map['preparationStatus'] as String?), returnStatus: returnStatusFromString(map['returnStatus'] as String?), ); } catch (e) { print('Error parsing event $id: $e'); print('Event data: $map'); rethrow; } } // Méthodes utilitaires pour le parsing sécurisé static double _parseDouble(dynamic value) { if (value is double) return value; if (value is int) return value.toDouble(); if (value is String) { final parsed = double.tryParse(value); if (parsed != null) return parsed; } return 0.0; } static int _parseInt(dynamic value) { if (value is int) return value; if (value is double) return value.toInt(); if (value is String) { final parsed = int.tryParse(value); if (parsed != null) return parsed; } return 0; } Map toMap() { return { 'Name': name, 'Description': description, 'StartDateTime': Timestamp.fromDate(startDateTime), 'EndDateTime': Timestamp.fromDate(endDateTime), 'BasePrice': basePrice, 'InstallationTime': installationTime, 'DisassemblyTime': disassemblyTime, 'EventType': eventTypeId.isNotEmpty ? FirebaseFirestore.instance.collection('eventTypes').doc(eventTypeId) : null, 'customer': customerId.isNotEmpty ? FirebaseFirestore.instance.collection('customers').doc(customerId) : null, 'Address': address, 'Position': GeoPoint(latitude, longitude), 'Latitude': latitude, 'Longitude': longitude, 'workforce': workforce, 'documents': documents, 'options': options, 'status': eventStatusToString(status), 'assignedEquipment': assignedEquipment.map((e) => e.toMap()).toList(), 'preparationStatus': preparationStatus != null ? preparationStatusToString(preparationStatus!) : null, 'returnStatus': returnStatus != null ? returnStatusToString(returnStatus!) : null, }; } }