import 'dart:async'; import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:intl/intl.dart'; import '../models/drink_type.dart'; class HydrationService extends ChangeNotifier { final Box _dataBox = Hive.box('hydrationData'); final Box _settingsBox = Hive.box('userSettings'); double _currentIntake = 0.0; double _purityScore = 1.0; DateTime _lastDrinkTime = DateTime.now(); Map _breakdown = { DrinkType.Water: 0, DrinkType.Coffee: 0, DrinkType.Tea: 0, DrinkType.Soda: 0, }; double _weight = 60.0; double _workoutMinutes = 0.0; bool _remindersEnabled = true; double _reminderInterval = 60.0; List _history = []; Timer? _decayTimer; HydrationService() { _loadData(); _startDecayCheck(); } double get currentIntake => _currentIntake; double get dailyGoal => (_weight * 35.0) + (_workoutMinutes * 8.0); double get percentage => (_currentIntake / dailyGoal).clamp(0.0, 1.0); List get history => _history; double get weight => _weight; double get workoutMinutes => _workoutMinutes; bool get remindersEnabled => _remindersEnabled; double get reminderInterval => _reminderInterval; Map get breakdown => _breakdown; Color get dynamicWaterColor { Color baseColor = const Color(0xFF00BFFF); Color impurityColor = const Color(0xFFD2B48C); Color blendedWithImpurity = Color.lerp(impurityColor, baseColor, _purityScore)!; final minutesSince = DateTime.now().difference(_lastDrinkTime).inMinutes; double freshness = 1.0; if (minutesSince > 120) { double overdueHours = (minutesSince - 120) / 60.0; freshness = (1.0 - (overdueHours * 0.2)).clamp(0.4, 1.0); } Color dryColor = Colors.grey.shade400; return Color.lerp(dryColor, blendedWithImpurity, freshness)!; } String get statusText { final minutesSince = DateTime.now().difference(_lastDrinkTime).inMinutes; if (minutesSince > 120) return "Body is withering..."; if (_purityScore < 0.7) return "Purify your system"; return "Metabolism Optimal"; } void _loadData() { final todayKey = DateFormat('yyyyMMdd').format(DateTime.now()); _currentIntake = _dataBox.get(todayKey, defaultValue: 0.0); _purityScore = _dataBox.get('purity_$todayKey', defaultValue: 1.0); int? lastTs = _dataBox.get('last_drink_ts'); _lastDrinkTime = lastTs != null ? DateTime.fromMillisecondsSinceEpoch(lastTs) : DateTime.now(); _weight = _settingsBox.get('weight', defaultValue: 60.0); _workoutMinutes = _settingsBox.get('workout', defaultValue: 0.0); _remindersEnabled = _settingsBox.get('reminders', defaultValue: true); _reminderInterval = _settingsBox.get('reminderInterval', defaultValue: 60.0); if (_history.isEmpty) { for (int i = 0; i < 7; i++) { _history.add(1500 + math.Random().nextInt(1500).toDouble()); } } _breakdown = { DrinkType.Water: _currentIntake * 0.7, DrinkType.Coffee: _currentIntake * 0.2, DrinkType.Tea: _currentIntake * 0.1, DrinkType.Soda: 0, }; notifyListeners(); } void updateSettings({double? weight, double? workout, bool? reminders, double? interval}) { if (weight != null) _weight = weight; if (workout != null) _workoutMinutes = workout; if (reminders != null) _remindersEnabled = reminders; if (interval != null) _reminderInterval = interval; _settingsBox.put('weight', _weight); _settingsBox.put('workout', _workoutMinutes); _settingsBox.put('reminders', _remindersEnabled); _settingsBox.put('reminderInterval', _reminderInterval); notifyListeners(); } void addWater(double amount, {DrinkType type = DrinkType.Water}) { double efficientAmount = amount * type.hydrationFactor; _currentIntake += efficientAmount; _breakdown[type] = (_breakdown[type] ?? 0) + efficientAmount; if (type == DrinkType.Water) { _purityScore = (_purityScore + 0.15).clamp(0.0, 1.0); } else { _purityScore = (_purityScore - (1.0 - type.purityFactor)).clamp(0.0, 1.0); } _lastDrinkTime = DateTime.now(); final todayKey = DateFormat('yyyyMMdd').format(DateTime.now()); _dataBox.put(todayKey, _currentIntake); _dataBox.put('purity_$todayKey', _purityScore); _dataBox.put('last_drink_ts', _lastDrinkTime.millisecondsSinceEpoch); notifyListeners(); } void _startDecayCheck() { _decayTimer = Timer.periodic(const Duration(minutes: 1), (timer) { notifyListeners(); }); } @override void dispose() { _decayTimer?.cancel(); super.dispose(); } }