FridgeService.java

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

import lombok.AllArgsConstructor;
import ntnu.idatt2016.v233.SmartMat.dto.request.product.FridgeProductRequest;
import ntnu.idatt2016.v233.SmartMat.entity.group.Waste;
import ntnu.idatt2016.v233.SmartMat.entity.fridgeProduct.FridgeProductAsso;
import ntnu.idatt2016.v233.SmartMat.entity.group.Fridge;
import ntnu.idatt2016.v233.SmartMat.entity.group.Group;
import ntnu.idatt2016.v233.SmartMat.repository.group.FridgeRepository;
import ntnu.idatt2016.v233.SmartMat.entity.product.Product;
import ntnu.idatt2016.v233.SmartMat.repository.group.GroupRepository;
import ntnu.idatt2016.v233.SmartMat.repository.group.WasteRepository;
import ntnu.idatt2016.v233.SmartMat.repository.product.FridgeProductAssoRepo;
import ntnu.idatt2016.v233.SmartMat.service.product.ProductService;

import ntnu.idatt2016.v233.SmartMat.util.GroupUtil;
import org.springframework.stereotype.Service;

import java.sql.Timestamp;
import java.time.LocalDate;
import java.util.Optional;

/**
 * Service for management of a group fridge
 *
 * @author Anders Austlid & Birk
 * @version 2
 */
@AllArgsConstructor
@Service
public class FridgeService {

    private final FridgeRepository fridgeRepository;
    private final ProductService productService;

    private final GroupRepository groupRepository;

    private final WasteRepository wasteRepository;

    private final FridgeProductAssoRepo fridgeProductAssoRepo;

    /**
     * Gets the fridge of a group
     *
     * @param groupId the id of the group
     * @return the fridge of the group
     */
    public Optional<Fridge> getFridgeByGroupId(long groupId) {
        return fridgeRepository.findByGroupGroupId(groupId);
    }
    
    /**
     * Gets the fridge by its fridge id
     * 
     * @param fridgeId the id of the fridge
     * @return the fridge if it exists
     */
    public Optional<Fridge> getFridgeByFridgeId(long fridgeId) {
        return fridgeRepository.findById(fridgeId);
    }


    /**
     * Adds a product to the fridge of a group
     * @param fridgeProductRequest the fridge product request
     * @return the product that was added to the fridge
     */
    public Optional<Product> addProductToFridge(FridgeProductRequest fridgeProductRequest) {
        Optional<Product> product = productService.getProductById(fridgeProductRequest.ean());
        Optional<Fridge> fridge = fridgeRepository.findByGroupGroupId(fridgeProductRequest.groupId());
        double price = 10.0;
        if(fridgeProductRequest.price() != 0.0){
            price = fridgeProductRequest.price();
        }

        if(product.isEmpty() || fridge.isEmpty()) return Optional.empty();

        fridge.get().addProduct(FridgeProductAsso.builder()
                        .fridgeId(fridge.get())
                        .ean(product.get())
                        .amount(fridgeProductRequest.amount())
                        .daysToExpiration(fridgeProductRequest.days())
                        .purchaseDate(java.sql.Date.valueOf(LocalDate.now()))
                        .buyPrice(price)
                .build());

        fridgeRepository.save(fridge.get());
        return product;

    }

    /**
     * Updates a product in the fridge of a group
     * @param request the fridge product request
     * @return the product that was added to the fridge
     */
    public Optional<FridgeProductAsso> updateProductInFridge(FridgeProductRequest request) {
        Optional<FridgeProductAsso> fridgeProductAsso = fridgeProductAssoRepo.findById(request.fridgeProductId());
        if (fridgeProductAsso.isEmpty()) return Optional.empty();

        fridgeProductAsso.get()
                                .setAmount(request.amount());

        fridgeProductAsso.get()
                                .setDaysToExpiration(request.days());
        


        return Optional.of(fridgeProductAssoRepo.save(fridgeProductAsso.get()));
    }


    /**
     * Remove a product from the fridge of a group
     *
     * @param FPId the id of the fridge product association
     * @return true if the product was removed
     */
    public boolean removeProductFromFridge(long FPId) {
        Optional<FridgeProductAsso> fridgeProductAsso = fridgeProductAssoRepo.findById(FPId);

        if (fridgeProductAsso.isEmpty()) return false;



        fridgeProductAssoRepo.delete(fridgeProductAsso.get());

        return true;
    }



