import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import '../models/supplement.dart'; import '../models/supplement_intake.dart'; import '../services/database_helper.dart'; import '../services/notification_service.dart'; class SupplementProvider with ChangeNotifier { final DatabaseHelper _databaseHelper = DatabaseHelper.instance; final NotificationService _notificationService = NotificationService(); List _supplements = []; List> _todayIntakes = []; List> _monthlyIntakes = []; bool _isLoading = false; Timer? _persistentReminderTimer; List get supplements => _supplements; List> get todayIntakes => _todayIntakes; List> get monthlyIntakes => _monthlyIntakes; bool get isLoading => _isLoading; Future initialize() async { await _notificationService.initialize(); // Set up the callback for handling supplement intake from notifications print('📱 Setting up notification callback...'); _notificationService.setTakeSupplementCallback((supplementId, supplementName, units, unitType) { print('📱 === NOTIFICATION CALLBACK TRIGGERED ==='); print('📱 Supplement ID: $supplementId'); print('📱 Supplement Name: $supplementName'); print('📱 Units: $units'); print('📱 Unit Type: $unitType'); // Record the intake when user taps "Take" on notification recordIntake(supplementId, 0.0, unitsTaken: units); print('📱 Intake recorded successfully'); print('📱 === CALLBACK COMPLETE ==='); if (kDebugMode) { print('📱 Recorded intake from notification: $supplementName ($units $unitType)'); } }); print('📱 Notification callback setup complete'); // Request permissions with error handling try { await _notificationService.requestPermissions(); } catch (e) { if (kDebugMode) { print('Error requesting notification permissions: $e'); } // Continue without notifications rather than crashing } await loadSupplements(); await loadTodayIntakes(); // Reschedule notifications for all active supplements to ensure persistence await _rescheduleAllNotifications(); // Start periodic checking for persistent reminders (every 5 minutes) _startPersistentReminderCheck(); } void _startPersistentReminderCheck() { // Cancel any existing timer _persistentReminderTimer?.cancel(); // Check every 5 minutes for persistent reminders _persistentReminderTimer = Timer.periodic(const Duration(minutes: 5), (timer) async { try { // This will be called from settings provider context, so we need to import it await _checkPersistentReminders(); } catch (e) { if (kDebugMode) { print('Error checking persistent reminders: $e'); } } }); // Also check immediately _checkPersistentReminders(); } Future _checkPersistentReminders() async { // This method will be enhanced to accept settings from the UI layer // For now, we'll check with default settings // In practice, the UI should call checkPersistentRemindersWithSettings if (kDebugMode) { print('📱 Checking persistent reminders with default settings'); } } // Method to be called from UI with actual settings Future checkPersistentRemindersWithSettings({ required bool persistentReminders, required int reminderRetryInterval, required int maxRetryAttempts, }) async { await _notificationService.checkPersistentReminders( persistentReminders, reminderRetryInterval, maxRetryAttempts, ); } @override void dispose() { _persistentReminderTimer?.cancel(); super.dispose(); } Future _rescheduleAllNotifications() async { if (kDebugMode) { print('📱 Rescheduling notifications for all active supplements...'); } for (final supplement in _supplements) { if (supplement.reminderTimes.isNotEmpty) { try { await _notificationService.scheduleSupplementReminders(supplement); } catch (e) { if (kDebugMode) { print('📱 Error rescheduling notifications for ${supplement.name}: $e'); } } } } if (kDebugMode) { print('📱 Finished rescheduling notifications'); } } Future loadSupplements() async { _isLoading = true; notifyListeners(); try { print('Loading supplements from database...'); _supplements = await _databaseHelper.getAllSupplements(); print('Loaded ${_supplements.length} supplements'); for (var supplement in _supplements) { print('Supplement: ${supplement.name}'); } } catch (e) { print('Error loading supplements: $e'); if (kDebugMode) { print('Error loading supplements: $e'); } } finally { _isLoading = false; notifyListeners(); } } Future addSupplement(Supplement supplement) async { try { print('Adding supplement: ${supplement.name}'); final id = await _databaseHelper.insertSupplement(supplement); print('Supplement inserted with ID: $id'); final newSupplement = supplement.copyWith(id: id); // Schedule notifications (skip if there's an error) try { await _notificationService.scheduleSupplementReminders(newSupplement); print('Notifications scheduled'); } catch (notificationError) { print('Warning: Could not schedule notifications: $notificationError'); } await loadSupplements(); print('Supplements reloaded, count: ${_supplements.length}'); } catch (e) { print('Error adding supplement: $e'); if (kDebugMode) { print('Error adding supplement: $e'); } rethrow; } } Future updateSupplement(Supplement supplement) async { try { await _databaseHelper.updateSupplement(supplement); // Reschedule notifications await _notificationService.scheduleSupplementReminders(supplement); await loadSupplements(); } catch (e) { if (kDebugMode) { print('Error updating supplement: $e'); } } } Future deleteSupplement(int id) async { try { await _databaseHelper.deleteSupplement(id); // Cancel notifications await _notificationService.cancelSupplementReminders(id); await loadSupplements(); } catch (e) { if (kDebugMode) { print('Error deleting supplement: $e'); } } } Future recordIntake(int supplementId, double dosage, {double? unitsTaken, String? notes, DateTime? takenAt}) async { try { final intake = SupplementIntake( supplementId: supplementId, takenAt: takenAt ?? DateTime.now(), dosageTaken: dosage, unitsTaken: unitsTaken ?? 1.0, notes: notes, ); await _databaseHelper.insertIntake(intake); await loadTodayIntakes(); // Show confirmation notification final supplement = _supplements.firstWhere((s) => s.id == supplementId); final unitsText = unitsTaken != null && unitsTaken != 1 ? '${unitsTaken.toStringAsFixed(unitsTaken % 1 == 0 ? 0 : 1)} ${supplement.unitType}' : ''; await _notificationService.showInstantNotification( 'Supplement Taken', 'Recorded ${supplement.name}${unitsText.isNotEmpty ? ' - $unitsText' : ''} (${supplement.ingredientsDisplay})', ); } catch (e) { if (kDebugMode) { print('Error recording intake: $e'); } } } Future loadTodayIntakes() async { try { _todayIntakes = await _databaseHelper.getIntakesWithSupplementsForDate(DateTime.now()); notifyListeners(); } catch (e) { if (kDebugMode) { print('Error loading today\'s intakes: $e'); } } } Future loadMonthlyIntakes(int year, int month) async { try { _monthlyIntakes = await _databaseHelper.getIntakesWithSupplementsForMonth(year, month); notifyListeners(); } catch (e) { if (kDebugMode) { print('Error loading monthly intakes: $e'); } } } Future>> getIntakesForDate(DateTime date) async { try { return await _databaseHelper.getIntakesWithSupplementsForDate(date); } catch (e) { if (kDebugMode) { print('Error loading intakes for date: $e'); } return []; } } Future deleteIntake(int intakeId) async { try { await _databaseHelper.deleteIntake(intakeId); await loadTodayIntakes(); // Also refresh monthly intakes if they're loaded if (_monthlyIntakes.isNotEmpty) { await loadMonthlyIntakes(DateTime.now().year, DateTime.now().month); } notifyListeners(); } catch (e) { if (kDebugMode) { print('Error deleting intake: $e'); } } } bool hasBeenTakenToday(int supplementId) { return _todayIntakes.any((intake) => intake['supplement_id'] == supplementId); } int getTodayIntakeCount(int supplementId) { return _todayIntakes.where((intake) => intake['supplement_id'] == supplementId).length; } // Archive functionality List _archivedSupplements = []; List get archivedSupplements => _archivedSupplements; Future loadArchivedSupplements() async { try { _archivedSupplements = await _databaseHelper.getArchivedSupplements(); notifyListeners(); } catch (e) { if (kDebugMode) { print('Error loading archived supplements: $e'); } } } Future archiveSupplement(int supplementId) async { try { await _databaseHelper.archiveSupplement(supplementId); await loadSupplements(); // Refresh active supplements await loadArchivedSupplements(); // Refresh archived supplements } catch (e) { if (kDebugMode) { print('Error archiving supplement: $e'); } } } Future unarchiveSupplement(int supplementId) async { try { await _databaseHelper.unarchiveSupplement(supplementId); await loadSupplements(); // Refresh active supplements await loadArchivedSupplements(); // Refresh archived supplements } catch (e) { if (kDebugMode) { print('Error unarchiving supplement: $e'); } } } Future deleteArchivedSupplement(int supplementId) async { try { await _databaseHelper.deleteSupplement(supplementId); await loadArchivedSupplements(); // Refresh archived supplements } catch (e) { if (kDebugMode) { print('Error deleting archived supplement: $e'); } } } // Debug methods for notification testing Future testNotifications() async { await _notificationService.testNotification(); } Future testScheduledNotification() async { await _notificationService.testScheduledNotification(); } Future testNotificationActions() async { await _notificationService.testNotificationWithActions(); } Future> getPendingNotifications() async { return await _notificationService.getPendingNotifications(); } // Debug method to test notification persistence Future rescheduleAllNotifications() async { await _rescheduleAllNotifications(); } // Debug method to cancel all notifications Future cancelAllNotifications() async { await _notificationService.cancelAllReminders(); } }