package com.rit.whatsapp.service; import com.rit.whatsapp.model.UserEntity; import com.rit.whatsapp.model.UserState; import com.rit.whatsapp.model.GrievanceDraft; import com.rit.whatsapp.repository.UserRepository; import com.rit.whatsapp.util.EncryptionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; @Service public class SessionService { private final Map userStates = new ConcurrentHashMap<>(); // In-memory cache for API session tokens private final Map sessionTokens = new ConcurrentHashMap<>(); private final Map temporaryLoginIds = new ConcurrentHashMap<>(); private final Map studentNames = new ConcurrentHashMap<>(); private final UserRepository userRepository; private final EncryptionUtils encryptionUtils; private final ImsApiService imsApiService; @Autowired public SessionService(UserRepository userRepository, EncryptionUtils encryptionUtils, ImsApiService imsApiService) { this.userRepository = userRepository; this.encryptionUtils = encryptionUtils; this.imsApiService = imsApiService; } public UserState getUserState(String phoneNumber) { return userStates.getOrDefault(phoneNumber, UserState.MAIN_MENU); } public void setUserState(String phoneNumber, UserState state) { userStates.put(phoneNumber, state); } public boolean isSessionCached(String phoneNumber) { return sessionTokens.containsKey(phoneNumber); } public boolean hasSavedCredentials(String phoneNumber) { return userRepository.existsByPhoneNumber(phoneNumber); } public void resetState(String phoneNumber) { userStates.remove(phoneNumber); } public boolean isAuthenticated(String phoneNumber) { if (sessionTokens.containsKey(phoneNumber)) { return true; } // Check database for persistent session Optional userOpt = userRepository.findByPhoneNumber(phoneNumber); if (userOpt.isPresent()) { UserEntity user = userOpt.get(); if (user.getEncryptedPasscode() == null) { return false; } String decryptedPasscode = encryptionUtils.decrypt(user.getEncryptedPasscode()); // Silent background login ImsApiService.AuthResult result = imsApiService.authenticate(user.getImsId(), decryptedPasscode); if (result != null) { sessionTokens.put(phoneNumber, result.token); if (user.isFaculty() != result.isFaculty) { user.setFaculty(result.isFaculty); userRepository.save(user); } return true; } } return false; } public void authenticateUser(String phoneNumber, String imsId, String passcode, ImsApiService.AuthResult result) { sessionTokens.put(phoneNumber, result.token); // Persist to database (students table) Optional userOpt = userRepository.findById(imsId); UserEntity user; if (userOpt.isPresent()) { user = userOpt.get(); user.setPhoneNumber(phoneNumber); user.setEncryptedPasscode(encryptionUtils.encrypt(passcode)); } else { String encryptedPasscode = encryptionUtils.encrypt(passcode); user = new UserEntity(imsId, phoneNumber, encryptedPasscode); } user.setFaculty(result.isFaculty); userRepository.save(user); } public String getImsToken(String phoneNumber) { // Ensure they are authenticated (which populates the token cache if needed) if (isAuthenticated(phoneNumber)) { return sessionTokens.get(phoneNumber); } return null; } public void logoutUser(String phoneNumber) { String token = sessionTokens.remove(phoneNumber); if (token != null) { imsApiService.logout(token); } Optional userOpt = userRepository.findByPhoneNumber(phoneNumber); if (userOpt.isPresent()) { UserEntity user = userOpt.get(); user.setEncryptedPasscode(null); userRepository.save(user); } studentNames.remove(phoneNumber); resetState(phoneNumber); } public String getStudentName(String phoneNumber, String token) { if (studentNames.containsKey(phoneNumber)) { return studentNames.get(phoneNumber); } // Try getting name from database first Optional userOpt = userRepository.findByPhoneNumber(phoneNumber); if (userOpt.isPresent() && userOpt.get().getName() != null && !userOpt.get().getName().isEmpty()) { String dbName = userOpt.get().getName(); studentNames.put(phoneNumber, dbName); return dbName; } // Fallback to IMS API String name; if (isFaculty(phoneNumber)) { name = imsApiService.getFacultyName(token); } else { name = imsApiService.getStudentName(token); } studentNames.put(phoneNumber, name); return name; } private final Map>> attendanceSubjectParams = new ConcurrentHashMap<>(); private final Map> activeAttendanceParams = new ConcurrentHashMap<>(); private final Map> activeStudentLists = new ConcurrentHashMap<>(); public void storeAttendanceSubjectParams(String phoneNumber, List> paramsList) { attendanceSubjectParams.put(phoneNumber, paramsList); } public Map getAttendanceSubjectParams(String phoneNumber, int index) { List> paramsList = attendanceSubjectParams.get(phoneNumber); if (paramsList != null && index >= 0 && index < paramsList.size()) { return paramsList.get(index); } return null; } public void setActiveAttendanceParams(String phoneNumber, Map params) { activeAttendanceParams.put(phoneNumber, params); } public Map getActiveAttendanceParams(String phoneNumber) { return activeAttendanceParams.get(phoneNumber); } public void clearActiveAttendanceParams(String phoneNumber) { activeAttendanceParams.remove(phoneNumber); activeStudentLists.remove(phoneNumber); } public void storeActiveStudentList(String phoneNumber, List list) { activeStudentLists.put(phoneNumber, list); } public List getActiveStudentList(String phoneNumber) { return activeStudentLists.get(phoneNumber); } private final Map> receiptIdMappings = new ConcurrentHashMap<>(); public void storeReceiptIdMappings(String phoneNumber, Map mappings) { receiptIdMappings.put(phoneNumber, mappings); } public String getReceiptIdMapping(String phoneNumber, String shortId) { Map mappings = receiptIdMappings.get(phoneNumber); if (mappings != null) { return mappings.get(shortId); } return null; } public void setTempLoginId(String phoneNumber, String tempId) { temporaryLoginIds.put(phoneNumber, tempId); } public String getTempLoginId(String phoneNumber) { return temporaryLoginIds.get(phoneNumber); } public void clearTempLoginId(String phoneNumber) { temporaryLoginIds.remove(phoneNumber); } private final Map grievanceDrafts = new ConcurrentHashMap<>(); public GrievanceDraft getOrCreateGrievanceDraft(String phoneNumber) { return grievanceDrafts.computeIfAbsent(phoneNumber, k -> { GrievanceDraft draft = new GrievanceDraft(); draft.setPhoneNumber(phoneNumber); return draft; }); } public GrievanceDraft getGrievanceDraft(String phoneNumber) { return grievanceDrafts.get(phoneNumber); } public void clearGrievanceDraft(String phoneNumber) { grievanceDrafts.remove(phoneNumber); } public String getImsId(String phoneNumber) { Optional userOpt = userRepository.findByPhoneNumber(phoneNumber); if (userOpt.isPresent()) { return userOpt.get().getImsId(); } return null; } public boolean isFaculty(String phoneNumber) { Optional userOpt = userRepository.findByPhoneNumber(phoneNumber); if (userOpt.isPresent()) { return userOpt.get().isFaculty(); } return false; } public void deleteUser(String phoneNumber) { logoutUser(phoneNumber); Optional userOpt = userRepository.findByPhoneNumber(phoneNumber); if (userOpt.isPresent()) { userRepository.delete(userOpt.get()); } } }