import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/firebase/firebase_providers.dart'; import '../../household/data/household_providers.dart'; import '../../today/domain/due_task.dart'; import '../domain/plant.dart'; /// Firestore-Anbindung der Pflanzen: /// households/{householdId}/plants/{plantId} Plant _plantFromDoc(String id, Map data) { return Plant( id: id, nickname: data['nickname'] as String? ?? '', species: data['species'] as String? ?? '', locationId: data['locationId'] as String?, description: data['description'] as String? ?? '', careNotes: data['careNotes'] as String? ?? '', photoUrl: data['photoUrl'] as String?, wateringIntervalDays: (data['wateringIntervalDays'] as num?)?.toInt() ?? 7, fertilizingIntervalDays: (data['fertilizingIntervalDays'] as num?)?.toInt() ?? 28, lastWatered: (data['lastWatered'] as Timestamp?)?.toDate(), lastFertilized: (data['lastFertilized'] as Timestamp?)?.toDate(), lastWateredBy: data['lastWateredBy'] as String?, lastFertilizedBy: data['lastFertilizedBy'] as String?, repottingIntervalMonths: (data['repottingIntervalMonths'] as num?)?.toInt(), lastRepotted: (data['lastRepotted'] as Timestamp?)?.toDate(), lastRepottedBy: data['lastRepottedBy'] as String?, fitStars: (data['fitStars'] as num?)?.toInt(), fitReasoning: data['fitReasoning'] as String?, fitLocationId: data['fitLocationId'] as String?, fitAssessedAt: (data['fitAssessedAt'] as Timestamp?)?.toDate(), ); } Map _plantToMap(Plant plant) { return { 'nickname': plant.nickname, 'species': plant.species, 'locationId': plant.locationId, 'description': plant.description, 'careNotes': plant.careNotes, 'photoUrl': plant.photoUrl, 'wateringIntervalDays': plant.wateringIntervalDays, 'fertilizingIntervalDays': plant.fertilizingIntervalDays, 'lastWatered': plant.lastWatered != null ? Timestamp.fromDate(plant.lastWatered!) : null, 'lastFertilized': plant.lastFertilized != null ? Timestamp.fromDate(plant.lastFertilized!) : null, 'lastWateredBy': plant.lastWateredBy, 'lastFertilizedBy': plant.lastFertilizedBy, 'repottingIntervalMonths': plant.repottingIntervalMonths, 'lastRepotted': plant.lastRepotted != null ? Timestamp.fromDate(plant.lastRepotted!) : null, 'lastRepottedBy': plant.lastRepottedBy, }; } /// Alle Pflanzen eines bestimmten Haushalts, live aus Firestore. /// Die Tages-Checkliste nutzt das für jeden Haushalt des Nutzers. final householdPlantsProvider = StreamProvider.family, String>((ref, householdId) { return ref .watch(firestoreProvider) .collection('households') .doc(householdId) .collection('plants') .orderBy('nickname') .snapshots() .map((snapshot) => [ for (final doc in snapshot.docs) _plantFromDoc(doc.id, doc.data()), ]); }); /// Die Pflanzen des gerade aktiven Haushalts. /// Leere Liste, solange kein Haushalt geladen ist (z. B. direkt nach Login). final plantsProvider = Provider>>((ref) { final householdId = ref.watch(householdIdProvider).value; if (householdId == null) return const AsyncValue.data([]); return ref.watch(householdPlantsProvider(householdId)); }); final plantByIdProvider = Provider.family((ref, id) { final plants = ref.watch(plantsProvider).value ?? const []; for (final plant in plants) { if (plant.id == id) return plant; } return null; }); class PlantRepository { PlantRepository(this._firestore, this._householdIdGetter, this._userLabelGetter); final FirebaseFirestore _firestore; final String? Function() _householdIdGetter; /// Anzeigename des angemeldeten Nutzers für "bestätigt von". final String? Function() _userLabelGetter; CollectionReference> get _plants { final householdId = _householdIdGetter(); if (householdId == null) { throw StateError('Kein Haushalt geladen – Aktion nicht möglich.'); } return _firestore .collection('households') .doc(householdId) .collection('plants'); } Future addPlant(Plant plant) => _plants.add(_plantToMap(plant)); Future updatePlant(Plant plant) => _plants.doc(plant.id).update(_plantToMap(plant)); Future removePlant(String plantId) => _plants.doc(plantId).delete(); /// Bestätigt eine Aufgabe: setzt das Erledigungsdatum, wodurch sich die /// nächste Fälligkeit automatisch neu berechnet (Intervall-Modell). /// [householdId] kommt aus der Aufgabe – die Checkliste zeigt Aufgaben /// aus allen Haushalten, nicht nur dem aktiven. Future confirmTask( String householdId, String plantId, CareTaskType type) { final field = switch (type) { CareTaskType.watering => 'lastWatered', CareTaskType.fertilizing => 'lastFertilized', CareTaskType.repotting => 'lastRepotted', }; return _firestore .collection('households') .doc(householdId) .collection('plants') .doc(plantId) .update({ field: Timestamp.now(), '${field}By': _userLabelGetter(), }); } /// Speichert das Ergebnis einer Eignungs-Bewertung (V3, auf Abruf) für /// den aktuell zugeordneten Stellplatz. Future saveFitAssessment( String plantId, { required int stars, required String reasoning, required String locationId, }) { return _plants.doc(plantId).update({ 'fitStars': stars, 'fitReasoning': reasoning, 'fitLocationId': locationId, 'fitAssessedAt': Timestamp.now(), }); } } final plantRepositoryProvider = Provider((ref) { return PlantRepository( ref.watch(firestoreProvider), () => ref.read(householdIdProvider).value, () { final email = ref.read(firebaseAuthProvider).currentUser?.email; // "chris@example.com" → "chris" als Anzeigename. return email?.split('@').first; }, ); });