import 'package:drift/drift.dart';

import '../../../core/database/database.dart';

part 'rooms_dao.g.dart';

/// Stats for a room including task counts and cleanliness ratio.
class RoomWithStats {
  final Room room;
  final int totalTasks;
  final int dueTasks;
  final int overdueCount;
  final double cleanlinessRatio;

  const RoomWithStats({
    required this.room,
    required this.totalTasks,
    required this.dueTasks,
    required this.overdueCount,
    required this.cleanlinessRatio,
  });
}

@DriftAccessor(tables: [Rooms, Tasks, TaskCompletions])
class RoomsDao extends DatabaseAccessor<AppDatabase> with _$RoomsDaoMixin {
  RoomsDao(super.attachedDatabase);

  /// Watch all rooms ordered by sortOrder.
  Stream<List<Room>> watchAllRooms() {
    return (select(rooms)..orderBy([(r) => OrderingTerm.asc(r.sortOrder)]))
        .watch();
  }

  /// Watch all rooms with computed task stats.
  ///
  /// Cleanliness ratio = (totalTasks - overdueCount) / totalTasks.
  /// 1.0 when no tasks are overdue, 0.0 when all are overdue.
  /// Rooms with no tasks have ratio 1.0.
  Stream<List<RoomWithStats>> watchRoomWithStats({DateTime? today}) {
    final now = today ?? DateTime.now();
    final todayDateOnly = DateTime(now.year, now.month, now.day);

    return watchAllRooms().asyncMap((roomList) async {
      final stats = <RoomWithStats>[];
      for (final room in roomList) {
        final taskList = await (select(tasks)
              ..where(
                (t) => t.roomId.equals(room.id) & t.isActive.equals(true),
              ))
            .get();

        final totalTasks = taskList.length;
        var overdueCount = 0;
        var dueTasks = 0;

        for (final task in taskList) {
          final dueDate = DateTime(
            task.nextDueDate.year,
            task.nextDueDate.month,
            task.nextDueDate.day,
          );
          if (dueDate.isBefore(todayDateOnly) ||
              dueDate.isAtSameMomentAs(todayDateOnly)) {
            dueTasks++;
          }
          if (dueDate.isBefore(todayDateOnly)) {
            overdueCount++;
          }
        }

        final ratio =
            totalTasks == 0 ? 1.0 : (totalTasks - overdueCount) / totalTasks;

        stats.add(RoomWithStats(
          room: room,
          totalTasks: totalTasks,
          dueTasks: dueTasks,
          overdueCount: overdueCount,
          cleanlinessRatio: ratio,
        ));
      }
      return stats;
    });
  }

  /// Insert a new room. Returns the auto-generated id.
  Future<int> insertRoom(RoomsCompanion room) => into(rooms).insert(room);

  /// Update an existing room. Returns true if a row was updated.
  Future<bool> updateRoom(Room room) => update(rooms).replace(room);

  /// Delete a room and cascade to its tasks and completions.
  Future<void> deleteRoom(int roomId) {
    return transaction(() async {
      // Get all task IDs for this room
      final taskList = await (select(tasks)
            ..where((t) => t.roomId.equals(roomId)))
          .get();

      // Delete completions for each task
      for (final task in taskList) {
        await (delete(taskCompletions)
              ..where((c) => c.taskId.equals(task.id)))
            .go();
      }

      // Delete tasks
      await (delete(tasks)..where((t) => t.roomId.equals(roomId))).go();

      // Delete room
      await (delete(rooms)..where((r) => r.id.equals(roomId))).go();
    });
  }

  /// Reorder rooms by updating sortOrder for each room in the list.
  Future<void> reorderRooms(List<int> roomIds) {
    return transaction(() async {
      for (var i = 0; i < roomIds.length; i++) {
        await (update(rooms)..where((r) => r.id.equals(roomIds[i])))
            .write(RoomsCompanion(sortOrder: Value(i)));
      }
    });
  }

  /// Get a single room by its id.
  Future<Room> getRoomById(int id) {
    return (select(rooms)..where((r) => r.id.equals(id))).getSingle();
  }
}
