import 'package:drift/drift.dart'; import '../../../core/database/database.dart'; import '../domain/daily_plan_models.dart'; part 'daily_plan_dao.g.dart'; @DriftAccessor(tables: [Tasks, Rooms, TaskCompletions]) class DailyPlanDao extends DatabaseAccessor with _$DailyPlanDaoMixin { DailyPlanDao(super.attachedDatabase); /// Watch all tasks joined with room name, sorted by nextDueDate ascending. /// Includes ALL tasks (overdue, today, future) -- filtering is done in the /// provider layer to avoid multiple queries. Stream> watchAllTasksWithRoomName() { final query = select(tasks).join([ innerJoin(rooms, rooms.id.equalsExp(tasks.roomId)), ]); query.orderBy([OrderingTerm.asc(tasks.nextDueDate)]); return query.watch().map((rows) { return rows.map((row) { final task = row.readTable(tasks); final room = row.readTable(rooms); return TaskWithRoom( task: task, roomName: room.name, roomId: room.id, ); }).toList(); }); } /// Count task completions recorded today. /// Uses customSelect with readsFrom for proper stream invalidation. Stream watchCompletionsToday({DateTime? today}) { final now = today ?? DateTime.now(); final startOfDay = DateTime(now.year, now.month, now.day); final endOfDay = startOfDay.add(const Duration(days: 1)); return customSelect( 'SELECT COUNT(*) AS c FROM task_completions ' 'WHERE completed_at >= ? AND completed_at < ?', variables: [ Variable(startOfDay.millisecondsSinceEpoch ~/ 1000), Variable(endOfDay.millisecondsSinceEpoch ~/ 1000), ], readsFrom: {taskCompletions}, ).watchSingle().map((row) => row.read('c')); } }