import 'dart:convert'; import 'package:crypto/crypto.dart'; import 'package:drift/drift.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../database/app_database.dart'; import 'database_provider.dart'; // ── Insight type enum ───────────────────────────────────────────────────────── enum InsightType { weekly, monthly, query } // ── Cached insight lookup ───────────────────────────────────────────────────── final cachedInsightProvider = FutureProvider.autoDispose.family((ref, promptHash) { return ref.watch(insightDaoProvider).getCached(promptHash); }); // ── Insight notifier — triggers generation and caches results ───────────────── class InsightNotifier extends Notifier> { @override AsyncValue build() => const AsyncData(null); String _hashPrompt(String prompt) => sha256.convert(utf8.encode(prompt)).toString(); Future generateInsight({ required String prompt, required InsightType type, DateTime? rangeStart, DateTime? rangeEnd, }) async { state = const AsyncLoading(); final hash = _hashPrompt(prompt); final dao = ref.read(insightDaoProvider); final cached = await dao.getCached(hash); if (!ref.mounted) return; if (cached != null) { state = AsyncData(cached.responseText); return; } // Generation happens in LlmNotifier (see llm_provider.dart) state = const AsyncData(null); } Future saveInsight({ required String promptHash, required String responseText, required InsightType type, DateTime? rangeStart, DateTime? rangeEnd, }) async { await ref.read(insightDaoProvider).upsertInsight( LlmInsightsCompanion.insert( type: type.name, promptHash: promptHash, responseText: responseText, rangeStart: Value(rangeStart), rangeEnd: Value(rangeEnd), ), ); if (!ref.mounted) return; state = AsyncData(responseText); } void clear() => state = const AsyncData(null); } final insightNotifierProvider = NotifierProvider.autoDispose>( InsightNotifier.new);