ChatMessageController.java

package edu.ucsb.cs156.happiercows.controllers;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;

import edu.ucsb.cs156.happiercows.entities.ChatMessage;
import edu.ucsb.cs156.happiercows.repositories.ChatMessageRepository;

import edu.ucsb.cs156.happiercows.entities.User;
import edu.ucsb.cs156.happiercows.entities.UserCommons;
import edu.ucsb.cs156.happiercows.repositories.UserCommonsRepository;

import org.springframework.security.core.Authentication;


import java.util.Optional;

@Tag(name = "Chat Message")
@RequestMapping("/api/chat")
@RestController
@Slf4j
public class ChatMessageController extends ApiController{

    @Autowired
    private ChatMessageRepository chatMessageRepository;

    @Autowired
    private UserCommonsRepository userCommonsRepository;

    @Autowired
    ObjectMapper mapper;

    @Operation(summary = "Get all chat messages", description = "Get all chat messages associated with a specific commons.")
    @PreAuthorize("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
    @GetMapping("/get")
    public ResponseEntity<Object> getChatMessages(@Parameter(description = "The id of the common") @RequestParam Long commonsId,
                                            @Parameter(name="page") @RequestParam int page,
                                            @Parameter(name="size") @RequestParam int size) {
        
        // Make sure the user is part of the commons or is an admin
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (!auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))){
            log.info("User is not an admin");
            User user = getCurrentUser().getUser();
            Long userId = user.getId();
            Optional<UserCommons> userCommonsLookup = userCommonsRepository.findByCommonsIdAndUserId(commonsId, userId);

            if (!userCommonsLookup.isPresent()) {
                return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
            }
        }

        // Return the list of non-hidden chat messages
        Page<ChatMessage> messages = chatMessageRepository.findByCommonsId(commonsId, PageRequest.of(page, size, Sort.by("timestamp").descending()));
        return ResponseEntity.ok(messages);
    }

    @Operation(summary = "Get all chat messages (Admins)", description = "Get all chat messages associated with a specific commons, even the hidden ones. Used only by admins")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @GetMapping("/admin/get")
    public ResponseEntity<Object> getAllChatMessages(@Parameter(description = "The id of the common") @RequestParam Long commonsId,
                                                    @Parameter(name="page") @RequestParam int page,
                                                    @Parameter(name="size") @RequestParam int size) {
        
        // Return the list of chat messages
        Page<ChatMessage> messages = chatMessageRepository.findAllByCommonsId(commonsId, PageRequest.of(page, size, Sort.by("timestamp").descending()));
        return ResponseEntity.ok(messages);
    }

    
    @Operation(summary = "Get hidden chat messages", description = "Get all hidden chat messages associated with a specific commons. Used only by admins")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @GetMapping("/admin/hidden")
    public ResponseEntity<Object> getHiddenChatMessages(@Parameter(description = "The id of the common") @RequestParam Long commonsId,
                                                    @Parameter(name="page") @RequestParam int page,
                                                    @Parameter(name="size") @RequestParam int size) {
        
        // Return the list of hidden chat messages
        Page<ChatMessage> messages = chatMessageRepository.findByCommonsIdAndHidden(commonsId, PageRequest.of(page, size, Sort.by("timestamp").descending()));
        return ResponseEntity.ok(messages);
    }
    
    
    @Operation(summary = "Create a chat message", description = "Create a chat message associated with a specific commons")
    @PreAuthorize("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
    @PostMapping("/post")
    public ResponseEntity<Object> createChatMessage(@Parameter(description = "The id of the common") @RequestParam Long commonsId,
                                                    @Parameter(description = "The message to be sent") @RequestParam String content) {
        
        User user = getCurrentUser().getUser();
        Long userId = user.getId();

        // Make sure the user is part of the commons or is an admin
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (!auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))){
            log.info("User is not an admin");
            Optional<UserCommons> userCommonsLookup = userCommonsRepository.findByCommonsIdAndUserId(commonsId, userId);

            if (!userCommonsLookup.isPresent()) {
                return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
            }
        }

        // Create the chat message
        ChatMessage chatMessage = ChatMessage.builder()
        .commonsId(commonsId)
        .userId(userId)
        .message(content)
        .hidden(false)
        .dm(false)
        .toUserId(0)
        .build();

        // Save the message
        chatMessageRepository.save(chatMessage);

        return ResponseEntity.ok(chatMessage);
    }

    @Operation(summary = "Delete a chat message", description = "Delete a chat message associated with a specific commons")
    @PreAuthorize("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
    @PutMapping("/hide")
    public ResponseEntity<Object> hideChatMessage(@Parameter(description = "The id of the chat message") @RequestParam Long chatMessageId) {

        // Try to get the chat message
        Optional<ChatMessage> chatMessageLookup = chatMessageRepository.findById(chatMessageId);
        if (!chatMessageLookup.isPresent()) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }
        ChatMessage chatMessage = chatMessageLookup.get();

        User user = getCurrentUser().getUser();
        Long userId = user.getId();

        // Check if the user is the author of the message
        if (chatMessage.getUserId() != userId) {
            // Check if the user is an admin
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            if (!auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))){
                return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
            }
        }

        // Hide the message
        chatMessage.setHidden(true);
        chatMessageRepository.save(chatMessage);

        return ResponseEntity.ok(chatMessage);
    }

    

}