import '../models/deck.dart'; import '../models/question.dart'; import '../models/deck_config.dart'; import '../algorithms/priority_manager.dart'; /// Service for managing deck operations. class DeckService { /// Marks a question as known (manual override). /// /// Sets streak to threshold and isKnown to true. static Deck markQuestionAsKnown({ required Deck deck, required String questionId, }) { final updatedQuestions = deck.questions.map((q) { if (q.id == questionId) { return q.copyWith( consecutiveCorrect: deck.config.requiredConsecutiveCorrect, isKnown: true, ); } return q; }).toList(); return deck.copyWith(questions: updatedQuestions); } /// Marks a question as needs practice (manual override). /// /// Sets isKnown to false and streak to 0, but keeps priority unchanged. static Deck markQuestionAsNeedsPractice({ required Deck deck, required String questionId, }) { final updatedQuestions = deck.questions.map((q) { if (q.id == questionId) { return q.copyWith( isKnown: false, consecutiveCorrect: 0, // priorityPoints remains unchanged ); } return q; }).toList(); return deck.copyWith(questions: updatedQuestions); } /// Resets the entire deck. /// /// Resets all questions: streak = 0, isKnown = false, priority = 0. /// Optionally resets totalAttempts if [resetAttemptCounts] is true. /// Optionally clears attempt history if [clearAttemptHistory] is true. static Deck resetDeck({ required Deck deck, bool resetAttemptCounts = false, bool clearAttemptHistory = false, }) { final updatedQuestions = deck.questions.map((q) { return q.copyWith( consecutiveCorrect: 0, isKnown: false, priorityPoints: 0, lastAttemptIndex: -1, totalCorrectAttempts: resetAttemptCounts ? 0 : q.totalCorrectAttempts, totalAttempts: resetAttemptCounts ? 0 : q.totalAttempts, ); }).toList(); return deck.copyWith( questions: updatedQuestions, currentAttemptIndex: 0, attemptHistory: clearAttemptHistory ? [] : deck.attemptHistory, ); } /// Updates a question's state after an answer. /// /// Applies correctness rules and updates streak, priority, and isKnown. static Question updateQuestionAfterAnswer({ required Question question, required bool isCorrect, required DeckConfig config, required int currentAttemptIndex, }) { Question updated = question; if (isCorrect) { // Increment streak final newStreak = question.consecutiveCorrect + 1; updated = updated.copyWith(consecutiveCorrect: newStreak); // Update isKnown if streak reaches threshold if (newStreak >= config.requiredConsecutiveCorrect) { updated = updated.copyWith(isKnown: true); } // Decrease priority updated = PriorityManager.applyAnswerResult( question: updated, isCorrect: true, config: config, ); // Update totals updated = updated.copyWith( totalCorrectAttempts: question.totalCorrectAttempts + 1, totalAttempts: question.totalAttempts + 1, ); } else { // Reset streak updated = updated.copyWith( consecutiveCorrect: 0, isKnown: false, ); // Increase priority updated = PriorityManager.applyAnswerResult( question: updated, isCorrect: false, config: config, ); // Update totals updated = updated.copyWith( totalAttempts: question.totalAttempts + 1, ); } // Update lastAttemptIndex updated = updated.copyWith(lastAttemptIndex: currentAttemptIndex); return updated; } /// Updates a question with manual override after an answer. /// /// If user marks as "Needs Practice" even after correct answer, /// ignore correctness and don't increment streak or decrease priority. static Question updateQuestionWithManualOverride({ required Question question, required bool isCorrect, required bool userMarkedNeedsPractice, required DeckConfig config, required int currentAttemptIndex, }) { if (userMarkedNeedsPractice) { // User marked as needs practice - ignore correctness // Don't increment streak, don't decrease priority // Just update attempt counts and lastAttemptIndex return question.copyWith( totalAttempts: question.totalAttempts + 1, lastAttemptIndex: currentAttemptIndex, ); } else { // Normal flow return updateQuestionAfterAnswer( question: question, isCorrect: isCorrect, config: config, currentAttemptIndex: currentAttemptIndex, ); } } }