import 'package:flutter/foundation.dart'; import 'package:em2rp/models/container_model.dart'; import 'package:em2rp/models/equipment_model.dart'; import 'package:em2rp/services/container_service.dart'; import 'package:em2rp/services/data_service.dart'; import 'package:em2rp/services/api_service.dart'; class ContainerProvider with ChangeNotifier { final ContainerService _containerService = ContainerService(); final DataService _dataService = DataService(FirebaseFunctionsApiService()); List _containers = []; ContainerType? _selectedType; EquipmentStatus? _selectedStatus; String _searchQuery = ''; bool _isLoading = false; bool _isInitialized = false; List get containers => _containers; ContainerType? get selectedType => _selectedType; EquipmentStatus? get selectedStatus => _selectedStatus; String get searchQuery => _searchQuery; bool get isLoading => _isLoading; bool get isInitialized => _isInitialized; /// S'assure que les conteneurs sont chargés (charge si nécessaire) Future ensureLoaded() async { if (_isInitialized || _isLoading) { print('[ContainerProvider] Containers already loaded or loading, skipping...'); return; } print('[ContainerProvider] Containers not loaded, loading now...'); await loadContainers(); } /// Charger tous les containers via l'API Future loadContainers() async { _isLoading = true; notifyListeners(); try { final containers = await _containerService.getContainers( type: _selectedType, status: _selectedStatus, searchQuery: _searchQuery, ); _containers = containers; _isLoading = false; _isInitialized = true; notifyListeners(); } catch (e) { print('Error loading containers: $e'); _isLoading = false; notifyListeners(); } } /// Récupérer les containers avec filtres appliqués Future> getContainers() async { return await _containerService.getContainers( type: _selectedType, status: _selectedStatus, searchQuery: _searchQuery, ); } /// Stream des containers - retourne un stream depuis les données en cache /// Pour compatibilité avec les widgets existants qui utilisent StreamBuilder Stream> get containersStream async* { // Si les données ne sont pas chargées, charger d'abord if (!_isInitialized) { await loadContainers(); } // Émettre les données actuelles yield _containers; // Continuer à émettre les mises à jour du cache // Note: Pour un vrai temps réel, il faudrait implémenter un StreamController // et notifier quand les données changent } /// Définir le type sélectionné /// Définir le type sélectionné void setSelectedType(ContainerType? type) { _selectedType = type; notifyListeners(); } /// Définir le statut sélectionné void setSelectedStatus(EquipmentStatus? status) { _selectedStatus = status; notifyListeners(); } /// Définir la requête de recherche void setSearchQuery(String query) { _searchQuery = query; notifyListeners(); } /// Créer un nouveau container Future createContainer(ContainerModel container) async { await _containerService.createContainer(container); notifyListeners(); } /// Mettre à jour un container Future updateContainer(String id, Map data) async { await _containerService.updateContainer(id, data); notifyListeners(); } /// Supprimer un container Future deleteContainer(String id) async { await _containerService.deleteContainer(id); notifyListeners(); } /// Récupérer un container par ID Future getContainerById(String id) async { return await _containerService.getContainerById(id); } /// Charge plusieurs conteneurs par leurs IDs (optimisé pour les détails d'événement) Future> getContainersByIds(List containerIds) async { if (containerIds.isEmpty) return []; print('[ContainerProvider] Loading ${containerIds.length} containers by IDs...'); try { // Vérifier d'abord le cache local final cachedContainers = []; final missingIds = []; for (final id in containerIds) { final cached = _containers.firstWhere( (c) => c.id == id, orElse: () => ContainerModel( id: '', name: '', type: ContainerType.flightCase, status: EquipmentStatus.available, equipmentIds: [], createdAt: DateTime.now(), updatedAt: DateTime.now(), ), ); if (cached.id.isNotEmpty) { cachedContainers.add(cached); } else { missingIds.add(id); } } print('[ContainerProvider] Found ${cachedContainers.length} in cache, ${missingIds.length} missing'); // Si tous sont en cache, retourner directement if (missingIds.isEmpty) { return cachedContainers; } // Charger les manquants depuis l'API final containersData = await _dataService.getContainersByIds(missingIds); final loadedContainers = containersData.map((data) { return ContainerModel.fromMap(data, data['id'] as String); }).toList(); // Ajouter au cache for (final container in loadedContainers) { if (!_containers.any((c) => c.id == container.id)) { _containers.add(container); } } print('[ContainerProvider] Loaded ${loadedContainers.length} containers from API'); // Retourner tous les conteneurs (cache + chargés) return [...cachedContainers, ...loadedContainers]; } catch (e) { print('[ContainerProvider] Error loading containers by IDs: $e'); rethrow; } } /// Ajouter un équipement à un container Future> addEquipmentToContainer({ required String containerId, required String equipmentId, String? userId, }) async { final result = await _containerService.addEquipmentToContainer( containerId: containerId, equipmentId: equipmentId, userId: userId, ); notifyListeners(); return result; } /// Retirer un équipement d'un container Future removeEquipmentFromContainer({ required String containerId, required String equipmentId, String? userId, }) async { await _containerService.removeEquipmentFromContainer( containerId: containerId, equipmentId: equipmentId, userId: userId, ); notifyListeners(); } /// Vérifier la disponibilité d'un container Future> checkContainerAvailability({ required String containerId, required DateTime startDate, required DateTime endDate, String? excludeEventId, }) async { return await _containerService.checkContainerAvailability( containerId: containerId, startDate: startDate, endDate: endDate, excludeEventId: excludeEventId, ); } /// Récupérer les équipements d'un container Future> getContainerEquipment(String containerId) async { return await _containerService.getContainerEquipment(containerId); } /// Trouver tous les containers contenant un équipement Future> findContainersWithEquipment(String equipmentId) async { return await _containerService.findContainersWithEquipment(equipmentId); } /// Vérifier si un ID existe Future checkContainerIdExists(String id) async { return await _containerService.checkContainerIdExists(id); } /// Générer un ID unique pour un container /// Format: BOX_{TYPE}_{NAME}_{NUMBER} static String generateContainerId({ required ContainerType type, required String name, int? number, }) { // Obtenir le type en majuscules final typeStr = containerTypeToString(type); // Nettoyer le nom (enlever espaces, caractères spéciaux) final cleanName = name .replaceAll(' ', '_') .replaceAll(RegExp(r'[^a-zA-Z0-9_-]'), '') .toUpperCase(); if (number != null) { return 'BOX_${typeStr}_${cleanName}_#$number'; } return 'BOX_${typeStr}_$cleanName'; } /// Assurer l'unicité d'un ID de container static Future ensureUniqueContainerId( String baseId, ContainerService service, ) async { String uniqueId = baseId; int counter = 1; while (await service.checkContainerIdExists(uniqueId)) { uniqueId = '${baseId}_$counter'; counter++; } return uniqueId; } }