import 'package:hive_flutter/hive_flutter.dart'; import 'package:flutter/foundation.dart'; import '../models/focus_session.dart'; import '../models/user_progress.dart'; /// Service to manage local storage using Hive class StorageService { static const String _focusSessionBox = 'focus_sessions'; static const String _userProgressBox = 'user_progress'; static const String _progressKey = 'user_progress_key'; // Cache for today's sessions to improve performance List? _todaySessionsCache; DateTime? _cacheDate; // Cache for user progress UserProgress? _userProgressCache; /// Initialize Hive storage service /// /// This method initializes Hive, registers adapters, and opens the focus sessions box. /// It should be called once during app initialization. Future init() async { try { await Hive.initFlutter(); // Register adapters Hive.registerAdapter(FocusSessionAdapter()); Hive.registerAdapter(UserProgressAdapter()); // Open boxes await Hive.openBox(_focusSessionBox); await Hive.openBox(_userProgressBox); if (kDebugMode) { print('StorageService initialized successfully'); } } catch (e) { if (kDebugMode) { print('Failed to initialize StorageService: $e'); } rethrow; } } /// Get the focus sessions box Box get _sessionsBox => Hive.box(_focusSessionBox); /// Get the user progress box Box get _progressBox => Hive.box(_userProgressBox); /// Invalidate the cache when data changes void _invalidateCache() { _todaySessionsCache = null; _cacheDate = null; } /// Invalidate user progress cache void _invalidateProgressCache() { _userProgressCache = null; } // ==================== User Progress Methods ==================== /// Get user progress (creates new one if doesn't exist) UserProgress getUserProgress() { try { // Return cached progress if available if (_userProgressCache != null) { return _userProgressCache!; } // Try to get from box var progress = _progressBox.get(_progressKey); // Create new progress if doesn't exist if (progress == null) { progress = UserProgress(); _progressBox.put(_progressKey, progress); } // Cache and return _userProgressCache = progress; return progress; } catch (e) { if (kDebugMode) { print('Failed to get user progress: $e'); } // Return new progress as fallback return UserProgress(); } } /// Save user progress Future saveUserProgress(UserProgress progress) async { try { await _progressBox.put(_progressKey, progress); _userProgressCache = progress; // Update cache } catch (e) { if (kDebugMode) { print('Failed to save user progress: $e'); } rethrow; } } /// Update user progress with a function Future updateUserProgress(Function(UserProgress) updateFn) async { try { final progress = getUserProgress(); updateFn(progress); await saveUserProgress(progress); } catch (e) { if (kDebugMode) { print('Failed to update user progress: $e'); } rethrow; } } /// Clear user progress (for testing/reset) Future clearUserProgress() async { try { await _progressBox.delete(_progressKey); _invalidateProgressCache(); } catch (e) { if (kDebugMode) { print('Failed to clear user progress: $e'); } rethrow; } } /// Save a focus session to local storage /// /// [session] - The focus session to save /// Returns a Future that completes when the session is saved Future saveFocusSession(FocusSession session) async { try { await _sessionsBox.add(session); _invalidateCache(); // Invalidate cache when data changes } catch (e) { if (kDebugMode) { print('Failed to save focus session: $e'); } rethrow; } } /// Get all focus sessions from local storage /// /// Returns a list of all focus sessions stored locally List getAllSessions() { try { return _sessionsBox.values.toList(); } catch (e) { if (kDebugMode) { print('Failed to get all sessions: $e'); } return []; } } /// Get today's focus sessions with caching /// /// Returns a list of focus sessions that occurred today /// Uses caching to improve performance for frequent calls List getTodaySessions() { try { final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); // Check if cache is valid if (_todaySessionsCache != null && _cacheDate == today) { return _todaySessionsCache!; } // Query and cache results final sessions = _sessionsBox.values.where((session) { final sessionDate = DateTime( session.startTime.year, session.startTime.month, session.startTime.day, ); return sessionDate == today; }).toList(); // Update cache _todaySessionsCache = sessions; _cacheDate = today; return sessions; } catch (e) { if (kDebugMode) { print('Failed to get today\'s sessions: $e'); } return []; } } /// Get total focus minutes for today /// /// Returns the sum of actual minutes focused today int getTodayTotalMinutes() { return getTodaySessions() .fold(0, (sum, session) => sum + session.actualMinutes); } /// Get total distractions for today /// /// Returns the total number of distractions recorded today int getTodayDistractionCount() { return getTodaySessions() .fold(0, (sum, session) => sum + session.distractionCount); } /// Get total completed sessions for today /// /// Returns the number of focus sessions completed today int getTodayCompletedCount() { return getTodaySessions() .where((session) => session.completed) .length; } /// Get total sessions count for today (including stopped early) /// /// Returns the total number of focus sessions started today int getTodaySessionsCount() { return getTodaySessions().length; } /// Delete a focus session from local storage /// /// [session] - The focus session to delete /// Returns a Future that completes when the session is deleted Future deleteSession(FocusSession session) async { try { await session.delete(); _invalidateCache(); // Invalidate cache when data changes } catch (e) { if (kDebugMode) { print('Failed to delete focus session: $e'); } rethrow; } } /// Clear all sessions from local storage (for testing/debugging) /// /// Returns a Future that completes when all sessions are cleared Future clearAllSessions() async { try { await _sessionsBox.clear(); _invalidateCache(); // Invalidate cache when data changes } catch (e) { if (kDebugMode) { print('Failed to clear all sessions: $e'); } rethrow; } } /// Close all Hive boxes /// /// Should be called when the app is closing to properly clean up resources static Future close() async { try { await Hive.close(); } catch (e) { if (kDebugMode) { print('Failed to close Hive boxes: $e'); } rethrow; } } }