View Javadoc
1   package org.petify.backend.services;
2   
3   import org.petify.backend.models.Achievement;
4   import org.petify.backend.models.AchievementCategory;
5   import org.petify.backend.models.ApplicationUser;
6   import org.petify.backend.models.UserAchievement;
7   import org.petify.backend.repository.AchievementRepository;
8   import org.petify.backend.repository.UserAchievementRepository;
9   import org.petify.backend.repository.UserRepository;
10  
11  import lombok.extern.slf4j.Slf4j;
12  import org.springframework.beans.factory.annotation.Autowired;
13  import org.springframework.stereotype.Service;
14  import org.springframework.transaction.annotation.Transactional;
15  
16  import java.time.LocalDateTime;
17  import java.util.HashMap;
18  import java.util.List;
19  import java.util.Map;
20  import java.util.Optional;
21  
22  @Service
23  @Slf4j
24  public class AchievementService {
25  
26      @Autowired
27      private AchievementRepository achievementRepository;
28  
29      @Autowired
30      private UserAchievementRepository userAchievementRepository;
31  
32      @Autowired
33      private UserRepository userRepository;
34  
35      @Transactional(readOnly = true)
36      public List<UserAchievement> getUserAchievements(String username) {
37          ApplicationUser user = userRepository.findByUsername(username)
38                  .orElseThrow(() -> new RuntimeException("User not found"));
39  
40          return userAchievementRepository.findByUser(user);
41      }
42  
43      @Transactional
44      public UserAchievement trackAchievementProgress(String username, Long achievementId, int progressIncrement) {
45          ApplicationUser user = userRepository.findByUsername(username)
46                  .orElseThrow(() -> new RuntimeException("User not found"));
47  
48          Achievement achievement = achievementRepository.findById(achievementId)
49                  .orElseThrow(() -> new RuntimeException("Achievement not found"));
50  
51          UserAchievement userAchievement = userAchievementRepository
52                  .findByUserAndAchievementId(user, achievementId)
53                  .orElseGet(() -> {
54                      UserAchievement newAchievement = new UserAchievement();
55                      newAchievement.setUser(user);
56                      newAchievement.setAchievement(achievement);
57                      newAchievement.setCurrentProgress(0);
58                      return newAchievement;
59                  });
60  
61          if (userAchievement.getCompleted()) {
62              return userAchievement;
63          }
64  
65          int newProgress = userAchievement.getCurrentProgress() + progressIncrement;
66          userAchievement.setCurrentProgress(newProgress);
67  
68          if (newProgress >= achievement.getRequiredActions()) {
69              userAchievement.setCompleted(true);
70              userAchievement.setCompletionDate(LocalDateTime.now());
71  
72              user.setXpPoints(user.getXpPoints() + achievement.getXpReward());
73  
74              updateBadgeCounts(user, achievement.getCategory());
75  
76              updateUserLevel(user);
77  
78              userRepository.save(user);
79  
80              log.info("User {} completed achievement: {} (+{} XP)",
81                      username, achievement.getName(), achievement.getXpReward());
82          }
83  
84          return userAchievementRepository.save(userAchievement);
85      }
86  
87      @Transactional
88      public void trackLikeAchievements(String username) {
89          ApplicationUser user = userRepository.findByUsername(username)
90                  .orElseThrow(() -> new RuntimeException("User not found"));
91  
92          user.setLikesCount(user.getLikesCount() + 1);
93          userRepository.save(user);
94  
95          List<Achievement> likeAchievements = achievementRepository.findByCategory(AchievementCategory.LIKES);
96  
97          for (Achievement achievement : likeAchievements) {
98              trackAchievementProgress(username, achievement.getId(), 1);
99          }
100         log.info("Tracked like achievements for user {} - new like count: {}",
101                 username, user.getLikesCount());
102     }
103 
104     @Transactional
105     public void trackSupportAchievements(String username) {
106         ApplicationUser user = userRepository.findByUsername(username)
107                 .orElseThrow(() -> new RuntimeException("User not found"));
108 
109         user.setSupportCount(user.getSupportCount() + 1);
110         userRepository.save(user);
111 
112         List<Achievement> supportAchievements = achievementRepository.findByCategory(AchievementCategory.SUPPORT);
113 
114         for (Achievement achievement : supportAchievements) {
115             trackAchievementProgress(username, achievement.getId(), 1);
116         }
117 
118         log.info("Tracked support achievements for user {} - new support count: {}",
119                 username, user.getSupportCount());
120     }
121 
122     @Transactional
123     public void trackProfileAchievementByName(String username, String achievementName) {
124         trackAchievementByNameAndCategory(username, achievementName, AchievementCategory.PROFILE);
125     }
126 
127     private void updateBadgeCounts(ApplicationUser user, AchievementCategory category) {
128         switch (category) {
129             case LIKES:
130                 break;
131             case SUPPORT:
132                 break;
133             case ADOPTION:
134                 user.setAdoptionCount(user.getAdoptionCount() + 1);
135                 user.setBadgesCount(user.getBadgesCount() + 1);
136                 break;
137             case BADGE:
138             case PROFILE:
139             case VOLUNTEER:
140                 user.setBadgesCount(user.getBadgesCount() + 1);
141                 break;
142             default:
143                 break;
144         }
145     }
146 
147     private void updateUserLevel(ApplicationUser user) {
148         int xp = user.getXpPoints();
149         int newLevel = (xp / 100) + 1;
150 
151         if (newLevel > user.getLevel()) {
152             int oldLevel = user.getLevel();
153             user.setLevel(newLevel);
154             log.info("User {} leveled up from {} to {}! (Total XP: {})",
155                     user.getUsername(), oldLevel, newLevel, xp);
156         }
157     }
158 
159     public Map<String, Object> getUserLevelInfo(String username) {
160         ApplicationUser user = userRepository.findByUsername(username)
161                 .orElseThrow(() -> new RuntimeException("User not found"));
162 
163         Map<String, Object> levelInfo = new HashMap<>();
164         levelInfo.put("level", user.getLevel());
165         levelInfo.put("xpPoints", user.getXpPoints());
166         levelInfo.put("xpToNextLevel", user.getXpToNextLevel());
167         levelInfo.put("likesCount", user.getLikesCount());
168         levelInfo.put("supportCount", user.getSupportCount());
169         levelInfo.put("badgesCount", user.getBadgesCount());
170         levelInfo.put("adoptionCount", user.getAdoptionCount());
171 
172         return levelInfo;
173     }
174 
175     @Transactional
176     public void initializeUserAchievements(ApplicationUser user) {
177         List<Achievement> allAchievements = achievementRepository.findAll();
178 
179         for (Achievement achievement : allAchievements) {
180             UserAchievement userAchievement = new UserAchievement();
181             userAchievement.setUser(user);
182             userAchievement.setAchievement(achievement);
183             userAchievement.setCurrentProgress(0);
184             userAchievement.setCompleted(false);
185 
186             userAchievementRepository.save(userAchievement);
187         }
188     }
189 
190     @Transactional
191     public void trackVolunteerAchievements(String username) {
192         List<Achievement> volunteerAchievements = achievementRepository.findByCategory(AchievementCategory.VOLUNTEER);
193 
194         for (Achievement achievement : volunteerAchievements) {
195             trackAchievementProgress(username, achievement.getId(), 1);
196         }
197     }
198 
199     @Transactional
200     public void trackVolunteerAchievementByName(String username, String achievementName) {
201         trackAchievementByNameAndCategory(username, achievementName, AchievementCategory.VOLUNTEER);
202     }
203 
204     private void trackAchievementByNameAndCategory(String username, String achievementName, AchievementCategory category) {
205         try {
206             List<Achievement> achievements = achievementRepository.findByCategory(category);
207 
208             Optional<Achievement> achievement = achievements.stream()
209                     .filter(a -> a.getName().equals(achievementName))
210                     .findFirst();
211 
212             if (achievement.isPresent()) {
213                 ApplicationUser user = userRepository.findByUsername(username)
214                         .orElseThrow(() -> new RuntimeException("User not found"));
215 
216                 Optional<UserAchievement> existingUserAchievement = userAchievementRepository
217                         .findByUserAndAchievementId(user, achievement.get().getId());
218 
219                 if (existingUserAchievement.isEmpty() || !existingUserAchievement.get().getCompleted()) {
220                     trackAchievementProgress(username, achievement.get().getId(), 1);
221                 }
222             } else {
223                 log.warn("Achievement with name '{}' not found in {} category", achievementName, category);
224             }
225         } catch (Exception e) {
226             log.error("Error tracking {} achievement '{}' for user {}: {}",
227                     category, achievementName, username, e.getMessage());
228         }
229     }
230 
231     @Transactional
232     public void trackAdoptionAchievements(String username) {
233         List<Achievement> adoptionAchievements = achievementRepository.findByCategory(AchievementCategory.ADOPTION);
234 
235         for (Achievement achievement : adoptionAchievements) {
236             trackAchievementProgress(username, achievement.getId(), 1);
237         }
238     }
239 
240     @Transactional
241     public void addExperiencePointsForDonation(String username, int xpPoints) {
242         ApplicationUser user = userRepository.findByUsername(username)
243                 .orElseThrow(() -> new RuntimeException("User not found"));
244 
245         user.setXpPoints(user.getXpPoints() + xpPoints);
246         updateUserLevel(user);
247         userRepository.save(user);
248 
249         log.info("Added {} XP to user {} for donation (Total XP: {})", 
250                 xpPoints, username, user.getXpPoints());
251     }
252 }