    /**
     * Delete an amount from a fridge product
     * @param fridgeProductId the id of the fridge product
     * @param amount the amount to delete
     * @return an optional containing the fridge product if it exists
     */
    public Optional<FridgeProductAsso> deleteAmountFromFridge(long fridgeProductId, double amount) {
        Optional<FridgeProductAsso> fridgeProductAsso = fridgeProductAssoRepo.findAllById(fridgeProductId);
        if(fridgeProductAsso.isEmpty()) return Optional.empty();
        FridgeProductAsso fridgeProductAsso1 = fridgeProductAsso.get();
        if(amount < fridgeProductAsso1.getAmount() ){
            fridgeProductAsso1.setAmount(fridgeProductAsso1.getAmount() - amount);
            return Optional.of(fridgeProductAssoRepo.save(fridgeProductAsso1));
        } else {
            Group group = fridgeProductAsso1.getFridgeId().getGroup();
            group.setPoints(group.getPoints() + 1);
            group.setLevel(GroupUtil.getLevel(group.getPoints()));
            groupRepository.save(group);
            fridgeProductAssoRepo.delete(fridgeProductAsso.get());
            return Optional.empty();
        }
    }

    /**
     * Deletes a product from the fridge and saves a waste object representing the discarded product.
     * @param fridgeProductId the ID of the fridge product association to delete
     * @return an Optional containing the saved waste object, or an empty Optional if the fridge product association with the given ID is not found
     */
    public Optional<Waste> wasteProductFromFridge(long fridgeProductId){
        Optional<FridgeProductAsso> fridgeProductAsso = fridgeProductAssoRepo.findById(fridgeProductId);
        if(fridgeProductAsso.isEmpty()) return Optional.empty();
        FridgeProductAsso fridgeProductAsso1 = fridgeProductAsso.get();
        fridgeProductAssoRepo.delete(fridgeProductAsso1);
        Group group = fridgeProductAsso1.getFridgeId().getGroup();
        if(group.getPoints() > 10.0){
            group.setPoints(group.getPoints() - 1);
            group.setLevel(GroupUtil.getLevel(group.getPoints()));
        }
        groupRepository.save(group);
        return Optional.of(wasteRepository.save(Waste.builder()
                        .buyPrice(fridgeProductAsso1.getBuyPrice())
                .amount(fridgeProductAsso1.getAmount())
                .unit(fridgeProductAsso1.getEan().getUnit())
                .ean(fridgeProductAsso1.getEan())
                .groupId(fridgeProductAsso1.getFridgeId().getGroup())
                .timestamp(new Timestamp(System.currentTimeMillis()))
                .build()));
    }


    /**
     * Get all the fridge products of a group
     * @param username the username of the user
     * @param fridgeProductId the id of the fridge product
     * @return true if the user is in the group of the fridge product
     */
    public boolean isUserInGroupWithFridgeProduct(String username, long fridgeProductId) {
        Optional<Fridge> fridge = fridgeProductAssoRepo.findById(fridgeProductId)
                .map(FridgeProductAsso::getFridgeId);
        return fridge.map(value -> value.getGroup().getUser().stream()
                .anyMatch(user -> user.getUser().getUsername().equals(username))).orElse(false);
    }

    /**
     * check if user has accsess to fridge
     * @param username the username of the user
     * @param fridgeId the id of the fridge
     * @return true if the user is in the group of the fridge
     */
    public boolean isUserInFridge(String username, long fridgeId) {
        Optional<Fridge> fridge = fridgeRepository.findById(fridgeId);
        return fridge.map(value -> value.getGroup().getUser().stream()
                .anyMatch(user -> user.getUser().getUsername().equals(username))).orElse(false);
    }


    /**
     * Get the group id of a fridge product
     * @param fridgeProductId the id of the fridge product
     * @return the id of the group of the fridge product
     */
    public long getGroupIdFromFridgeProuctId(long fridgeProductId){
        return fridgeProductAssoRepo.findById(fridgeProductId)
                .map(fridgeProductAsso -> fridgeProductAsso.getFridgeId().getGroup().getGroupId()).orElse(0L);

    }
}