package com.wishurgift.service;

import com.wishurgift.helper.ElementMaker;
import com.wishurgift.helper.UserMaker;
import com.wishurgift.helper.WishlistMaker;
import com.wishurgift.persistence.entity.Element;
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.bean.ElementServiceBean;
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 org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import java.util.Optional;

import static org.junit.Assert.*;

public class ElementServiceTest {

    @Mock
    private ElementRepository elementRepository;

    @Mock
    private WishlistRepository wishlistRepository;

    @Mock
    private WishlistService wishlistService;

    @Mock
    private UserRepository userRepository;

    @Mock
    private ImageService imageService;

    @InjectMocks
    private ElementServiceBean service;

    @Before
    public void before() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void addTest() {
        Wishlist wishlist = WishlistMaker.makeWishlist();
        ElementRequestDTO expected = ElementMaker.makeElementRequestDTO();

        ElementDTO actual = service.add(wishlist, expected);
        assertEquals(expected.getName(), actual.getName());
        assertEquals(expected.getLink(), actual.getLink());
        assertEquals(expected.getAddress(), actual.getAddress());
        assertEquals(expected.getDescription(), actual.getDescription());
        assertEquals(expected.getPrice(), actual.getPrice(), 0);
        assertEquals(expected.getImage().size(), actual.getImage().size());
        assertEquals(Constant.Status.IDEA.getName(), actual.getStatus());
    }

