FridgeController.java

package ntnu.idatt2016.v233.SmartMat.controller.group;

import lombok.AllArgsConstructor;
import ntnu.idatt2016.v233.SmartMat.dto.enums.Authority;
import ntnu.idatt2016.v233.SmartMat.dto.request.product.FridgeProductRequest;
import ntnu.idatt2016.v233.SmartMat.entity.fridgeProduct.FridgeProductAsso;
import ntnu.idatt2016.v233.SmartMat.entity.group.Fridge;
import ntnu.idatt2016.v233.SmartMat.entity.product.Product;
import ntnu.idatt2016.v233.SmartMat.service.group.FridgeService;
import ntnu.idatt2016.v233.SmartMat.service.group.GroupService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.web.bind.annotation.*;

import java.util.Optional;


/**
 * Controller for fridges API, providing endpoints for fridge management
 *
 * @author Anders Austlid & Birk
 * @version 2.0
 * @since 5.05.2023
 */
@AllArgsConstructor
@RestController
@RequestMapping("/api/fridges")
public class FridgeController {

    private final FridgeService fridgeService;

    private final GroupService groupService;


    /**
     * Gets the fridge of a group
     * @param groupId the id of the group must exist
     * @return the fridge of the group if it exists, or a 404 if it doesn't exist or the user is not in the group
     */
    @GetMapping("/group/{groupId}")
    public ResponseEntity<Fridge> getFridgeByGroupId(@PathVariable("groupId") long groupId, Authentication authentication) {
        Optional<Fridge> fridge = fridgeService.getFridgeByGroupId(groupId);

        if (fridge.isEmpty()) {
            return ResponseEntity.notFound().build();
        }

        if (!fridgeService.isUserInFridge(authentication.getName(), fridge.get().getFridgeId())
                && !authentication.getAuthorities().contains(new SimpleGrantedAuthority(Authority.ADMIN.name()))) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }

