import 'package:drift/drift.dart'; import 'package:drift_postgres/drift_postgres.dart'; import 'package:injectable/injectable.dart'; import 'package:mnemo_cards_backend/database/database.dart'; import 'package:mnemo_cards_common_backend/mnemo_cards_common_backend.dart' hide VoiceModel; import 'card_pack_drift_extension.dart'; /// Extension для конвертации GameCard (Drift) в GameCardModel extension GameCardToModel on GameCard { GameCardModel toModel() { return GameCardModel( id: id, image: image, mnemo: mnemo ?? '', original: original, translation: translation, transcription: transcription, transcriptionMnemo: transcriptionMnemo, imageBack: imageBack, back: back, ); } } /// Repository для работы с паками /// Работает с доменными моделями (CardPackModel), использует DAO для доступа к БД @lazySingleton class PackRepository { final AppDatabase _db; PackRepository(this._db); /// Получить пак по ID /// [includeCards] - загружать ли связанные карточки /// [includePreviewCards] - загружать ли превью карточки Future getPackById( String id, { bool includeCards = false, bool includePreviewCards = false, }) async { final pack = await _db.packDao.getPackById(id); if (pack == null) return null; final packModel = await pack.toModel(); if (includeCards) { final cards = await _db.packDao.getPackCards(id); packModel.cards.addAll(cards.map((c) => c.toModel())); } if (includePreviewCards) { final previewCards = await _db.packDao.getPreviewCards(id); packModel.previewCards.addAll(previewCards.map((c) => c.toModel())); } return packModel; } /// Получить все паки Future> getAllPacks({ bool enabledOnly = false, String? orderByField, bool orderDesc = false, bool includeCards = false, bool includePreviewCards = false, }) async { final packs = await _db.packDao.getAllPacks( enabledOnly: enabledOnly, orderByField: orderByField, orderDesc: orderDesc, ); if (includeCards || includePreviewCards) { final packModels = []; for (final pack in packs) { final packModel = await pack.toModel(); if (includeCards) { final cards = await _db.packDao.getPackCards(pack.id); packModel.cards.addAll(cards.map((c) => c.toModel())); } if (includePreviewCards) { final previewCards = await _db.packDao.getPreviewCards(pack.id); packModel.previewCards.addAll(previewCards.map((c) => c.toModel())); } packModels.add(packModel); } return packModels; } return await Future.wait(packs.map((p) => p.toModel())); } /// Создать пак Future createPack(CardPackModel packModel) async { final companion = CardPacksCompanion.insert( title: packModel.title, subtitle: packModel.subtitle, size: packModel.size, color: Value(packModel.color), version: Value(packModel.version), cover: Value(packModel.cover), description: Value(packModel.description), googlePlayId: Value(packModel.googlePlayId), rustoreId: Value(packModel.rustoreId), appStoreId: Value(packModel.appStoreId), price: Value(packModel.price), currency: packModel.currency != null ? Value(packModel.currency!) : const Value.absent(), enabled: Value(packModel.enabled), order: Value(packModel.order), cardsOrder: Value(packModel.cardsOrder), ); return await _db.packDao.createPack(companion); } /// Обновить пак Future updatePack(CardPackModel packModel) async { if (packModel.id == null) { throw ArgumentError('Pack ID is required'); } await _db.packDao.updatePackPartial( CardPacksCompanion( id: Value(packModel.id!), title: Value(packModel.title), subtitle: Value(packModel.subtitle), size: Value(packModel.size), color: Value(packModel.color), version: Value(packModel.version), cover: Value(packModel.cover), description: Value(packModel.description), googlePlayId: Value(packModel.googlePlayId), rustoreId: Value(packModel.rustoreId), appStoreId: Value(packModel.appStoreId), price: Value(packModel.price), currency: packModel.currency != null ? Value(packModel.currency!) : const Value.absent(), enabled: Value(packModel.enabled), order: Value(packModel.order), cardsOrder: Value(packModel.cardsOrder), updatedAt: Value(PgDateTime(DateTime.now())), ), ); } /// Обновить пак частично Future updatePackPartial(CardPacksCompanion updates) async { await _db.packDao.updatePackPartial(updates); } /// Удалить пак (soft delete) Future softDeletePack(String packId) async { await _db.packDao.softDeletePack(packId); } /// Подсчитать паки Future countPacks({bool enabledOnly = false}) async { return await _db.packDao.countPacks(enabledOnly: enabledOnly); } /// Получить карточки пака Future> getPackCards(String packId) async { final cards = await _db.packDao.getPackCards(packId); return cards.map((c) => c.toModel()).toList(); } /// Получить превью карточки пака Future> getPreviewCards(String packId) async { final cards = await _db.packDao.getPreviewCards(packId); return cards.map((c) => c.toModel()).toList(); } /// Получить карточку по ID Future getCardById(String id) async { final card = await _db.packDao.getCardById(id); return card?.toModel(); } /// Получить паки для карточки Future> getPacksForCard(String cardId) async { final packs = await _db.packDao.getPacksForCard(cardId); return await Future.wait(packs.map((p) => p.toModel())); } /// Добавить карточку в пак Future addCardToPack({ required String packId, required String cardId, int order = 0, }) async { await _db.packDao.addCardToPack( packId: packId, cardId: cardId, order: order, ); } /// Удалить карточку из пака Future removeCardFromPack(String packId, String cardId) async { await _db.packDao.removeCardFromPack(packId, cardId); } /// Обновить порядок карточек в паке Future updatePackCardsOrder(String packId, List cardIds) async { await _db.packDao.updatePackCardsOrder(packId, cardIds); } /// Установить preview карточки для пака Future setPreviewCards(String packId, List cardIds) async { await _db.packDao.setPreviewCards(packId, cardIds); } // ==================== VoiceModels ==================== /// Получить голосовые модели карточки Future> getCardVoices(String cardId) async { return await _db.packDao.getCardVoices(cardId); } /// Получить голосовую модель по ID Future getVoiceById(String id) async { return await _db.packDao.getVoiceById(id); } /// Обновить путь/URL аудиофайла голосовой модели Future updateVoiceUrl(String voiceId, String voiceUrl) async { await _db.packDao.updateVoiceUrl(voiceId, voiceUrl); } }