    @Test
    public void updateTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        ElementRequestDTO request = ElementMaker.makeElementRequestDTO();
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element expected = wishlist.getElements().get(0);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(expected));

        ElementDTO actual = service.update(userDTO, id, request);
        assertEquals(ElementTransformer.entityToDto(expected), actual);
    }

    @Test
    public void elementNotFoundUpdateTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        ElementRequestDTO request = ElementMaker.makeElementRequestDTO();

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.empty());

        ElementDTO actual = service.update(userDTO, id, request);
        assertNull(actual);
    }

    @Test
    public void unauthorizedUpdateTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        ElementRequestDTO request = ElementMaker.makeElementRequestDTO();
        Wishlist wishlist = WishlistMaker.makeWishlist();
        wishlist.getUser().setId(2);
        Element expected = wishlist.getElements().get(0);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(expected));

        ElementDTO actual = service.update(userDTO, id, request);
        assertNull(actual);
    }

    @Test
    public void getElementTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        WishlistDTO wishlistDTO = WishlistMaker.makeWishlistDTO();
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element expected = wishlist.getElements().get(0);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(expected));
        Mockito.when(wishlistService.getWishlist(wishlist.getId(), userDTO)).thenReturn(wishlistDTO);

        ElementDTO actual = service.getElement(userDTO, id);
        assertEquals(ElementTransformer.entityToDto(expected), actual);
    }

    @Test
    public void elementNotFoundGetElementTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.empty());

        ElementDTO actual = service.getElement(userDTO, id);
        assertNull(actual);
    }

    @Test
    public void unauthorizedGetElementTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element expected = wishlist.getElements().get(0);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(expected));
        Mockito.when(wishlistService.getWishlist(wishlist.getId(), userDTO)).thenReturn(null);

        ElementDTO actual = service.getElement(userDTO, id);
        assertNull(actual);
    }

    @Test
    public void removeTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));

        assertTrue(service.remove(userDTO, id));
    }

    @Test
    public void elementNotFoundRemoveTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.empty());

        assertFalse(service.remove(userDTO, id));
    }

    @Test
    public void unauthorizedRemoveTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        userDTO.setId(2);
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));

        assertFalse(service.remove(userDTO, id));
    }

    @Test
    public void notInListRemoveTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);
        wishlist.getElements().remove(0);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));

        assertFalse(service.remove(userDTO, id));
    }

    @Test
    public void setStatusChangeTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Constant.Status status = Constant.Status.CRUSH;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));

        ElementDTO actual = service.setStatus(userDTO, id, status);
        element.setStatus(status.getName());
        assertEquals(ElementTransformer.entityToDto(element), actual);
    }

    @Test
    public void failSetStatusChangeTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Constant.Status status = Constant.Status.CRUSH;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        wishlist.getUser().setId(2);
        Element element = wishlist.getElements().get(0);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));

        ElementDTO actual = service.setStatus(userDTO, id, status);
        assertNull(actual);
    }

    @Test
    public void setStatusForward1Test() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Constant.Status status = Constant.Status.RESERVED;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);
        WishlistDTO wishlistDTO = WishlistMaker.makeWishlistDTO();
        User user = UserMaker.makeUser();

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));
        Mockito.when(wishlistService.getWishlist(wishlist.getId(), userDTO)).thenReturn(wishlistDTO);
        Mockito.when(userRepository.findById(userDTO.getId())).thenReturn(Optional.of(user));

        ElementDTO actual = service.setStatus(userDTO, id, status);
        element.setStatus(status.getName());
        assertEquals(ElementTransformer.entityToDto(element), actual);
    }

    @Test
    public void setStatusForward2Test() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Constant.Status status = Constant.Status.PURCHASED;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);
        User user = UserMaker.makeUser();
        element.setStatus(Constant.Status.RESERVED.getName());
        element.setBuyer(user);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));

        ElementDTO actual = service.setStatus(userDTO, id, status);
        element.setStatus(status.getName());
        assertEquals(ElementTransformer.entityToDto(element), actual);
    }

    @Test
    public void noAccesSetStatusForward1Test() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Constant.Status status = Constant.Status.RESERVED;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));
        Mockito.when(wishlistService.getWishlist(wishlist.getId(), userDTO)).thenReturn(null);

        ElementDTO actual = service.setStatus(userDTO, id, status);
        assertNull(actual);
    }

    @Test
    public void userNotFoundSetStatusForward1Test() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Constant.Status status = Constant.Status.RESERVED;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);
        WishlistDTO wishlistDTO = WishlistMaker.makeWishlistDTO();

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));
        Mockito.when(wishlistService.getWishlist(wishlist.getId(), userDTO)).thenReturn(wishlistDTO);
        Mockito.when(userRepository.findById(userDTO.getId())).thenReturn(Optional.empty());

        ElementDTO actual = service.setStatus(userDTO, id, status);
        assertNull(actual);
    }

    @Test
    public void invalidBuyerSetStatusForward2Test() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Constant.Status status = Constant.Status.PURCHASED;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);
        User user = UserMaker.makeUser();
        user.setId(2);
        element.setStatus(Constant.Status.RESERVED.getName());
        element.setBuyer(user);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));

        ElementDTO actual = service.setStatus(userDTO, id, status);
        assertNull(actual);
    }

    @Test
    public void noBuyerSetStatusForward2Test() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Constant.Status status = Constant.Status.PURCHASED;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);
        element.setStatus(Constant.Status.RESERVED.getName());

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));

        ElementDTO actual = service.setStatus(userDTO, id, status);
        assertNull(actual);
    }

    @Test
    public void setStatusBackwardTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Constant.Status status = Constant.Status.IDEA;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);
        User user = UserMaker.makeUser();
        element.setStatus(Constant.Status.PURCHASED.getName());
        element.setBuyer(user);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));

        ElementDTO actual = service.setStatus(userDTO, id, status);
        element.setStatus(status.getName());
        assertEquals(ElementTransformer.entityToDto(element), actual);
    }

    @Test
    public void setStatusBackwardToCrushTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Constant.Status status = Constant.Status.CRUSH;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);
        User user = UserMaker.makeUser();
        element.setStatus(Constant.Status.PURCHASED.getName());
        element.setBuyer(user);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));

        ElementDTO actual = service.setStatus(userDTO, id, status);
        element.setStatus(status.getName());
        assertEquals(ElementTransformer.entityToDto(element), actual);
    }

    @Test
    public void toCrushSetStatusBackwardTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Constant.Status status = Constant.Status.CRUSH;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);
        User user = UserMaker.makeUser();
        user.setId(2);
        element.setStatus(Constant.Status.PURCHASED.getName());
        element.setBuyer(user);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));

        ElementDTO actual = service.setStatus(userDTO, id, status);
        assertNull(actual);
    }

    @Test
    public void invalidBuyerSetStatusBackwardTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Constant.Status status = Constant.Status.IDEA;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);
        User user = UserMaker.makeUser();
        user.setId(2);
        element.setStatus(Constant.Status.PURCHASED.getName());
        element.setBuyer(user);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));

        ElementDTO actual = service.setStatus(userDTO, id, status);
        assertNull(actual);
    }

    @Test
    public void noBuyerSetStatusBackwardTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Constant.Status status = Constant.Status.IDEA;
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);
        element.setStatus(Constant.Status.PURCHASED.getName());

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));

        ElementDTO actual = service.setStatus(userDTO, id, status);
        assertNull(actual);
    }

    @Test
    public void elementNotFoundSetStatusTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        Constant.Status status = Constant.Status.CRUSH;

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.empty());

        ElementDTO actual = service.setStatus(userDTO, id, status);
        assertNull(actual);
    }

    @Test
    public void addImageTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        ImageRequestDTO request = ElementMaker.makeImageRequestDTO();
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);
        ImageDTO expected = ElementMaker.makeImageDTO();

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));
        Mockito.when(imageService.add(element, request)).thenReturn(expected);

        ImageDTO actual = service.addImage(userDTO, id, request);
        assertEquals(expected, actual);
    }

    @Test
    public void elementNotFoundAddImageTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        ImageRequestDTO request = ElementMaker.makeImageRequestDTO();

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.empty());

        ImageDTO actual = service.addImage(userDTO, id, request);
        assertNull(actual);
    }

    @Test
    public void unauthorizedAddImageTest() {
        long id = 8;
        UserDTO userDTO = UserMaker.makeUserDTO();
        userDTO.setId(2);
        ImageRequestDTO request = ElementMaker.makeImageRequestDTO();
        Wishlist wishlist = WishlistMaker.makeWishlist();
        Element element = wishlist.getElements().get(0);

        Mockito.when(elementRepository.findById(id)).thenReturn(Optional.of(element));

        ImageDTO actual = service.addImage(userDTO, id, request);
        assertNull(actual);
    }

}
