BadgeController.java

package no.ntnu.idi.stud.savingsapp.controller.badge;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import no.ntnu.idi.stud.savingsapp.dto.badge.BadgeDTO;
import no.ntnu.idi.stud.savingsapp.model.notification.Notification;
import no.ntnu.idi.stud.savingsapp.model.notification.NotificationType;
import no.ntnu.idi.stud.savingsapp.model.user.Badge;
import no.ntnu.idi.stud.savingsapp.model.user.BadgeUserId;
import no.ntnu.idi.stud.savingsapp.model.user.User;
import no.ntnu.idi.stud.savingsapp.security.AuthIdentity;
import no.ntnu.idi.stud.savingsapp.service.BadgeService;
import no.ntnu.idi.stud.savingsapp.service.NotificationService;
import no.ntnu.idi.stud.savingsapp.service.UserService;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Controller handling badge related requests.
 */
@CrossOrigin
@RestController
@RequestMapping("/api/badge")
@EnableAutoConfiguration
@Tag(name = "Badge")
@Slf4j
public class BadgeController {

	@Autowired
	private BadgeService badgeService;

	@Autowired
	private UserService userService;

	@Autowired
	private NotificationService notificationService;

	@Autowired
	private ModelMapper modelMapper;

	/**
	 * Retrieves a badge by its id.
	 * @param badgeId The id of the badge to retrieve.
	 * @return ResponseEntity containing the BadgeDTO.
	 */
	@Operation(summary = "Get the budget", description = "Get budget by its id ")
	@ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successfully got budget"),
			@ApiResponse(responseCode = "500", description = "Badge is not found"), })
	@GetMapping(value = "/{badgeId}", produces = MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<BadgeDTO> getBadge(@PathVariable long badgeId) {
		Badge badge = badgeService.findBadgeByBadgeId(badgeId);
		BadgeDTO response = modelMapper.map(badge, BadgeDTO.class);
		log.info("[BadgeController:getBadge] badge: {}", badge.getId());
		return ResponseEntity.ok(response);
	}

	/**
	 * Retrieves all the badges.
	 * @return ResponseEntity containing a list of BadgeDTOs.
	 * @apiNote This endpoint is used to fetch all the badges stored in the database.
	 */
	@Operation(summary = "Get the list of badges", description = "Get all badges stored " + "in the database")
	@ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successfully got badges") })
	@GetMapping(value = "", produces = MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<List<BadgeDTO>> getAllBadges() {
		List<Badge> badges = badgeService.findAllBadges();
		List<BadgeDTO> badgeDTOS = badges.stream().map(badge -> modelMapper.map(badge, BadgeDTO.class)).toList();
		for (BadgeDTO badgeDTO : badgeDTOS) {
			log.info("[BadgeController:getAllBadges] badge: {}", badgeDTO.getId());
		}
		return ResponseEntity.ok(badgeDTOS);
	}

	/**
	 * Retrieves all the badges unlocked by active user.
	 * @param identity The security context of the authenticated user.
	 * @return ResponseEntity containing a list of BadgeDTOs.
	 * @apiNote This endpoint is used to fetch all the badges that are unlocked by the
	 * active user.
	 */
	@Operation(summary = "Get the list of badges", description = "Get all badges unlocked " + "by the user")
	@ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successfully got badges") })
	@GetMapping(value = "unlocked", produces = MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<List<BadgeDTO>> getBadgesUnlockedByActiveUser(
			@AuthenticationPrincipal AuthIdentity identity) {
		List<Badge> badges = badgeService.findBadgesUnlockedByUser(identity.getId());
		List<BadgeDTO> badgeDTOS = badges.stream().map(badge -> modelMapper.map(badge, BadgeDTO.class)).toList();
		log.info("[BadgeController:getBadgesUnlockedByUser] userId: {}", identity.getId());
		for (BadgeDTO badgeDTO : badgeDTOS) {
			log.info("[BadgeController:getBadgesUnlockedByUser] badge: {}", badgeDTO.getId());
		}
		return ResponseEntity.ok(badgeDTOS);
	}

	/**
	 * Retrieves all the badges unlocked by the user.
	 * @param userId The is of the user.
	 * @return ResponseEntity containing a list of BadgeDTOs.
	 * @apiNote This endpoint is used to fetch all the badges that are unlocked by the
	 * user.
	 */
	@Operation(summary = "Get the list of badges", description = "Get all badges unlocked " + "by the user")
	@ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successfully got badges") })
	@GetMapping(value = "unlocked/{userId}", produces = MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<List<BadgeDTO>> getBadgesUnlockedByUser(@PathVariable Long userId) {
		List<Badge> badges = badgeService.findBadgesUnlockedByUser(userId);
		List<BadgeDTO> badgeDTOS = badges.stream().map(badge -> modelMapper.map(badge, BadgeDTO.class)).toList();
		log.info("[BadgeController:getBadgesUnlockedByUser] userId: {}", userId);
		for (BadgeDTO badgeDTO : badgeDTOS) {
			log.info("[BadgeController:getBadgesUnlockedByUser] badge: {}", badgeDTO.getId());
		}
		return ResponseEntity.ok(badgeDTOS);
	}

	/**
	 * Retrieves all the badges not unlocked by the active user.
	 * @param identity The security context of the authenticated user.
	 * @return ResponseEntity containing a list of BadgeDTOs.
	 * @apiNote This endpoint is used to fetch all the badges that are not unlocked by the
	 * user.
	 */
	@Operation(summary = "Get the list of badges", description = "Get all badges not " + "unlocked by the user")
	@ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successfully got badges") })
	@GetMapping(value = "locked", produces = MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<List<BadgeDTO>> getBadgesNotUnlockedByActiveUser(
			@AuthenticationPrincipal AuthIdentity identity) {
		List<Badge> badges = badgeService.findBadgesNotUnlockedByUser(identity.getId());
		List<BadgeDTO> badgeDTOS = badges.stream().map(badge -> modelMapper.map(badge, BadgeDTO.class)).toList();
		log.info("[BadgeController:getBadgesNotUnlockedByUser] userId: {}", identity.getId());
		for (BadgeDTO badgeDTO : badgeDTOS) {
			log.info("[BadgeController:getBadgesNotUnlockedByUser] badge: {}", badgeDTO.getId());
		}
		return ResponseEntity.ok(badgeDTOS);
	}

	/**
	 * Updates the unlocked badges for the active user by checking if a user's score
	 * matches the badges criteria, and if so adds the badge to the user and sends a
	 * notification that the badge is unlocked.
	 * @param identity The security context of the authenticated user.
	 * @return ResponseEntity containing a list of newly unlocked badges.
	 */
	@Operation(summary = "Updates unlocked badges",
			description = "Checks if a user " + "has met the criteria for unlocking badges")
	@ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Successfully updated badges") })
	@GetMapping(value = "update", produces = MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<?> updateUnlockedBadges(@AuthenticationPrincipal AuthIdentity identity) {
		User user = userService.findById(identity.getId());
		System.out.println(user.getPoint().getTotalEarnedPoints());
		List<Badge> badges = badgeService.findNewlyUnlockedBadgesByUserId(identity.getId());
		for (Badge badge : badges) {
			BadgeUserId badgeUserId = new BadgeUserId(badge, user);
			badgeService.addBadgeToUser(badgeUserId);
			// Send a notification that a new badge is unlocked
			Notification notification = new Notification(null, user,
					"You have unlocked a new badge " + badge.getBadgeName(), true, NotificationType.BADGE,
					Timestamp.from(Instant.now()));
			notificationService.updateNotification(notification);
		}
		List<BadgeDTO> badgeDTOS = badges.stream().map(badge -> modelMapper.map(badge, BadgeDTO.class)).toList();
		for (BadgeDTO badgeDTO : badgeDTOS) {
			log.info("[BadgeController:updateUnlockedBadges] badge: {}", badgeDTO.getId());
		}
		return ResponseEntity.ok(badgeDTOS);
	}

}