11import Progress from "./progress.model.js" ;
22import User from "../../auth/User.model.js" ;
3- import { checkAndUnlockMultipleBadges } from "../../badges/badge.service.js" ;
43
54// Helper = recompute overall totals from all chapters
65const recomputeTotals = ( chapters ) => {
@@ -24,9 +23,9 @@ export const updateChapterProgress = async (userId, career, chapterId, easyScore
2423 easyScore = Math . max ( 0 , parseInt ( easyScore ) || 0 ) ;
2524 mediumScore = Math . max ( 0 , parseInt ( mediumScore ) || 0 ) ;
2625 hardScore = Math . max ( 0 , parseInt ( hardScore ) || 0 ) ;
27-
26+
2827 const newTotal = easyScore + mediumScore + hardScore ;
29-
28+
3029 // Sanity check: total score shouldn't exceed 100 (reasonable quiz limit)
3130 if ( newTotal > 100 ) {
3231 throw new Error ( `Invalid score: total (${ newTotal } ) exceeds maximum of 100. Possible cheating attempt.` ) ;
@@ -101,73 +100,37 @@ export const updateChapterProgress = async (userId, career, chapterId, easyScore
101100 { returnDocument : "after" }
102101 ) ;
103102
104- // AWARD XP/TOKENS only if newly passed (prevents duplicate rewards)
105- let tokensAwarded = 0 ;
106- let xpAwarded = 0 ;
107-
108- if ( passed && ! previouslyPassed ) {
109- // First time passing: award for ALL correct answers
110- tokensAwarded = newTotal ;
111- xpAwarded = newTotal * 3 ;
112-
103+ // Award XP based on first-time or retry pass
104+ if ( passed ) {
105+ const xp = previouslyPassed ? 5 : 10 ;
113106 try {
114107 await User . findByIdAndUpdate ( userId , {
115- $inc : {
116- tokens : tokensAwarded ,
117- totalXP : xpAwarded ,
118- casesSolved : tokensAwarded
119- }
108+ $inc : { totalXP : xp }
120109 } ) ;
121-
122- // Record this in progress so we don't re-award on retry
123- await Progress . updateOne (
124- { userId, career, "chapters.chapterId" : chapterId } ,
125- { $set : { "chapters.$.rewardedQuestions" : newTotal } }
126- ) ;
127- console . log ( `✅ First-time pass reward: ${ tokensAwarded } tokens, ${ xpAwarded } XP to user ${ userId } for ${ career } /${ chapterId } ` ) ;
128-
129- // Check and unlock relevant badges
130- const badgesToCheck = [ 'beginner' , 'cases_5' , 'cases_10' , 'cases_25' ] ;
131- const newlyUnlocked = await checkAndUnlockMultipleBadges ( userId , badgesToCheck ) ;
132- if ( newlyUnlocked . length > 0 ) {
133- console . log ( `✅ New badges unlocked: ${ newlyUnlocked . join ( ', ' ) } ` ) ;
134- }
110+ console . log ( `✅ Chapter ${ previouslyPassed ? 'retry' : 'first' } pass: +${ xp } XP to user ${ userId } for ${ career } /${ chapterId } ` ) ;
135111 } catch ( err ) {
136- console . error ( `❌ Failed to award first-time pass rewards for user ${ userId } :` , err . message ) ;
112+ console . error ( `❌ Failed to award chapter XP :` , err . message ) ;
137113 }
138- } else if ( passed && previouslyPassed && newTotal > previousRewarded ) {
139- // Already passed before: only award for NEW correct answers (improvement bonus)
140- tokensAwarded = newTotal - previousRewarded ;
141- xpAwarded = tokensAwarded * 3 ;
142-
114+ }
115+
116+ // Award 1 token when all 4 chapters passed for the first time
117+ progress = await Progress . findOne ( { userId, career } ) ;
118+ const allChaptersPassed = progress . chapters . length === 4 &&
119+ progress . chapters . every ( ch => ch . passed ) ;
120+
121+ if ( allChaptersPassed && ! progress . careerRewarded ) {
143122 try {
144123 await User . findByIdAndUpdate ( userId , {
145- $inc : {
146- tokens : tokensAwarded ,
147- totalXP : xpAwarded ,
148- casesSolved : tokensAwarded
149- }
124+ $inc : { tokens : 1 }
150125 } ) ;
151-
152- // Update rewardedQuestions to track the new high score
153126 await Progress . updateOne (
154- { userId, career, "chapters.chapterId" : chapterId } ,
155- { $set : { "chapters.$.rewardedQuestions" : newTotal } }
127+ { userId, career } ,
128+ { $set : { careerRewarded : true } }
156129 ) ;
157- console . log ( `✅ Improvement bonus: +${ tokensAwarded } tokens, +${ xpAwarded } XP to user ${ userId } for ${ career } /${ chapterId } ` ) ;
158-
159- // Check and unlock relevant badges
160- const badgesToCheck = [ 'beginner' , 'cases_5' , 'cases_10' , 'cases_25' ] ;
161- const newlyUnlocked = await checkAndUnlockMultipleBadges ( userId , badgesToCheck ) ;
162- if ( newlyUnlocked . length > 0 ) {
163- console . log ( `✅ New badges unlocked: ${ newlyUnlocked . join ( ', ' ) } ` ) ;
164- }
130+ console . log ( `✅ Career complete: +1 token to user ${ userId } for ${ career } ` ) ;
165131 } catch ( err ) {
166- console . error ( `❌ Failed to award improvement bonus for user ${ userId } :` , err . message ) ;
132+ console . error ( `❌ Failed to award career token :` , err . message ) ;
167133 }
168- } else if ( ! passed ) {
169- // Did not pass - no XP/tokens awarded
170- console . log ( `ℹ️ Quiz not passed (${ newTotal } correct, need >12): no rewards for user ${ userId } ` ) ;
171134 }
172135
173136 // Refetch and return updated document
0 commit comments