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<AppDatabase>
    with _$DailyPlanDaoMixin {
  DailyPlanDao(super.attachedDatabase);

  /// Watch all active tasks joined with room name, sorted by nextDueDate ascending.
  /// Includes overdue, today, and future tasks -- filtering is done in the
  /// provider layer to avoid multiple queries. Excludes soft-deleted tasks.
  Stream<List<TaskWithRoom>> watchAllTasksWithRoomName() {
    final query = select(tasks).join([
      innerJoin(rooms, rooms.id.equalsExp(tasks.roomId)),
    ]);
    query.where(tasks.isActive.equals(true));
    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();
    });
  }

  /// One-shot count of overdue + today active tasks (for notification body).
  Future<int> getOverdueAndTodayTaskCount({DateTime? today}) async {
    final now = today ?? DateTime.now();
    final endOfToday = DateTime(now.year, now.month, now.day + 1);
    final result = await (selectOnly(tasks)
          ..addColumns([tasks.id.count()])
          ..where(
            tasks.nextDueDate.isSmallerThanValue(endOfToday) &
                tasks.isActive.equals(true),
          ))
        .getSingle();
    return result.read(tasks.id.count()) ?? 0;
  }

  /// One-shot count of overdue active tasks only (for notification body split).
  Future<int> getOverdueTaskCount({DateTime? today}) async {
    final now = today ?? DateTime.now();
    final startOfToday = DateTime(now.year, now.month, now.day);
    final result = await (selectOnly(tasks)
          ..addColumns([tasks.id.count()])
          ..where(
            tasks.nextDueDate.isSmallerThanValue(startOfToday) &
                tasks.isActive.equals(true),
          ))
        .getSingle();
    return result.read(tasks.id.count()) ?? 0;
  }

  /// Count task completions recorded today.
  /// Uses customSelect with readsFrom for proper stream invalidation.
  Stream<int> 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<int>('c'));
  }
}
