import 'dart:convert'; import 'package:cloud_firestore/cloud_firestore.dart'; /// Model representing a travel trip in the application. /// /// This class encapsulates all trip-related information including dates, /// location, participants, and budget details. It provides serialization /// methods for Firebase operations and supports trip lifecycle management. class Trip { /// Unique identifier for the trip (usually Firestore document ID). final String? id; /// Title or name of the trip. final String title; /// Detailed description of the trip. final String description; /// Trip destination or location. final String location; /// Trip start date and time. final DateTime startDate; /// Trip end date and time. final DateTime endDate; /// Optional budget for the trip in the local currency. final double? budget; /// List of participant user IDs. final List participants; /// User ID of the trip creator. final String createdBy; /// Timestamp when the trip was created. final DateTime createdAt; /// Timestamp when the trip was last updated. final DateTime updatedAt; /// Current status of the trip (e.g., 'draft', 'active', 'completed'). final String status; final String? imageUrl; /// Creates a new [Trip] instance. /// /// Most fields are required except [id] and [budget]. /// [status] defaults to 'draft' for new trips. Trip({ this.id, required this.title, required this.description, required this.location, required this.startDate, required this.endDate, this.budget, required this.participants, required this.createdBy, required this.createdAt, required this.updatedAt, this.status = 'draft', this.imageUrl, }); // NOUVELLE MÉTHODE HELPER pour convertir n'importe quel format de date static DateTime _parseDateTime(dynamic value) { if (value == null) return DateTime.now(); // Si c'est déjà un Timestamp Firebase if (value is Timestamp) { return value.toDate(); } // Si c'est un int (millisecondes depuis epoch) if (value is int) { return DateTime.fromMillisecondsSinceEpoch(value); } // Si c'est un String (ISO 8601) if (value is String) { return DateTime.parse(value); } // Si c'est déjà un DateTime if (value is DateTime) { return value; } // Par défaut return DateTime.now(); } // Constructeur pour créer un Trip depuis un Map (utile pour Firebase) factory Trip.fromMap(Map map, String id) { try { return Trip( id: id, title: map['title'] as String? ?? '', description: map['description'] as String? ?? '', location: map['location'] as String? ?? '', startDate: _parseDateTime(map['startDate']), endDate: _parseDateTime(map['endDate']), budget: (map['budget'] as num?)?.toDouble(), createdBy: map['createdBy'] as String? ?? '', participants: List.from(map['participants'] as List? ?? []), createdAt: _parseDateTime(map['createdAt']), updatedAt: _parseDateTime(map['updatedAt']), status: map['status'] as String? ?? 'draft', imageUrl: map['imageUrl'] as String?, ); } catch (e) { rethrow; } } // MODIFIÉ : Convertir en Map avec Timestamp pour Firestore Map toMap() { return { 'title': title, 'description': description, 'location': location, 'startDate': Timestamp.fromDate(startDate), 'endDate': Timestamp.fromDate(endDate), 'budget': budget, 'participants': participants, 'createdBy': createdBy, 'createdAt': Timestamp.fromDate(createdAt), 'updatedAt': Timestamp.fromDate(updatedAt), 'status': status, 'imageUrl': imageUrl, }; } // Méthode pour convertir un Trip en JSON String toJson() { return json.encode(toMap()); } // Méthode pour créer une copie avec des modifications Trip copyWith({ String? id, String? title, String? description, String? location, DateTime? startDate, DateTime? endDate, double? budget, List? participants, String? createdBy, DateTime? createdAt, DateTime? updatedAt, String? status, String? imageUrl, }) { return Trip( id: id ?? this.id, title: title ?? this.title, description: description ?? this.description, location: location ?? this.location, startDate: startDate ?? this.startDate, endDate: endDate ?? this.endDate, budget: budget ?? this.budget, participants: participants ?? this.participants, createdBy: createdBy ?? this.createdBy, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, status: status ?? this.status, imageUrl: imageUrl ?? this.imageUrl, ); } // Méthode pour obtenir la durée du voyage en jours int get durationInDays { return endDate.difference(startDate).inDays + 1; } // Méthode pour vérifier si le voyage est en cours bool get isActive { final now = DateTime.now(); return status == 'active' && now.isAfter(startDate) && now.isBefore(endDate.add(Duration(days: 1))); } // Méthode pour vérifier si le voyage est à venir bool get isUpcoming { final now = DateTime.now(); return status == 'active' && now.isBefore(startDate); } // Méthode pour vérifier si le voyage est terminé bool get isCompleted { final now = DateTime.now(); return status == 'completed' || (status == 'active' && now.isAfter(endDate)); } // Méthode pour obtenir le budget par participant double? get budgetPerParticipant { if (budget == null || participants.isEmpty) return null; return budget! / (participants.length + 1); } // Méthode pour obtenir le nombre total de participants (incluant le créateur) int get totalParticipants { return participants.length + 1; } // Méthode pour formater les dates String get formattedDates { return '${startDate.day}/${startDate.month}/${startDate.year} - ${endDate.day}/${endDate.month}/${endDate.year}'; } // Méthode pour obtenir le statut formaté String get formattedStatus { switch (status) { case 'draft': return 'Brouillon'; case 'active': return 'Actif'; case 'completed': return 'Terminé'; case 'cancelled': return 'Annulé'; default: return 'Inconnu'; } } @override String toString() { return 'Trip(id: $id, title: $title, location: $location, status: $status)'; } @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is Trip && other.id == id; } @override int get hashCode => id.hashCode; }