import 'package:drift/drift.dart'; import 'package:tallee/core/enums.dart'; import 'package:tallee/data/db/database.dart'; import 'package:tallee/data/db/tables/game_table.dart'; import 'package:tallee/data/models/game.dart'; part 'game_dao.g.dart'; @DriftAccessor(tables: [GameTable]) class GameDao extends DatabaseAccessor with _$GameDaoMixin { GameDao(super.db); /* Create */ /// Adds a new [game] to the database. /// If a game with the same ID already exists, no action is taken. /// Returns `true` if the game was added, `false` otherwise. Future addGame({required Game game}) async { if (!await gameExists(gameId: game.id)) { await into(gameTable).insert( GameTableCompanion.insert( id: game.id, name: game.name, ruleset: game.ruleset.name, description: game.description, color: game.color.name, icon: game.icon, createdAt: game.createdAt, ), mode: InsertMode.insertOrReplace, ); return true; } return false; } /// Adds multiple [games] to the database in a batch operation. /// Uses insertOrIgnore to avoid overwriting existing games. Future addGamesAsList({required List games}) async { if (games.isEmpty) return false; await db.batch( (b) => b.insertAll( gameTable, games .map( (game) => GameTableCompanion.insert( id: game.id, name: game.name, ruleset: game.ruleset.name, description: game.description, color: game.color.name, icon: game.icon, createdAt: game.createdAt, ), ) .toList(), mode: InsertMode.insertOrIgnore, ), ); return true; } /* Read */ /// Retrieves the total count of games in the database. Future getGameCount() async { final count = await (selectOnly(gameTable)..addColumns([gameTable.id.count()])) .map((row) => row.read(gameTable.id.count())) .getSingle(); return count ?? 0; } /// Checks if a game with the given [gameId] exists in the database. /// Returns `true` if the game exists, `false` otherwise. Future gameExists({required String gameId}) async { final query = select(gameTable)..where((g) => g.id.equals(gameId)); final result = await query.getSingleOrNull(); return result != null; } /// Retrieves all games from the database. Future> getAllGames() async { final query = select(gameTable); final result = await query.get(); return result .map( (row) => Game( id: row.id, name: row.name, ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset), description: row.description, color: GameColor.values.firstWhere((e) => e.name == row.color), icon: row.icon, createdAt: row.createdAt, ), ) .toList(); } /// Retrieves a [Game] by its [gameId]. Future getGameById({required String gameId}) async { final query = select(gameTable)..where((g) => g.id.equals(gameId)); final result = await query.getSingle(); return Game( id: result.id, name: result.name, ruleset: Ruleset.values.firstWhere((e) => e.name == result.ruleset), description: result.description, color: GameColor.values.firstWhere((e) => e.name == result.color), icon: result.icon, createdAt: result.createdAt, ); } /* Update */ /// Updates the name of the game with the given [gameId] to [name]. Future updateGameName({ required String gameId, required String name, }) async { final rowsAffected = await (update(gameTable)..where((g) => g.id.equals(gameId))).write( GameTableCompanion(name: Value(name)), ); return rowsAffected > 0; } /// Updates the ruleset of the game with the given [gameId]. Future updateGameRuleset({ required String gameId, required Ruleset ruleset, }) async { final rowsAffected = await (update(gameTable)..where((g) => g.id.equals(gameId))).write( GameTableCompanion(ruleset: Value(ruleset.name)), ); return rowsAffected > 0; } /// Updates the description of the game with the given [gameId]. Future updateGameDescription({ required String gameId, required String description, }) async { final rowsAffected = await (update(gameTable)..where((g) => g.id.equals(gameId))).write( GameTableCompanion(description: Value(description)), ); return rowsAffected > 0; } /// Updates the color of the game with the given [gameId]. Future updateGameColor({ required String gameId, required GameColor color, }) async { final rowsAffected = await (update(gameTable)..where((g) => g.id.equals(gameId))).write( GameTableCompanion(color: Value(color.name)), ); return rowsAffected > 0; } /// Updates the icon of the game with the given [gameId]. Future updateGameIcon({ required String gameId, required String icon, }) async { final rowsAffected = await (update(gameTable)..where((g) => g.id.equals(gameId))).write( GameTableCompanion(icon: Value(icon)), ); return rowsAffected > 0; } /* Delete */ /// Deletes the game with the given [gameId] from the database. /// Returns `true` if the game was deleted, `false` if the game did not exist. Future deleteGame({required String gameId}) async { final query = delete(gameTable)..where((g) => g.id.equals(gameId)); final rowsAffected = await query.go(); return rowsAffected > 0; } /// Deletes all games from the database. /// Returns `true` if more than 0 rows were affected, otherwise `false`. Future deleteAllGames() async { final query = delete(gameTable); final rowsAffected = await query.go(); return rowsAffected > 0; } /// Retrieves all games with their respective match counts. /// Returns a list of tuples (Game, matchCount). Future> getGameUsage() async { final games = await getAllGames(); final results = <(Game, int)>[]; for (final game in games) { final matchCount = await (selectOnly(db.matchTable) ..where(db.matchTable.gameId.equals(game.id)) ..addColumns([db.matchTable.id.count()])) .map((row) => row.read(db.matchTable.id.count())) .getSingle(); results.add((game, matchCount ?? 0)); } return results; } }