        return fridge.map(ResponseEntity::ok).get();
    }


    /**
     * Gets the fridge by its fridge id
     * @param fridgeId the id of the fridge to get
     * @param authentication a user authentication object for validation of user authorization
     * @return the fridge if it exists, or a 404 if it doesn't, or a 403 if the user is not in the fridge
     */
    @GetMapping("/fridge/{fridgeId}")
    public ResponseEntity<Fridge> getFridgeByFridgeId(@PathVariable("fridgeId") long fridgeId,
                                                      Authentication authentication) {
        if (!fridgeService.isUserInFridge(authentication.getName(), fridgeId)
                && !authentication.getAuthorities().contains(new SimpleGrantedAuthority(Authority.ADMIN.name()))) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }

        return fridgeService.getFridgeByFridgeId(fridgeId)
                .map(ResponseEntity::ok)
                .orElseGet(() -> ResponseEntity.notFound().build());
    }


    /**
     * Adds a product to the fridge of a group
     *
     * @param request the request containing the group id and product id
     * @param authentication a user authentication object for validation of user authorization
     * @return success if the product was added, bad request if the product was already in the fridge, or not found if the group or product doesn't exist
     */
    @PostMapping("/group/product")
    public ResponseEntity<Product> addProductToFridge(@RequestBody FridgeProductRequest request,
                                                      Authentication authentication) {

        Optional<Fridge> fridge = fridgeService.getFridgeByGroupId(request.groupId());

        if (fridge.isEmpty()) {
            return ResponseEntity.notFound().build();
        }
        if(authentication.getAuthorities().stream().noneMatch(a -> a.getAuthority().equals(Authority.ADMIN.name()))){
            if (!fridgeService.isUserInFridge(authentication.getName(), fridge.get().getFridgeId())) {
                return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
            }
        }

        try {
            return fridgeService.addProductToFridge(request).map(ResponseEntity::ok)
                    .orElseGet(() -> ResponseEntity.notFound().build());
        } catch (IllegalArgumentException e) {
            return ResponseEntity.badRequest().build();
        }
    }

    /**
     * Updates a product in a fridge
     * @param request the request containing the group id and product id
     * @param authentication a user authentication object for validation of user authorization
     * @return success if the product was added, bad request if the product was already in the fridge,
     * or not found if the group or product doesn't exist
     */
    @PutMapping("/group/product")
    public ResponseEntity<FridgeProductAsso> updateProductInFridge(@RequestBody FridgeProductRequest request,
                                                                   Authentication authentication) {
        Optional<Fridge> fridge = fridgeService.getFridgeByGroupId(request.groupId());

        if (fridge.isEmpty()) {
            return ResponseEntity.notFound().build();
        }

        if(authentication.getAuthorities().stream().noneMatch(a -> a.getAuthority().equals(Authority.ADMIN.name()))){
            if (!fridgeService.isUserInFridge(authentication.getName(), fridge.get().getFridgeId())) {
                return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
            }

            if(groupService.getUserGroupAssoAuthority(authentication.getName(), request.groupId())
                    .equalsIgnoreCase("RESTRICTED"))
                return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }

        return fridgeService.updateProductInFridge(request).map(ResponseEntity::ok).orElseGet(()-> ResponseEntity.notFound().build());
    }

    /**
     * Deletes an amount of a product from a fridge
     * @param fridgeProductId the id of the fridge product to delete
     * @param amountStr      the amount to delete
     * @param authentication the authentication of the user
     * @return 200 if the amount was deleted, 404 if the fridge product doesn't exist, 403 if the user is not in the group
     */
    @DeleteMapping("/group/delete/product/{fridgeProductId}/{amount}")
    public ResponseEntity<?> deleteAmountFridgeProduct(@PathVariable("fridgeProductId") long fridgeProductId,
                                                       @PathVariable("amount") String amountStr, Authentication authentication) {


        if(authentication.getAuthorities().stream().noneMatch(a -> a.getAuthority().equals(Authority.ADMIN.name()))){
            if (!fridgeService.isUserInGroupWithFridgeProduct(authentication.getName(), fridgeProductId)) {
                return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
            }

            if(groupService.getUserGroupAssoAuthority(authentication.getName(),
                    fridgeService.getGroupIdFromFridgeProuctId(fridgeProductId))
                    .equalsIgnoreCase("RESTRICTED")
                   )
                return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }

        try {
            double amount = Double.parseDouble(amountStr);

            if (amount < 0.0) {
                return ResponseEntity.badRequest().body("Amount must be greater than or equal to 0.");
            }

            return ResponseEntity.ok(fridgeService.deleteAmountFromFridge(fridgeProductId, amount));
        } catch (NumberFormatException e) {
            return ResponseEntity.badRequest().body("Invalid amount format. Please provide a valid number.");
        }
    }



    /**
     * Deletes a product from the fridge
     * @param fridgeProductId the id of the fridge product association
     * @param authentication the authentication of the user
     * @return success if the product was deleted, bad request if the product wasn't found
     * , or forbidden if the user is not in the group
     */
    @DeleteMapping("/delete/product/{fridgeProductId}")
    public ResponseEntity<String> removeProductFromFridge(@PathVariable("fridgeProductId") long fridgeProductId,
                                                          Authentication authentication) {

        if(authentication.getAuthorities().stream().noneMatch(a -> a.getAuthority().equals(Authority.ADMIN.name()))){
            if (!fridgeService.isUserInGroupWithFridgeProduct(authentication.getName(), fridgeProductId)) {
                return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
            }

            if(groupService.getUserGroupAssoAuthority(authentication.getName(),
                            fridgeService.getGroupIdFromFridgeProuctId(fridgeProductId))
                    .equalsIgnoreCase("RESTRICTED")
            )
                return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }

        try {
            return (fridgeService.removeProductFromFridge(fridgeProductId)) ?
                    ResponseEntity.ok("Product removed from fridge")
                    : ResponseEntity.status(HttpStatus.NOT_FOUND).body("Product not found in the fridge");

        } catch (Exception e) {
            return ResponseEntity.status(500).body("Internal server error");
        }
    }

    /**
     * Deletes a product from the fridge and creates a waste object from it.
     *
     * @param fridgeProductId The id of the fridge product association to be deleted
     * @param authentication The authentication of the user
     * @return A ResponseEntity with status code 200 if successful,
     * or status code 404 if the specified fridge product association was not found.
     * or status code 403 if the user is not in the group
     */
    @DeleteMapping("/waste/product/{fridgeProductId}")
    public ResponseEntity<?> wasteProductFromFridge(@PathVariable("fridgeProductId") long fridgeProductId,
                                                    Authentication authentication){
        if(authentication.getAuthorities().stream().noneMatch(a -> a.getAuthority().equals(Authority.ADMIN.name()))){
            if (!fridgeService.isUserInGroupWithFridgeProduct(authentication.getName(), fridgeProductId)) {
                return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
            }

            if(groupService.getUserGroupAssoAuthority(authentication.getName(),
                            fridgeService.getGroupIdFromFridgeProuctId(fridgeProductId))
                    .equalsIgnoreCase("RESTRICTED")
            )
                return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }

        return fridgeService.wasteProductFromFridge(fridgeProductId)
                .map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
    }


}