import 'dart:convert'; import 'dart:io'; import 'package:injectable/injectable.dart'; import 'package:mnemo_cards_backend/database/database.dart'; import 'package:mnemo_cards_common_backend/mnemo_cards_common_backend.dart'; import 'package:mnemo_cards_backend/packs/pack_manager.dart'; import 'package:mnemo_cards_backend/tests/generators/models/creation_test_data.dart'; import 'package:mnemo_cards_backend/tests/test_extension.dart'; import 'package:mnemo_cards_common/mnemo_cards_common.dart'; import 'package:drift/drift.dart' as drift; import '../packs/pack_dto_converter.dart'; import 'generators/question_generators/input_buttons_question_generator.dart'; import 'generators/pack_test_generator.dart'; import 'generators/question_generators/simple_question_generator.dart'; @lazySingleton class TestManager { final AppDatabase _db; final PackDtoConverter _packDtoConverter; List _customCreationTestData = []; TestManager(this._db, this._packDtoConverter); Future _testStatisticsDto( int userId, int testId) async { final statistics = await _db.testDao.getTestStatistics(userId, testId); if (statistics == null) return null; // Convert TestStatistic to TestStatisticsDto return TestStatisticsDto( testId: statistics.testId.toString(), results: statistics.results, completedAt: statistics.completedAt.toIso8601String(), ); } Future fetchTest(String id, UserModel user) async { final testId = int.tryParse(id); if (testId == null) return null; final test = await _db.testDao.getTestById(testId); if (test == null) return null; final questions = await _db.testDao.getTestQuestions(testId); final statistics = await _testStatisticsDto(user.id!, testId); // Convert Test to TestDto final questionsList = questions.map((q) { // Parse question body based on type final body = json.decode(q.body) as Map; return AbstractTestQuestion.fromJson({ 'questionType': q.questionType, ...body, }); }).toList(); return TestDto( id: testId.toString(), name: test.name, color: test.color, cover: test.cover, version: test.version ?? '1.0', time: test.time, timeSubtitle: test.timeSubtitle, questions: questionsList, statistics: statistics, ); } Future> availableTests(UserModel userModel) async { final tests = await _db.testDao.getAllTests(); final testDtos = []; for (final test in tests) { final questions = await _db.testDao.getTestQuestions(test.id); final statistics = await _testStatisticsDto(userModel.id!, test.id); final questionsList = questions.map((q) { final body = json.decode(q.body) as Map; return AbstractTestQuestion.fromJson({ 'questionType': q.questionType, ...body, }); }).toList(); testDtos.add(TestDto( id: test.id.toString(), name: test.name, color: test.color, cover: test.cover, version: test.version ?? '1.0', time: test.time, timeSubtitle: test.timeSubtitle, questions: questionsList, statistics: statistics, )); } return testDtos; } Future> fetchPackTests( UserModel user, CardPackModel model, ) async { final packId = model.id; if (packId == null) return []; final tests = await _db.testDao.getTestsByPackId(packId); final testDtos = []; for (final test in tests) { final questions = await _db.testDao.getTestQuestions(test.id); final statistics = await _testStatisticsDto(user.id!, test.id); final questionsList = questions.map((q) { final body = json.decode(q.body) as Map; return AbstractTestQuestion.fromJson({ 'questionType': q.questionType, ...body, }); }).toList(); testDtos.add(TestDto( id: test.id.toString(), name: test.name, color: test.color, cover: test.cover, version: test.version ?? '1.0', time: test.time, timeSubtitle: test.timeSubtitle, questions: questionsList, statistics: statistics, )); } return testDtos; } Future _updateGeneratedTestsIfRequired(CardPackModel model) async { final packId = model.id; if (packId == null) return false; // Check if pack has any generated tests final existingTests = await _db.testDao.getTestsByPackId(packId); final hasGeneratedTests = existingTests.any((test) => test.version?.contains('generated') ?? false); // Generate tests if none exist if (!hasGeneratedTests) { await updateGeneratedTests(model); return true; } return false; } Future refreshCustomCreationTestsData() async { // Load custom creation test data from database // For now, keep it simple _customCreationTestData = []; } Future updateGeneratedTests(CardPackModel model) async { final packId = model.id; if (packId == null) return; // Get pack data from database final pack = await _db.packDao.getPackById(packId); if (pack == null) return; // Get all cards for the pack final cards = await _db.packDao.getPackCards(packId); if (cards.isEmpty) return; // Create creation test data final testDataItems = cards.map((card) { return TestDataItem( id: card.id.toString(), original: card.original, translation: card.translation, mnemo: card.mnemo, image: card.image, back: card.back, transcription: card.transcription, ); }).toList(); final creationTestData = CreationTestData( items: testDataItems, color: pack.color, packId: packId.toString(), ); // Generate test using PackTestGenerator final generator = PackTestGenerator(creationTestData); final testDto = await generator.generate( name: '${pack.title} Test', ratios: { TestQuestionType.simple: 0.7, TestQuestionType.input_buttons: 0.3, }, multiply: 1.0, ); // Save test to database await addTest(testDto); // Link test to pack final testId = int.tryParse(testDto.id ?? ''); if (testId != null) { await _db.testDao.linkTestToPack(testId, packId); } } Future addTest(TestDto testDto) async { await _db.transaction(() async { // Create test final testCompanion = TestsCompanion.insert( name: testDto.name, color: drift.Value(testDto.color), cover: drift.Value(testDto.cover), version: drift.Value(testDto.version), time: drift.Value(testDto.time), timeSubtitle: drift.Value(testDto.timeSubtitle), ); final testId = await _db.testDao.createTest(testCompanion); // Create questions for (final question in testDto.questions) { final questionCompanion = TestQuestionsCompanion.insert( testId: testId, questionType: question.questionType.name, body: json.encode(question.toJson()), ); await _db.testDao.createTestQuestion(questionCompanion); } }); } }