import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import '../models/alert_model.dart'; import '../utils/debug_log.dart'; import 'api_service.dart' show FirebaseFunctionsApiService; /// Service de gestion des alertes /// Architecture simplifiée : le client appelle uniquement les Cloud Functions /// Toute la logique métier est gérée côté backend class AlertService { final FirebaseFirestore _firestore = FirebaseFirestore.instance; final FirebaseAuth _auth = FirebaseAuth.instance; /// Stream des alertes pour l'utilisateur connecté Stream> getAlertsStream() { final user = _auth.currentUser; if (user == null) { DebugLog.info('[AlertService] Pas d\'utilisateur connecté'); return Stream.value([]); } DebugLog.info('[AlertService] Stream alertes pour utilisateur: ${user.uid}'); return _firestore .collection('alerts') .where('assignedTo', arrayContains: user.uid) .where('status', isEqualTo: 'ACTIVE') .orderBy('createdAt', descending: true) .snapshots() .map((snapshot) { final alerts = snapshot.docs .map((doc) => AlertModel.fromFirestore(doc)) .toList(); DebugLog.info('[AlertService] ${alerts.length} alertes actives'); return alerts; }); } /// Récupère les alertes non lues Future> getUnreadAlerts() async { final user = _auth.currentUser; if (user == null) return []; try { final snapshot = await _firestore .collection('alerts') .where('assignedTo', arrayContains: user.uid) .where('isRead', isEqualTo: false) .where('status', isEqualTo: 'ACTIVE') .orderBy('createdAt', descending: true) .get(); return snapshot.docs .map((doc) => AlertModel.fromFirestore(doc)) .toList(); } catch (e) { DebugLog.error('[AlertService] Erreur récupération alertes', e); return []; } } /// Marque une alerte comme lue Future markAsRead(String alertId) async { try { await _firestore.collection('alerts').doc(alertId).update({ 'isRead': true, 'readAt': FieldValue.serverTimestamp(), }); DebugLog.info('[AlertService] Alerte $alertId marquée comme lue'); } catch (e) { DebugLog.error('[AlertService] Erreur marquage alerte', e); rethrow; } } /// Marque toutes les alertes comme lues Future markAllAsRead() async { final user = _auth.currentUser; if (user == null) return; try { final snapshot = await _firestore .collection('alerts') .where('assignedTo', arrayContains: user.uid) .where('isRead', isEqualTo: false) .get(); final batch = _firestore.batch(); for (var doc in snapshot.docs) { batch.update(doc.reference, { 'isRead': true, 'readAt': FieldValue.serverTimestamp(), }); } await batch.commit(); DebugLog.info('[AlertService] ${snapshot.docs.length} alertes marquées comme lues'); } catch (e) { DebugLog.error('[AlertService] Erreur marquage alertes', e); rethrow; } } /// Archive une alerte Future archiveAlert(String alertId) async { try { await _firestore.collection('alerts').doc(alertId).update({ 'status': 'ARCHIVED', 'archivedAt': FieldValue.serverTimestamp(), }); DebugLog.info('[AlertService] Alerte $alertId archivée'); } catch (e) { DebugLog.error('[AlertService] Erreur archivage alerte', e); rethrow; } } /// Crée une alerte manuelle (appelée par l'utilisateur) /// Cette méthode appelle la Cloud Function createAlert Future createManualAlert({ required AlertType type, required AlertSeverity severity, required String message, String? title, String? equipmentId, String? eventId, String? actionUrl, Map? metadata, }) async { try { DebugLog.info('[AlertService] === CRÉATION ALERTE MANUELLE ==='); DebugLog.info('[AlertService] Type: $type'); DebugLog.info('[AlertService] Severity: $severity'); final apiService = FirebaseFunctionsApiService(); final result = await apiService.call( 'createAlert', { 'type': alertTypeToString(type), 'severity': severity.name.toUpperCase(), 'title': title, 'message': message, 'equipmentId': equipmentId, 'eventId': eventId, 'actionUrl': actionUrl, 'metadata': metadata ?? {}, }, ); final alertId = result['alertId'] as String; DebugLog.info('[AlertService] ✓ Alerte créée: $alertId'); return alertId; } catch (e, stackTrace) { DebugLog.error('[AlertService] ❌ Erreur création alerte', e); DebugLog.error('[AlertService] Stack', stackTrace); rethrow; } } /// Stream des alertes pour un utilisateur spécifique Stream> alertsStreamForUser(String userId) { return _firestore .collection('alerts') .where('assignedTo', arrayContains: userId) .where('status', isEqualTo: 'ACTIVE') .orderBy('createdAt', descending: true) .snapshots() .map((snapshot) => snapshot.docs .map((doc) => AlertModel.fromFirestore(doc)) .toList()); } /// Récupère les alertes pour un utilisateur Future> getAlertsForUser(String userId) async { try { final snapshot = await _firestore .collection('alerts') .where('assignedTo', arrayContains: userId) .where('status', isEqualTo: 'ACTIVE') .orderBy('createdAt', descending: true) .get(); return snapshot.docs .map((doc) => AlertModel.fromFirestore(doc)) .toList(); } catch (e) { DebugLog.error('[AlertService] Erreur récupération alertes', e); return []; } } /// Stream du nombre d'alertes non lues pour un utilisateur Stream unreadCountStreamForUser(String userId) { return _firestore .collection('alerts') .where('assignedTo', arrayContains: userId) .where('isRead', isEqualTo: false) .where('status', isEqualTo: 'ACTIVE') .snapshots() .map((snapshot) => snapshot.docs.length); } /// Supprime une alerte Future deleteAlert(String alertId) async { try { await _firestore.collection('alerts').doc(alertId).delete(); DebugLog.info('[AlertService] Alerte $alertId supprimée'); } catch (e) { DebugLog.error('[AlertService] Erreur suppression alerte', e); rethrow; } } /// Crée une alerte de création d'événement Future createEventCreatedAlert({ required String eventId, required String eventName, required DateTime eventDate, }) async { await createManualAlert( type: AlertType.eventCreated, severity: AlertSeverity.info, message: 'Nouvel événement créé: "$eventName" le ${_formatDate(eventDate)}', eventId: eventId, metadata: { 'eventName': eventName, 'eventDate': eventDate.toIso8601String(), }, ); } /// Crée une alerte de modification d'événement Future createEventModifiedAlert({ required String eventId, required String eventName, required String modification, }) async { await createManualAlert( type: AlertType.eventModified, severity: AlertSeverity.info, message: 'Événement "$eventName" modifié: $modification', eventId: eventId, metadata: { 'eventName': eventName, 'modification': modification, }, ); } String _formatDate(DateTime date) { return '${date.day}/${date.month}/${date.year}'; } }