import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; enum ThemeOption { system, light, dark, } class SettingsProvider extends ChangeNotifier { ThemeOption _themeOption = ThemeOption.system; // Time range settings (stored as hours, 0-23) int _morningStart = 5; int _morningEnd = 10; int _afternoonStart = 11; int _afternoonEnd = 16; int _eveningStart = 17; int _eveningEnd = 22; int _nightStart = 23; int _nightEnd = 4; // Notifications int _snoozeMinutes = 10; // Notification retry settings bool _notificationRetryEnabled = true; int _notificationRetryCount = 3; int _notificationRetryDelayMinutes = 5; // Auto-sync settings bool _autoSyncEnabled = false; int _autoSyncDebounceSeconds = 5; ThemeOption get themeOption => _themeOption; // Time range getters int get morningStart => _morningStart; int get morningEnd => _morningEnd; int get afternoonStart => _afternoonStart; int get afternoonEnd => _afternoonEnd; int get eveningStart => _eveningStart; int get eveningEnd => _eveningEnd; int get nightStart => _nightStart; int get nightEnd => _nightEnd; // Notifications int get snoozeMinutes => _snoozeMinutes; // Notification retry getters bool get notificationRetryEnabled => _notificationRetryEnabled; int get notificationRetryCount => _notificationRetryCount; int get notificationRetryDelayMinutes => _notificationRetryDelayMinutes; // Auto-sync getters bool get autoSyncEnabled => _autoSyncEnabled; int get autoSyncDebounceSeconds => _autoSyncDebounceSeconds; // Helper method to get formatted time ranges for display String get morningRange => '${_formatHour(_morningStart)} - ${_formatHour((_morningEnd + 1) % 24)}'; String get afternoonRange => '${_formatHour(_afternoonStart)} - ${_formatHour((_afternoonEnd + 1) % 24)}'; String get eveningRange => '${_formatHour(_eveningStart)} - ${_formatHour((_eveningEnd + 1) % 24)}'; String get nightRange => '${_formatHour(_nightStart)} - ${_formatHour((_nightEnd + 1) % 24)}'; String _formatHour(int hour) { return '${hour.toString().padLeft(2, '0')}:00'; } ThemeMode get themeMode { switch (_themeOption) { case ThemeOption.light: return ThemeMode.light; case ThemeOption.dark: return ThemeMode.dark; case ThemeOption.system: return ThemeMode.system; } } Future initialize() async { final prefs = await SharedPreferences.getInstance(); final themeIndex = prefs.getInt('theme_option') ?? 0; _themeOption = ThemeOption.values[themeIndex]; // Load time range settings _morningStart = prefs.getInt('morning_start') ?? 5; _morningEnd = prefs.getInt('morning_end') ?? 10; _afternoonStart = prefs.getInt('afternoon_start') ?? 11; _afternoonEnd = prefs.getInt('afternoon_end') ?? 16; _eveningStart = prefs.getInt('evening_start') ?? 17; _eveningEnd = prefs.getInt('evening_end') ?? 22; _nightStart = prefs.getInt('night_start') ?? 23; _nightEnd = prefs.getInt('night_end') ?? 4; // Load snooze setting _snoozeMinutes = prefs.getInt('snooze_minutes') ?? 10; // Load notification retry settings _notificationRetryEnabled = prefs.getBool('notification_retry_enabled') ?? true; _notificationRetryCount = prefs.getInt('notification_retry_count') ?? 3; _notificationRetryDelayMinutes = prefs.getInt('notification_retry_delay_minutes') ?? 5; // Load auto-sync settings _autoSyncEnabled = prefs.getBool('auto_sync_enabled') ?? false; _autoSyncDebounceSeconds = prefs.getInt('auto_sync_debounce_seconds') ?? 30; notifyListeners(); } Future setThemeOption(ThemeOption option) async { _themeOption = option; notifyListeners(); final prefs = await SharedPreferences.getInstance(); await prefs.setInt('theme_option', option.index); } Future setDateOfBirthAndGender(DateTime dateOfBirth, String gender) async { notifyListeners(); final prefs = await SharedPreferences.getInstance(); await prefs.setString('date_of_birth', dateOfBirth.toIso8601String()); await prefs.setString('gender', gender); } Future setTimeRanges({ required int morningStart, required int morningEnd, required int afternoonStart, required int afternoonEnd, required int eveningStart, required int eveningEnd, required int nightStart, required int nightEnd, }) async { // Validate ranges don't overlap (simplified validation) if (!_areTimeRangesValid( morningStart, morningEnd, afternoonStart, afternoonEnd, eveningStart, eveningEnd, nightStart, nightEnd, )) { throw ArgumentError('Time ranges overlap or are invalid'); } _morningStart = morningStart; _morningEnd = morningEnd; _afternoonStart = afternoonStart; _afternoonEnd = afternoonEnd; _eveningStart = eveningStart; _eveningEnd = eveningEnd; _nightStart = nightStart; _nightEnd = nightEnd; notifyListeners(); final prefs = await SharedPreferences.getInstance(); await prefs.setInt('morning_start', morningStart); await prefs.setInt('morning_end', morningEnd); await prefs.setInt('afternoon_start', afternoonStart); await prefs.setInt('afternoon_end', afternoonEnd); await prefs.setInt('evening_start', eveningStart); await prefs.setInt('evening_end', eveningEnd); await prefs.setInt('night_start', nightStart); await prefs.setInt('night_end', nightEnd); } bool _areTimeRangesValid( int morningStart, int morningEnd, int afternoonStart, int afternoonEnd, int eveningStart, int eveningEnd, int nightStart, int nightEnd, ) { // Basic validation - ensure start < end for non-wrapping periods if (morningStart > morningEnd) return false; if (afternoonStart > afternoonEnd) return false; if (eveningStart > eveningEnd) return false; // Night can wrap around midnight, so we allow nightStart > nightEnd // Check for overlaps in sequential periods if (morningEnd >= afternoonStart) return false; if (afternoonEnd >= eveningStart) return false; if (eveningEnd >= nightStart) return false; return true; } // Method to determine time category based on current settings String determineTimeCategory(List reminderTimes) { if (reminderTimes.isEmpty) { return 'anytime'; } // Convert reminder times to hours for categorization final hours = reminderTimes.map((time) { final parts = time.split(':'); return int.tryParse(parts[0]) ?? 12; }).toList(); // Count how many times fall into each category int morningCount = 0; int afternoonCount = 0; int eveningCount = 0; int nightCount = 0; for (final hour in hours) { if (hour >= _morningStart && hour <= _morningEnd) { morningCount++; } else if (hour >= _afternoonStart && hour <= _afternoonEnd) { afternoonCount++; } else if (hour >= _eveningStart && hour <= _eveningEnd) { eveningCount++; } else if (_isInNightRange(hour)) { nightCount++; } } // If supplement is taken throughout the day (has times in multiple periods) final periodsCount = (morningCount > 0 ? 1 : 0) + (afternoonCount > 0 ? 1 : 0) + (eveningCount > 0 ? 1 : 0) + (nightCount > 0 ? 1 : 0); if (periodsCount >= 2) { // Categorize based on the earliest reminder time for consistency final earliestHour = hours.reduce((a, b) => a < b ? a : b); if (earliestHour >= _morningStart && earliestHour <= _morningEnd) { return 'morning'; } else if (earliestHour >= _afternoonStart && earliestHour <= _afternoonEnd) { return 'afternoon'; } else if (earliestHour >= _eveningStart && earliestHour <= _eveningEnd) { return 'evening'; } else if (_isInNightRange(earliestHour)) { return 'night'; } } // If all times are in one period, categorize accordingly if (morningCount > 0) { return 'morning'; } else if (afternoonCount > 0) { return 'afternoon'; } else if (eveningCount > 0) { return 'evening'; } else if (nightCount > 0) { return 'night'; } else { return 'anytime'; } } bool _isInNightRange(int hour) { // Night range can wrap around midnight if (_nightStart <= _nightEnd) { // Normal range (doesn't wrap around midnight) return hour >= _nightStart && hour <= _nightEnd; } else { // Wrapping range (e.g., 23:00 to 4:00) return hour >= _nightStart || hour <= _nightEnd; } } // Notifications setters Future setSnoozeMinutes(int minutes) async { const allowed = [5, 10, 15, 20]; if (!allowed.contains(minutes)) { throw ArgumentError('Snooze minutes must be one of ${allowed.join(", ")}'); } _snoozeMinutes = minutes; notifyListeners(); final prefs = await SharedPreferences.getInstance(); await prefs.setInt('snooze_minutes', minutes); } Future setNotificationRetryEnabled(bool enabled) async { _notificationRetryEnabled = enabled; notifyListeners(); final prefs = await SharedPreferences.getInstance(); await prefs.setBool('notification_retry_enabled', enabled); } Future setNotificationRetryCount(int count) async { if (count < 0 || count > 10) { throw ArgumentError('Retry count must be between 0 and 10'); } _notificationRetryCount = count; notifyListeners(); final prefs = await SharedPreferences.getInstance(); await prefs.setInt('notification_retry_count', count); } Future setNotificationRetryDelayMinutes(int minutes) async { const allowed = [1, 2, 3, 5, 10, 15, 20, 30]; if (!allowed.contains(minutes)) { throw ArgumentError('Retry delay must be one of ${allowed.join(", ")} minutes'); } _notificationRetryDelayMinutes = minutes; notifyListeners(); final prefs = await SharedPreferences.getInstance(); await prefs.setInt('notification_retry_delay_minutes', minutes); } // Auto-sync setters Future setAutoSyncEnabled(bool enabled) async { _autoSyncEnabled = enabled; notifyListeners(); final prefs = await SharedPreferences.getInstance(); await prefs.setBool('auto_sync_enabled', enabled); } Future setAutoSyncDebounceSeconds(int seconds) async { _autoSyncDebounceSeconds = seconds; notifyListeners(); final prefs = await SharedPreferences.getInstance(); await prefs.setInt('auto_sync_debounce_seconds', seconds); } }