package com.wishurgift.service.bean;

import com.wishurgift.persistence.entity.Element;
import com.wishurgift.persistence.entity.Image;
import com.wishurgift.persistence.entity.User;
import com.wishurgift.persistence.entity.Wishlist;
import com.wishurgift.persistence.repository.ElementRepository;
import com.wishurgift.persistence.repository.UserRepository;
import com.wishurgift.persistence.repository.WishlistRepository;
import com.wishurgift.service.ElementService;
import com.wishurgift.service.ImageService;
import com.wishurgift.service.WishlistService;
import com.wishurgift.service.dto.ElementDTO;
import com.wishurgift.service.dto.ImageDTO;
import com.wishurgift.service.dto.UserDTO;
import com.wishurgift.service.dto.WishlistDTO;
import com.wishurgift.service.dto.request.ElementRequestDTO;
import com.wishurgift.service.dto.request.ImageRequestDTO;
import com.wishurgift.service.dto.transformer.ElementTransformer;
import com.wishurgift.utils.Constant;
import com.wishurgift.utils.StringUtils;

import javax.ejb.Stateless;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Stateless
public class ElementServiceBean implements ElementService {

    @Inject
    private ElementRepository repository;

    @Inject
    private WishlistRepository wishlistRepository;

    @Inject
    private UserRepository userRepository;

    @Inject
    private WishlistService wishlistService;

    @Inject
    private ImageService imageService;

    @Override
    public ElementDTO add(WishlistDTO wishlist, ElementRequestDTO request) {
        Optional<Wishlist> optionalWishlist = wishlistRepository.findById(wishlist.getId());
        return optionalWishlist.map(value -> add(value, request)).orElse(null);
    }

    @Override
    public ElementDTO add(Wishlist wishlist, ElementRequestDTO request) {
        // Création de l'element
        Element element = new Element();
        element.setWishlist(wishlist);
        element.setName(request.getName());
        element.setStatus(Constant.Status.IDEA.getName());
        if (StringUtils.isNotBlank(request.getLink())) {
            element.setLink(request.getLink());
        }
        if (StringUtils.isNotBlank(request.getAddress())) {
            element.setAddress(request.getAddress());
        }
        if (StringUtils.isNotBlank(request.getDescription())) {
            element.setDescription(request.getDescription());
        }
        if (request.getPrice() != null) {
            element.setPrice(request.getPrice());
        }
        if (request.getImage() != null && !request.getImage().isEmpty()) {
            List<Image> images = new ArrayList<>();
            request.getImage().forEach(elt -> {
                Image image = new Image();
                image.setExt(elt.getExt());
                image.setData(elt.getData());
                image.setElement(element);
                images.add(image);
            });
            element.setImages(images);
        }
        // Sauvegarde
        wishlist.getElements().add(element);
        wishlistRepository.update(wishlist, true);
        return ElementTransformer.entityToDto(wishlist.getElements().get(wishlist.getElements().size() - 1));
    }

    @Override
    public ElementDTO update(UserDTO principal, long id, ElementRequestDTO request) {
        // Recupèration de l'élement
        Optional<Element> optionalElement = repository.findById(id);
        if (!optionalElement.isPresent()) {
            return null;
        }
        Element element = optionalElement.get();
        // Verifie que l'utilisateur est le proprietaire de la liste de l'element
        if (principal.getId() != element.getWishlist().getUser().getId()) {
            return null;
        }
        // Met à jour l'element
        if (StringUtils.isNotBlank(request.getName())) {
            element.setName(request.getName());
        }
        if (StringUtils.isNotBlank(request.getLink())) {
            element.setLink(request.getLink());
        }
        if (StringUtils.isNotBlank(request.getDescription())) {
            element.setDescription(request.getDescription());
        }
        if (StringUtils.isNotBlank(request.getAddress())) {
            element.setAddress(request.getAddress());
        }
        if (request.getPrice() != null) {
            element.setPrice(request.getPrice());
        }
        // Sauvegarde
        repository.update(element);
        return ElementTransformer.entityToDto(element);
    }

    @Override
    public boolean remove(UserDTO principal, long id) {
        // Recupèration de l'élement
        Optional<Element> optionalElement = repository.findById(id);
        if (!optionalElement.isPresent()) {
            return false;
        }
        Element element = optionalElement.get();
        // Verifie que l'utilisateur est le proprietaire de la liste de l'element
        if (principal.getId() != element.getWishlist().getUser().getId()) {
            return false;
        }
        // Supprime
        List<Element> elements = element.getWishlist().getElements();
        if (!elements.remove(element)) {
            return false;
        }
        wishlistRepository.update(element.getWishlist());
        repository.delete(element);
        return true;
    }

