import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:hive_flutter/hive_flutter.dart'; import '../models/user_profile.dart'; import '../models/cycle_entry.dart'; import '../services/cycle_service.dart'; import '../services/sync_service.dart'; import 'prayer_provider.dart'; import 'teaching_plan_provider.dart'; import '../models/prayer_request.dart'; import '../models/teaching_plan.dart'; /// Provider for the user profile final userProfileProvider = StateNotifierProvider((ref) { return UserProfileNotifier(); }); /// Notifier for the user profile class UserProfileNotifier extends StateNotifier { UserProfileNotifier() : super(null) { _loadProfile(); } void _loadProfile() { final box = Hive.box('user_profile_v2'); state = box.get('current_user'); } Future updateProfile(UserProfile profile) async { final box = Hive.box('user_profile_v2'); await box.put('current_user', profile); state = profile; } Future updateThemeMode(AppThemeMode themeMode) async { if (state != null) { await updateProfile(state!.copyWith(themeMode: themeMode)); } } Future updateAccentColor(String accentColor) async { if (state != null) { await updateProfile(state!.copyWith(accentColor: accentColor)); } } Future updateRelationshipStatus( RelationshipStatus relationshipStatus) async { if (state != null) { await updateProfile( state!.copyWith(relationshipStatus: relationshipStatus)); } } Future clearProfile() async { final box = Hive.box('user_profile_v2'); await box.clear(); state = null; } } /// Provider for cycle entries final cycleEntriesProvider = StateNotifierProvider>((ref) { return CycleEntriesNotifier(ref); }); /// Notifier for cycle entries class CycleEntriesNotifier extends StateNotifier> { final Ref ref; Timer? _syncTimer; CycleEntriesNotifier(this.ref) : super([]) { _loadEntries(); syncData(); // Auto-sync on load // Start periodic sync every 10 seconds _syncTimer = Timer.periodic(const Duration(seconds: 10), (timer) { _pull(); }); } @override void dispose() { _syncTimer?.cancel(); super.dispose(); } void _loadEntries() { final box = Hive.box('cycle_entries_v2'); state = box.values.toList()..sort((a, b) => b.date.compareTo(a.date)); } Future addEntry(CycleEntry entry) async { final box = Hive.box('cycle_entries_v2'); await box.put(entry.id, entry); _loadEntries(); _push(); } Future updateEntry(CycleEntry entry) async { final box = Hive.box('cycle_entries_v2'); await box.put(entry.id, entry); _loadEntries(); _push(); } Future deleteEntry(String id) async { final box = Hive.box('cycle_entries_v2'); await box.delete(id); _loadEntries(); _push(); } Future deleteEntriesForMonth(int year, int month) async { final box = Hive.box('cycle_entries_v2'); final keysToDelete = []; for (var entry in box.values) { if (entry.date.year == year && entry.date.month == month) { keysToDelete.add(entry.key); } } await box.deleteAll(keysToDelete); _loadEntries(); _push(); } Future clearEntries() async { final box = Hive.box('cycle_entries_v2'); await box.clear(); state = []; _push(); } // Sync Logic Future syncData() async { await _pull(); // After pull, we might want to push any local changes not in remote? // For now, simpler consistency: Pull then Push current state? // Or just Pull. Push happens on edit. // Let's just Pull. } Future _push() async { final userBox = Hive.box('user_profile_v2'); final user = userBox.get('current_user'); // Read current state from other providers // Note: This relies on the providers being initialized. final plans = ref.read(teachingPlansProvider); final prayers = ref.read(prayerRequestsProvider); if (user != null) { final userDetails = { 'name': user.name, 'role': user.role.name, 'partnerId': user.partnerId, 'createdAt': user.createdAt.toIso8601String(), // Add other relevant fields if needed for display on partner's side }; await SyncService().pushSyncData( userId: user.id, entries: state, teachingPlans: plans, prayerRequests: prayers, userDetails: userDetails, ); } } Future _pull() async { final userBox = Hive.box('user_profile_v2'); final user = userBox.get('current_user'); if (user == null) return; final syncResult = await SyncService().pullSyncData( user.id, partnerId: user.partnerId, ); // 0. Check for Server-Side Profile Updates (Auto-Link) if (syncResult.containsKey('userProfile')) { final serverProfile = syncResult['userProfile'] as Map; final serverPartnerId = serverProfile['partnerId']; // If server has a partner ID and we don't (or it's different), update! if (serverPartnerId != null && serverPartnerId != user.partnerId) { // Update local profile final updatedProfile = user.copyWith(partnerId: serverPartnerId); await Hive.box('user_profile_v2') .put('current_user', updatedProfile); // Refresh provider state if needed, but important is to RE-SYNC with new ID // so we get the partner's data immediately. return _pull(); // Recursive call will now use new partnerId } } final remoteEntries = syncResult['entries'] as List? ?? []; final remotePlans = syncResult['teachingPlans'] as List? ?? []; final remotePrayers = syncResult['prayerRequests'] as List? ?? []; // 1. Cycle Entries if (remoteEntries.isNotEmpty) { final box = Hive.box('cycle_entries_v2'); // Simple merge: remote wins for id collisions for (var entry in remoteEntries) { await box.put(entry.id, entry); } _loadEntries(); } // 2. Teaching Plans if (remotePlans.isNotEmpty) { final box = Hive.box('teaching_plans_v2'); for (var plan in remotePlans) { await box.put(plan.id, plan); } // Refresh provider ref.invalidate(teachingPlansProvider); } // 3. Prayer Requests if (remotePrayers.isNotEmpty) { final box = Hive.box('prayer_requests_v2'); for (var req in remotePrayers) { await box.put(req.id, req); } // Refresh provider ref.invalidate(prayerRequestsProvider); } } // Example data generation removed } /// Computed provider for current cycle info final currentCycleInfoProvider = Provider((ref) { final user = ref.watch(userProfileProvider); final entries = ref.watch(cycleEntriesProvider); return CycleService.calculateCycleInfo(user, entries); });