FriendController.java

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

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 lombok.extern.slf4j.Slf4j;
import no.ntnu.idi.stud.savingsapp.dto.user.UserDTO;
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.Friend;
import no.ntnu.idi.stud.savingsapp.model.user.User;
import no.ntnu.idi.stud.savingsapp.security.AuthIdentity;
import no.ntnu.idi.stud.savingsapp.service.FriendService;
import no.ntnu.idi.stud.savingsapp.service.NotificationService;
import no.ntnu.idi.stud.savingsapp.service.UserService;

import java.util.List;
import java.util.ArrayList;

import org.modelmapper.ModelMapper;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

/**
 * Controller class for handling friend-related requests.
 */
@RestController
@RequestMapping("/api/friends")
@Tag(name = "Friend", description = "API for managing friend relationships")
@Slf4j
public class FriendController {

	private final UserService userService;

	private final FriendService friendService;

	private final NotificationService notificationService;

	private final ModelMapper modelMapper;

	public FriendController(UserService userService, FriendService friendService,
			NotificationService notificationService, ModelMapper modelMapper) {
		this.userService = userService;
		this.friendService = friendService;
		this.notificationService = notificationService;
		this.modelMapper = modelMapper;
	}

	@Operation(summary = "Get all friends", description = "Returns a list of all friends.")
	@ApiResponses({ @ApiResponse(responseCode = "200", description = "Successfully retrieved list of friends") })
	@GetMapping
	public ResponseEntity<List<UserDTO>> getFriends(@AuthenticationPrincipal AuthIdentity identity) {
		List<User> friendsUser = userService.getFriends(identity.getId());
		log.info("[FriendController:getFriends] user: {}", identity.getId());
		for (User user : friendsUser) {
			log.info("[FriendController:getFriends] friend: {}", user.getId());
		}
		return ResponseEntity.ok(convertToDto(friendsUser));
	}

	@Operation(summary = "Get friend requests",
			description = "Returns a list of all users who have sent a friend request.")
	@ApiResponses({ @ApiResponse(responseCode = "200", description = "Successfully retrieved friend requests") })
	@GetMapping("/requests")
	public ResponseEntity<List<UserDTO>> getFriendRequests(@AuthenticationPrincipal AuthIdentity identity) {
		List<User> friendsUser = userService.getFriendRequests(identity.getId());
		log.info("[FriendController:getFriendRequests] user: {}", identity.getId());
		for (User user : friendsUser) {
			log.info("[FriendController:getFriendRequests] friend requests: {}", user.getId());
		}
		return ResponseEntity.ok(convertToDto(friendsUser));
	}

	@Operation(summary = "Send a friend request",
			description = "Sends a new friend request to another user. A notification is sent to this user")
	@ApiResponses({ @ApiResponse(responseCode = "201", description = "Friend request successfully created") })
	@PostMapping("/{userId}")
	@ResponseStatus(HttpStatus.CREATED)
	public void addFriendRequest(@AuthenticationPrincipal AuthIdentity identity, @PathVariable long userId) {
		User user = userService.findById(identity.getId());
		User friend = userService.findById(userId);
		friendService.addFriendRequest(user, friend);
		Notification notification = new Notification(null, friend,
				"You have received a new friend request from " + user.getFirstName(), true,
				NotificationType.FRIEND_REQUEST, Timestamp.from(Instant.now()));
		notificationService.updateNotification(notification);
		log.info("[FriendController:addFriendRequest] from: {} to: {}", user.getId(), friend.getId());
	}

	@Operation(summary = "Accept a friend request", description = "Accepts a friend request from another user.")
	@ApiResponses({ @ApiResponse(responseCode = "200", description = "Friend request successfully accepted"),
			@ApiResponse(responseCode = "404", description = "Friend request not found"), })
	@PutMapping("/{friendId}")
	public ResponseEntity<?> acceptFriendRequest(@AuthenticationPrincipal AuthIdentity identity,
			@PathVariable long friendId) {
		User user = userService.findById(identity.getId());
		User friend = userService.findById(friendId);
		Friend friendRequest = friendService.getFriendRequest(user, friend);
		if (friendRequest == null) {
			log.error("[FriendController:acceptFriendRequest] No friend request found");
			return ResponseEntity.status(HttpStatus.NOT_FOUND).body("No friend request found.");
		}
		friendService.acceptFriendRequest(friendRequest);
		log.info("[FriendController:acceptFriendRequest] Friend request successfully accepted between: {} and: {}",
				user.getId(), friend.getId());
		return ResponseEntity.ok().build();
	}

	@Operation(summary = "Delete a friend or cancel a friend request",
			description = "Deletes an existing friend from your friend list or cancels a received friend request.")
	@ApiResponses({
			@ApiResponse(responseCode = "200", description = "Friend successfully deleted or friend request cancelled"),
			@ApiResponse(responseCode = "404", description = "Friend or friend request not found"), })
	@DeleteMapping("/{friendId}")
	public ResponseEntity<?> deleteFriendOrFriendRequest(@AuthenticationPrincipal AuthIdentity identity,
			@PathVariable long friendId) {
		User user = userService.findById(identity.getId());
		User friend = userService.findById(friendId);

		Friend friendStatus = friendService.getFriendStatus(user, friend);
		if (friendStatus == null) {
			log.error("[FriendController:deleteFriendOrFriendRequest] No friend relationship or friend request found");
			return ResponseEntity.status(HttpStatus.NOT_FOUND).body("No friend relationship or friend request found.");
		}

		friendService.deleteFriendOrFriendRequest(friendStatus);
		log.info("[FriendController:acceptFriendRequest] Friend request successfully deleted between: {} and: {}",
				user.getId(), friend.getId());
		return ResponseEntity.ok().build();
	}

	/**
	 * Converts a list of {@link User} objects to a list of {@link UserDTO} objects.
	 * @param users the list of users to convert
	 * @return The converted list of UserDTO objects
	 */
	private List<UserDTO> convertToDto(List<User> users) {
		List<UserDTO> userDTOs = new ArrayList<>();
		for (User user : users) {
			UserDTO userDTO = modelMapper.map(user, UserDTO.class);
			userDTOs.add(userDTO);
		}
		return userDTOs;
	}

}