    @Override
    public ElementDTO getElement(UserDTO principal, long id) {
        // Recupèration de l'élement
        Optional<Element> optionalElement = repository.findById(id);
        if (!optionalElement.isPresent()) {
            return null;
        }
        Element element = optionalElement.get();
        // Verification que l'utilisateur à accès à l'element
        WishlistDTO wishlistDTO = wishlistService.getWishlist(element.getWishlist().getId(), principal);
        if (wishlistDTO == null) {
            return null;
        }
        return ElementTransformer.entityToDto(element);
    }

    @Override
    public ImageDTO addImage(UserDTO principal, long id, ImageRequestDTO request) {
        // Recupèration de l'élement
        Optional<Element> optionalElement = repository.findById(id);
        if (!optionalElement.isPresent()) {
            return null;
        }
        Element element = optionalElement.get();
        // Verification que l'utilisateur est le proprietaire de la liste de l'element
        if (principal.getId() != element.getWishlist().getUser().getId()) {
            return null;
        }
        // Ajoute
        return imageService.add(element, request);
    }

    @Override
    public ElementDTO setStatus(UserDTO principal, long id, Constant.Status status) {
        // Recupèration de l'élement
        Optional<Element> optionalElement = repository.findById(id);
        if (!optionalElement.isPresent()) {
            return null;
        }
        Element element = optionalElement.get();
        // Regarde le type de modification du status
        Constant.Status actual = Constant.Status.fromName(element.getStatus());
        int action = actual.getPos() - status.getPos();
        if (action < 0) {
            element = forwardStatus(principal, element, actual, status);
        } else if (action == 0) {
            element = changeStatus(principal, element, status);
        } else {
            element = backwardStatus(principal, element, status);
        }
        repository.update(element);
        return ElementTransformer.entityToDto(element);
    }

    /**
     * Change un status pas un status de la même position
     *
     * @param principal L'utilisateur qui fait la demande
     * @param element   L'element à modifier
     * @param status    Le nouveau status
     * @return L'element modifié ou null
     */
    private Element changeStatus(UserDTO principal, Element element, Constant.Status status) {
        // L'utilisateur doit être celui qui possède l'element
        if (principal.getId() != element.getWishlist().getUser().getId()) {
            return null;
        }
        // Changement status
        element.setStatus(status.getName());
        return element;
    }

    /**
     * Change un status pas un status du position suivante
     *
     * @param principal L'utilisateur qui fait la demande
     * @param element   L'element à modifier
     * @param actual    Le status actuel
     * @param newStatus Le nouveau status
     * @return L'element modifié ou null
     */
    private Element forwardStatus(UserDTO principal, Element element, Constant.Status actual, Constant.Status newStatus) {
        // Achat par quelqu'un
        if (actual.getPos() == 1) {
            // Verifie que l'utilisateur à accès à l'element
            WishlistDTO wishlistDTO = wishlistService.getWishlist(element.getWishlist().getId(), principal);
            if (wishlistDTO == null) {
                return null;
            }
            // Récupère l'utilisateur
            Optional<User> optionalUser = userRepository.findById(principal.getId());
            if (!optionalUser.isPresent()) {
                return null;
            }
            User user = optionalUser.get();
            // Ajoute l'utilisateur en tant que buyer
            element.setBuyer(user);
        }
        // Anvancement de l'achat par quelqu'un
        else {
            // L'utilisateur doit être le buyer
            if (element.getBuyer() == null || principal.getId() != element.getBuyer().getId()) {
                return null;
            }
        }
        // Change le status
        element.setStatus(newStatus.getName());
        return element;
    }

    /**
     * Change un status pas un status d'une position prècedente
     *
     * @param principal L'utilisateur qui fait la demande
     * @param element   L'element à modifier
     * @param status    Le nouveau status
     * @return L'element modifié ou null
     */
    private Element backwardStatus(UserDTO principal, Element element, Constant.Status status) {
        // Verifie que l'utilisateur est l'acheteur
        if (element.getBuyer() == null || principal.getId() != element.getBuyer().getId()) {
            return null;
        }
        // Impossible de revenir sur crush sauf si l'acheteur est le proprietaire de la liste
        if (status == Constant.Status.CRUSH && principal.getId() != element.getWishlist().getUser().getId()) {
            return null;
        }
        // Si on retourne sur idea on retire le buyer
        if (status == Constant.Status.IDEA) {
            element.setBuyer(null);
        }
        element.setStatus(status.getName());
        return element;
    }

}
