import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:drift/drift.dart'; import '../data/db.dart'; export '../data/db.dart' show Account, CategoryGroup, Category, Transaction, Budget; // Singleton DB provider final dbProvider = Provider((ref) { final db = AppDatabase(); ref.onDispose(() => db.close()); db.seedIfEmpty(); return db; }); // ── Accounts ──────────────────────────────────────────────────────────── final accountsProvider = StreamProvider>((ref) { final db = ref.watch(dbProvider); return db.select(db.accounts).watch(); }); final openAccountsProvider = Provider>((ref) { final accounts = ref.watch(accountsProvider).valueOrNull ?? []; return accounts.where((a) => !a.isClosed).toList(); }); final totalBalanceProvider = Provider((ref) { final accounts = ref.watch(openAccountsProvider); return accounts.fold(0.0, (sum, a) => sum + a.balance); }); // ── Category Groups ───────────────────────────────────────────────────── final categoryGroupsProvider = StreamProvider>((ref) { final db = ref.watch(dbProvider); return (db.select(db.categoryGroups) ..orderBy([(g) => OrderingTerm(expression: g.sortOrder)])) .watch(); }); // ── Categories ────────────────────────────────────────────────────────── final categoriesProvider = StreamProvider>((ref) { final db = ref.watch(dbProvider); return (db.select(db.categories) ..orderBy([(c) => OrderingTerm(expression: c.sortOrder)])) .watch(); }); final categoriesByGroupProvider = Provider>>((ref) { final cats = ref.watch(categoriesProvider).valueOrNull ?? []; final map = >{}; for (final c in cats) { map.putIfAbsent(c.groupId, () => []).add(c); } return map; }); // ── Transactions ──────────────────────────────────────────────────────── final allTransactionsProvider = StreamProvider>((ref) { final db = ref.watch(dbProvider); return (db.select(db.transactions) ..orderBy([(t) => OrderingTerm(expression: t.date, mode: OrderingMode.desc)])) .watch(); }); final recentTransactionsProvider = Provider>((ref) { final all = ref.watch(allTransactionsProvider).valueOrNull ?? []; return all.take(10).toList(); }); final monthlyTransactionsProvider = Provider.family, String>((ref, month) { final all = ref.watch(allTransactionsProvider).valueOrNull ?? []; return all.where((t) { final ym = '${t.date.year}-${t.date.month.toString().padLeft(2, '0')}'; return ym == month; }).toList(); }); // ── Budget ────────────────────────────────────────────────────────────── final budgetsProvider = StreamProvider>((ref) { final db = ref.watch(dbProvider); return db.select(db.budgets).watch(); }); final currentMonthProvider = Provider((ref) { final now = DateTime.now(); return '${now.year}-${now.month.toString().padLeft(2, '0')}'; }); final monthlyBudgetProvider = Provider.family((ref, categoryId) { final month = ref.watch(currentMonthProvider); final budgets = ref.watch(budgetsProvider).valueOrNull ?? []; final match = budgets.where( (b) => b.categoryId == categoryId && b.month == month, ); return match.isNotEmpty ? match.first.budgeted : null; }); final categorySpentProvider = Provider.family((ref, categoryId) { final month = ref.watch(currentMonthProvider); final txs = ref.watch(monthlyTransactionsProvider(month)); final filtered = txs.where((t) => t.categoryId == categoryId); return filtered.fold(0.0, (sum, t) => sum + t.amount); }); final categoryRemainingProvider = Provider.family((ref, categoryId) { final budgeted = ref.watch(monthlyBudgetProvider(categoryId)) ?? 0.0; final spent = ref.watch(categorySpentProvider(categoryId)); return budgeted + spent; // spent is negative, so budgeted + spent = remaining });