import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import '../core/date_keys.dart'; import '../data/focus_seed_store.dart'; import '../models/app_state.dart'; import '../models/focus_session.dart'; import '../models/habit.dart'; import '../models/mood.dart'; import '../models/reflection_entry.dart'; import '../services/analytics_provider.dart'; enum FocusSeedError { openFailed, saveFailed } class FocusSeedController extends ChangeNotifier { FocusSeedController({ FocusSeedStore? store, AnalyticsProvider? analytics, List defaultHabitTitles = const [], }) : _store = store ?? FocusSeedStore(), _analytics = analytics ?? const NoopAnalyticsProvider(), _defaultHabitTitles = List.unmodifiable(defaultHabitTitles); final FocusSeedStore _store; final AnalyticsProvider _analytics; List _defaultHabitTitles; AppState? _state; bool _isLoading = true; bool _isSaving = false; FocusSeedError? _error; AppState? get state => _state; bool get isLoading => _isLoading; bool get isSaving => _isSaving; FocusSeedError? get error => _error; void updateDefaultHabitTitles(List titles) { _defaultHabitTitles = List.unmodifiable(titles); } Future load() async { _isLoading = true; _error = null; notifyListeners(); try { _state = await _store.load(defaultHabitTitles: _defaultHabitTitles); } on Object { _error = FocusSeedError.openFailed; } finally { _isLoading = false; notifyListeners(); } } Future setTodayAnchor(String value) async { final current = _state; final anchor = value.trim(); if (current == null || anchor.isEmpty) { return false; } _analytics.track('intention_saved'); return _commit(current.copyWith(anchor: anchor, anchorDateKey: todayKey())); } Future toggleHabit(Habit habit) async { final current = _state; if (current == null) { return false; } final today = todayKey(); final completedDates = Set.from(habit.completedDates); if (completedDates.contains(today)) { completedDates.remove(today); } else { completedDates.add(today); await HapticFeedback.lightImpact(); } final habits = current.habits .map( (item) => item.id == habit.id ? habit.copyWith(completedDates: completedDates) : item, ) .toList(growable: false); _analytics.track('habit_toggled'); return _commit(current.copyWith(habits: habits)); } Future addHabit(String value) async { final current = _state; final title = value.trim(); if (current == null || title.isEmpty) { return false; } final now = DateTime.now(); final habit = Habit( id: 'habit-${now.microsecondsSinceEpoch}', title: title, createdAt: now, completedDates: const {}, ); _analytics.track('habit_created'); return _commit(current.copyWith(habits: [...current.habits, habit])); } Future renameHabit(Habit habit, String value) async { final current = _state; final title = value.trim(); if (current == null || title.isEmpty) { return false; } final habits = current.habits .map((item) => item.id == habit.id ? item.copyWith(title: title) : item) .toList(growable: false); return _commit(current.copyWith(habits: habits)); } Future deleteHabit(Habit habit) async { final current = _state; if (current == null) { return false; } final habits = current.habits .where((item) => item.id != habit.id) .toList(growable: false); return _commit(current.copyWith(habits: habits)); } Future completeFocusSession(int minutes) async { final current = _state; if (current == null) { return false; } final now = DateTime.now(); final session = FocusSession( id: 'session-${now.microsecondsSinceEpoch}', minutes: minutes, completedAt: now, ); _analytics.track('focus_completed', {'minutes': minutes}); return _commit(current.copyWith(sessions: [...current.sessions, session])); } Future saveReflection(String value, Mood mood) async { final current = _state; final note = value.trim(); if (current == null || note.isEmpty) { return false; } final today = todayKey(); final now = DateTime.now(); final entry = ReflectionEntry( id: 'reflection-${now.microsecondsSinceEpoch}', dateKey: today, note: note, mood: mood, createdAt: now, ); final reflections = [ ...current.reflections.where((item) => item.dateKey != today), entry, ]; _analytics.track('reflection_saved'); return _commit(current.copyWith(reflections: reflections)); } Future importState(String raw) async { try { final decoded = jsonDecode(raw); if (decoded is! Map) { return false; } return _commit(AppState.fromJson(decoded)); } on Object { return false; } } Future clearData() async { return _commit(AppState.starter(defaultHabitTitles: _defaultHabitTitles)); } Future _commit(AppState nextState) async { final previousState = _state; _state = nextState; _isSaving = true; _error = null; notifyListeners(); try { await _store.save(nextState); return true; } on Object { _state = previousState; _error = FocusSeedError.saveFailed; return false; } finally { _isSaving = false; notifyListeners(); } } }